blob: adcdc6d47226381ee804990895ee95fe8658f588 [file] [edit]
// Copyright 2023 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NINJA_LOGGER_H_
#define NINJA_LOGGER_H_
#include <stdio.h>
#include <stdarg.h>
#include <memory>
#include <ostream>
#include <string>
#include "string_piece.h"
/// Convenience class to send log messages to various destinations, for
/// example to an stdio FILE* instance, a C++ std::ostream instance,
/// or a string buffer.
///
/// Usage is:
/// - Create instance using one of the Logger::CreateXXX() static
/// method (each one selects a different destination), or even
/// the default constructor (which doesn't do anything).
///
/// - Call `log.Log("message")` or `log.Log("formatted %s", "message")`
/// to send messages to the log.
///
/// - Call `log.Logv(fmt, args)` when you have a printf-style formatting
/// string and a va_list value for formatting arguments instead.
///
/// - Use the `log.enabled()` method to avoid doing complex computations
/// or allocations when logging is not necessary, for example, the
/// following line will always call std::to_string() and allocate
/// a temporary string, even if logging is disabled:
///
/// log.Log("Error: %s", std::to_string(value).c_str());
///
/// Instead, use:
///
/// log.enabled() && log.Log("Error: %s", std::to_string(value).c_str());
///
/// This is why each Log() and Logv() method returns a boolean,
/// which will be true if something was logged, and false otherwise.
///
/// Another alternative is to use:
///
/// LOGGER_LOG(log, "Error: %s", std::to_string(value).c_str())
///
/// which expands to the same thing.
///
/// Note that:
///
/// - Each Logger instance is only one pointer long, and thus can be
/// embedded in objects that need them easily.
///
/// - Each log message is considered independent from the others, there
/// is no feature to decompose a single message into multiple Log()
/// calls.
///
class Logger {
public:
/// Internal interface used by each implementation instance.
class Backend {
public:
virtual ~Backend() {}
virtual bool Log(StringPiece message) = 0;
virtual bool Log(const char* fmt, va_list args);
virtual bool enabled() const { return true; }
};
/// Default constructor creates an empty logger. All messages are lost.
Logger() = default;
/// Create new instance that sends all messages to an stdio stream.
/// Each message will be followed by a newline.
static Logger CreateForStdio(FILE* stream);
/// Create a new instance that sends all messages to an std::ostream
/// Each message will be followed by a newline.
static Logger CreateForStd(std::ostream& out);
/// Create a new instance that appends all messages to a string.
/// Each message will be followed by a newline character.
static Logger CreateForString(std::string* messages);
/// Create new instance that redirects everything to another logger.
static Logger CreateProxyFor(const Logger& logger);
/// Create new instance that sends each message to two distinct loggers.
static Logger CreateTeeFor(const Logger& logger_a, const Logger& logger_b);
/// Create new instance from custom Backend implementation provided
/// by the caller.
static Logger CreateForBackend(std::unique_ptr<Backend> backend);
/// Return true if this logger is enabled. False means that any
/// message is lost. Useful to avoid doing un-necessary work.
bool enabled() const;
/// Send formatted message to the log.
bool Log(StringPiece message) const;
/// Send printf-formatted message to the log.
bool Log(const char* fmt, ...) const;
/// Send vprintf-formatted message to the log.
bool Logv(const char* fmt, va_list args) const;
protected:
/// Constructor takes ownership of new |backend| object.
explicit Logger(Backend* backend) : backend_(backend) {}
explicit Logger(std::unique_ptr<Backend> backend)
: backend_(std::move(backend)) {}
std::unique_ptr<Backend> backend_;
};
/// A convenience macro used to only log arguments when the logger
/// is enabled. This expands to a boolean value which will be true
/// if something was logged.
#define LOGGER_LOG(log, ...) ((log).enabled() && (log).Log(__VA_ARGS__))
#endif // NINJA_LOGGER_H_