diff options
Diffstat (limited to 'src/libcamera/log.cpp')
-rw-r--r-- | src/libcamera/log.cpp | 999 |
1 files changed, 0 insertions, 999 deletions
diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp deleted file mode 100644 index 5ff0e901..00000000 --- a/src/libcamera/log.cpp +++ /dev/null @@ -1,999 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018, Google Inc. - * - * log.cpp - Logging infrastructure - */ - -#include "libcamera/internal/log.h" - -#include <array> -#if HAVE_BACKTRACE -#include <execinfo.h> -#endif -#include <fstream> -#include <iostream> -#include <list> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unordered_set> - -#include <libcamera/logging.h> - -#include <libcamera/base/utils.h> - -#include "libcamera/internal/thread.h" - -/** - * \file log.h - * \brief Logging infrastructure - * - * libcamera includes a logging infrastructure used through the library that - * allows inspection of internal operation in a user-configurable way. The log - * messages are grouped in categories that represent areas of libcamera, and - * output of messages for each category can be controlled by independent log - * levels. - * - * The levels are configurable through the LIBCAMERA_LOG_LEVELS environment - * variable that contains a comma-separated list of 'category:level' pairs. - * - * The category names are strings and can include a wildcard ('*') character at - * the end to match multiple categories. - * - * The level are either numeric values, or strings containing the log level - * name. The available log levels are DEBUG, INFO, WARN, ERROR and FATAL. Log - * message with a level higher than or equal to the configured log level for - * their category are output to the log, while other messages are silently - * discarded. - * - * By default log messages are output to stderr. They can be redirected to a log - * file by setting the LIBCAMERA_LOG_FILE environment variable to the name of - * the file. The file must be writable and is truncated if it exists. If any - * error occurs when opening the file, the file is ignored and the log is output - * to stderr. - */ - -/** - * \file logging.h - * \brief Logging management - * - * API to change the logging output destination and log levels programatically. - */ - -namespace libcamera { - -static int log_severity_to_syslog(LogSeverity severity) -{ - switch (severity) { - case LogDebug: - return LOG_DEBUG; - case LogInfo: - return LOG_INFO; - case LogWarning: - return LOG_WARNING; - case LogError: - return LOG_ERR; - case LogFatal: - return LOG_ALERT; - default: - return LOG_NOTICE; - } -} - -static const char *log_severity_name(LogSeverity severity) -{ - static const char *const names[] = { - "DEBUG", - " INFO", - " WARN", - "ERROR", - "FATAL", - }; - - if (static_cast<unsigned int>(severity) < std::size(names)) - return names[severity]; - else - return "UNKWN"; -} - -/** - * \brief Log output - * - * The LogOutput class models a log output destination - */ -class LogOutput -{ -public: - LogOutput(const char *path); - LogOutput(std::ostream *stream); - LogOutput(); - ~LogOutput(); - - bool isValid() const; - void write(const LogMessage &msg); - void write(const std::string &msg); - -private: - void writeSyslog(LogSeverity severity, const std::string &msg); - void writeStream(const std::string &msg); - - std::ostream *stream_; - LoggingTarget target_; -}; - -/** - * \brief Construct a log output based on a file - * \param[in] path Full path to log file - */ -LogOutput::LogOutput(const char *path) - : target_(LoggingTargetFile) -{ - stream_ = new std::ofstream(path); -} - -/** - * \brief Construct a log output based on a stream - * \param[in] stream Stream to send log output to - */ -LogOutput::LogOutput(std::ostream *stream) - : stream_(stream), target_(LoggingTargetStream) -{ -} - -/** - * \brief Construct a log output to syslog - */ -LogOutput::LogOutput() - : stream_(nullptr), target_(LoggingTargetSyslog) -{ - openlog("libcamera", LOG_PID, 0); -} - -LogOutput::~LogOutput() -{ - switch (target_) { - case LoggingTargetFile: - delete stream_; - break; - case LoggingTargetSyslog: - closelog(); - break; - default: - break; - } -} - -/** - * \brief Check if the log output is valid - * \return True if the log output is valid - */ -bool LogOutput::isValid() const -{ - switch (target_) { - case LoggingTargetFile: - return stream_->good(); - case LoggingTargetStream: - return stream_ != nullptr; - default: - return true; - } -} - -/** - * \brief Write message to log output - * \param[in] msg Message to write - */ -void LogOutput::write(const LogMessage &msg) -{ - std::string str; - - switch (target_) { - case LoggingTargetSyslog: - str = std::string(log_severity_name(msg.severity())) + " " - + msg.category().name() + " " + msg.fileInfo() + " " - + msg.msg(); - writeSyslog(msg.severity(), str); - break; - case LoggingTargetStream: - case LoggingTargetFile: - str = "[" + utils::time_point_to_string(msg.timestamp()) + "] [" - + std::to_string(Thread::currentId()) + "] " - + log_severity_name(msg.severity()) + " " - + msg.category().name() + " " + msg.fileInfo() + " " - + msg.msg(); - writeStream(str); - break; - default: - break; - } -} - -/** - * \brief Write string to log output - * \param[in] str String to write - */ -void LogOutput::write(const std::string &str) -{ - switch (target_) { - case LoggingTargetSyslog: - writeSyslog(LogDebug, str); - break; - case LoggingTargetStream: - case LoggingTargetFile: - writeStream(str); - break; - default: - break; - } -} - -void LogOutput::writeSyslog(LogSeverity severity, const std::string &str) -{ - syslog(log_severity_to_syslog(severity), "%s", str.c_str()); -} - -void LogOutput::writeStream(const std::string &str) -{ - stream_->write(str.c_str(), str.size()); - stream_->flush(); -} - -/** - * \brief Message logger - * - * The Logger class handles log configuration. - */ -class Logger -{ -public: - ~Logger(); - - static Logger *instance(); - - void write(const LogMessage &msg); - void backtrace(); - - int logSetFile(const char *path); - int logSetStream(std::ostream *stream); - int logSetTarget(LoggingTarget target); - void logSetLevel(const char *category, const char *level); - -private: - Logger(); - - void parseLogFile(); - void parseLogLevels(); - static LogSeverity parseLogLevel(const std::string &level); - - friend LogCategory; - void registerCategory(LogCategory *category); - - std::unordered_set<LogCategory *> categories_; - std::list<std::pair<std::string, LogSeverity>> levels_; - - std::shared_ptr<LogOutput> output_; -}; - -/** - * \enum LoggingTarget - * \brief Log destination type - * \var LoggingTargetNone - * \brief No logging destination - * \sa Logger::logSetTarget - * \var LoggingTargetSyslog - * \brief Log to syslog - * \sa Logger::logSetTarget - * \var LoggingTargetFile - * \brief Log to file - * \sa Logger::logSetFile - * \var LoggingTargetStream - * \brief Log to stream - * \sa Logger::logSetStream - */ - -/** - * \brief Direct logging to a file - * \param[in] path Full path to the log file - * - * This function directs the log output to the file identified by \a path. The - * previous log target, if any, is closed, and all new log messages will be - * written to the new log file. - * - * If the function returns an error, the log target is not changed. - * - * \return Zero on success, or a negative error code otherwise - */ -int logSetFile(const char *path) -{ - return Logger::instance()->logSetFile(path); -} - -/** - * \brief Direct logging to a stream - * \param[in] stream Stream to send log output to - * - * This function directs the log output to \a stream. The previous log target, - * if any, is closed, and all new log messages will be written to the new log - * stream. - * - * If the function returns an error, the log file is not changed - * - * \return Zero on success, or a negative error code otherwise. - */ -int logSetStream(std::ostream *stream) -{ - return Logger::instance()->logSetStream(stream); -} - -/** - * \brief Set the logging target - * \param[in] target Logging destination - * - * This function sets the logging output to the target specified by \a target. - * The allowed values of \a target are LoggingTargetNone and - * LoggingTargetSyslog. LoggingTargetNone will send the log output to nowhere, - * and LoggingTargetSyslog will send the log output to syslog. The previous - * log target, if any, is closed, and all new log messages will be written to - * the new log destination. - * - * LoggingTargetFile and LoggingTargetStream are not valid values for \a target. - * Use logSetFile() and logSetStream() instead, respectively. - * - * If the function returns an error, the log file is not changed. - * - * \return Zero on success, or a negative error code otherwise. - */ -int logSetTarget(LoggingTarget target) -{ - return Logger::instance()->logSetTarget(target); -} - -/** - * \brief Set the log level - * \param[in] category Logging category - * \param[in] level Log level - * - * This function sets the log level of \a category to \a level. - * \a level shall be one of the following strings: - * - "DEBUG" - * - "INFO" - * - "WARN" - * - "ERROR" - * - "FATAL" - * - * "*" is not a valid \a category for this function. - */ -void logSetLevel(const char *category, const char *level) -{ - Logger::instance()->logSetLevel(category, level); -} - -Logger::~Logger() -{ - for (LogCategory *category : categories_) - delete category; -} - -/** - * \brief Retrieve the logger instance - * - * The Logger is a singleton and can't be constructed manually. This function - * shall instead be used to retrieve the single global instance of the logger. - * - * \return The logger instance - */ -Logger *Logger::instance() -{ - static Logger instance; - return &instance; -} - -/** - * \brief Write a message to the configured logger output - * \param[in] msg The message object - */ -void Logger::write(const LogMessage &msg) -{ - std::shared_ptr<LogOutput> output = std::atomic_load(&output_); - if (!output) - return; - - output->write(msg); -} - -/** - * \brief Write a backtrace to the log - */ -void Logger::backtrace() -{ -#if HAVE_BACKTRACE - std::shared_ptr<LogOutput> output = std::atomic_load(&output_); - if (!output) - return; - - void *buffer[32]; - int num_entries = ::backtrace(buffer, std::size(buffer)); - char **strings = backtrace_symbols(buffer, num_entries); - if (!strings) - return; - - std::ostringstream msg; - msg << "Backtrace:" << std::endl; - - /* - * Skip the first two entries that correspond to this method and - * ~LogMessage(). - */ - for (int i = 2; i < num_entries; ++i) - msg << strings[i] << std::endl; - - output->write(msg.str()); - - free(strings); -#endif -} - -/** - * \brief Set the log file - * \param[in] path Full path to the log file - * - * \sa libcamera::logSetFile() - * - * \return Zero on success, or a negative error code otherwise. - */ -int Logger::logSetFile(const char *path) -{ - std::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(path); - if (!output->isValid()) - return -EINVAL; - - std::atomic_store(&output_, output); - return 0; -} - -/** - * \brief Set the log stream - * \param[in] stream Stream to send log output to - * - * \sa libcamera::logSetStream() - * - * \return Zero on success, or a negative error code otherwise. - */ -int Logger::logSetStream(std::ostream *stream) -{ - std::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(stream); - std::atomic_store(&output_, output); - return 0; -} - -/** - * \brief Set the log target - * \param[in] target Log destination - * - * \sa libcamera::logSetTarget() - * - * \return Zero on success, or a negative error code otherwise. - */ -int Logger::logSetTarget(enum LoggingTarget target) -{ - std::shared_ptr<LogOutput> output; - - switch (target) { - case LoggingTargetSyslog: - output = std::make_shared<LogOutput>(); - std::atomic_store(&output_, output); - break; - case LoggingTargetNone: - output = nullptr; - std::atomic_store(&output_, std::shared_ptr<LogOutput>()); - break; - default: - return -EINVAL; - } - - return 0; -} - -/** - * \brief Set the log level - * \param[in] category Logging category - * \param[in] level Log level - * - * \sa libcamera::logSetLevel() - */ -void Logger::logSetLevel(const char *category, const char *level) -{ - LogSeverity severity = parseLogLevel(level); - if (severity == LogInvalid) - return; - - for (LogCategory *c : categories_) { - if (!strcmp(c->name(), category)) { - c->setSeverity(severity); - break; - } - } -} - -/** - * \brief Construct a logger - */ -Logger::Logger() -{ - parseLogFile(); - parseLogLevels(); -} - -/** - * \brief Parse the log output file from the environment - * - * If the LIBCAMERA_LOG_FILE environment variable is set, open the file it - * points to and redirect the logger output to it. If the environment variable - * is set to "syslog", then the logger output will be directed to syslog. Errors - * are silently ignored and don't affect the logger output (set to stderr). - */ -void Logger::parseLogFile() -{ - const char *file = utils::secure_getenv("LIBCAMERA_LOG_FILE"); - if (!file) { - logSetStream(&std::cerr); - return; - } - - if (!strcmp(file, "syslog")) { - logSetTarget(LoggingTargetSyslog); - return; - } - - logSetFile(file); -} - -/** - * \brief Parse the log levels from the environment - * - * The log levels are stored in the LIBCAMERA_LOG_LEVELS environment variable - * as a list of "category:level" pairs, separated by commas (','). Parse the - * variable and store the levels to configure all log categories. - */ -void Logger::parseLogLevels() -{ - const char *debug = utils::secure_getenv("LIBCAMERA_LOG_LEVELS"); - if (!debug) - return; - - for (const char *pair = debug; *debug != '\0'; pair = debug) { - const char *comma = strchrnul(debug, ','); - size_t len = comma - pair; - - /* Skip over the comma. */ - debug = *comma == ',' ? comma + 1 : comma; - - /* Skip to the next pair if the pair is empty. */ - if (!len) - continue; - - std::string category; - std::string level; - - const char *colon = static_cast<const char *>(memchr(pair, ':', len)); - if (!colon) { - /* 'x' is a shortcut for '*:x'. */ - category = "*"; - level = std::string(pair, len); - } else { - category = std::string(pair, colon - pair); - level = std::string(colon + 1, comma - colon - 1); - } - - /* Both the category and the level must be specified. */ - if (category.empty() || level.empty()) - continue; - - LogSeverity severity = parseLogLevel(level); - if (severity == LogInvalid) - continue; - - levels_.push_back({ category, severity }); - } -} - -/** - * \brief Parse a log level string into a LogSeverity - * \param[in] level The log level string - * - * Log levels can be specified as an integer value in the range from LogDebug to - * LogFatal, or as a string corresponding to the severity name in uppercase. Any - * other value is invalid. - * - * \return The log severity, or LogInvalid if the string is invalid - */ -LogSeverity Logger::parseLogLevel(const std::string &level) -{ - static const char *const names[] = { - "DEBUG", - "INFO", - "WARN", - "ERROR", - "FATAL", - }; - - int severity; - - if (std::isdigit(level[0])) { - char *endptr; - severity = strtoul(level.c_str(), &endptr, 10); - if (*endptr != '\0' || severity > LogFatal) - severity = LogInvalid; - } else { - severity = LogInvalid; - for (unsigned int i = 0; i < std::size(names); ++i) { - if (names[i] == level) { - severity = i; - break; - } - } - } - - return static_cast<LogSeverity>(severity); -} - -/** - * \brief Register a log category with the logger - * \param[in] category The log category - * - * Log categories must have unique names. If a category with the same name - * already exists this function performs no operation. - */ -void Logger::registerCategory(LogCategory *category) -{ - categories_.insert(category); - - const std::string &name = category->name(); - for (const std::pair<std::string, LogSeverity> &level : levels_) { - bool match = true; - - for (unsigned int i = 0; i < level.first.size(); ++i) { - if (level.first[i] == '*') - break; - - if (i >= name.size() || - name[i] != level.first[i]) { - match = false; - break; - } - } - - if (match) { - category->setSeverity(level.second); - break; - } - } -} - -/** - * \enum LogSeverity - * Log message severity - * \var LogDebug - * Debug message - * \var LogInfo - * Informational message - * \var LogWarning - * Warning message, signals a potential issue - * \var LogError - * Error message, signals an unrecoverable issue - * \var LogFatal - * Fatal message, signals an unrecoverable issue and aborts execution - */ - -/** - * \class LogCategory - * \brief A category of log message - * - * The LogCategory class represents a category of log messages, related to an - * area of the library. It groups all messages belonging to the same category, - * and is used to control the log level per group. - */ - -/** - * \brief Construct a log category - * \param[in] name The category name - */ -LogCategory::LogCategory(const char *name) - : name_(name), severity_(LogSeverity::LogInfo) -{ - Logger::instance()->registerCategory(this); -} - -/** - * \fn LogCategory::name() - * \brief Retrieve the log category name - * \return The log category name - */ - -/** - * \fn LogCategory::severity() - * \brief Retrieve the severity of the log category - * \sa setSeverity() - * \return Return the severity of the log category - */ - -/** - * \brief Set the severity of the log category - * - * Messages of severity higher than or equal to the severity of the log category - * are printed, other messages are discarded. - */ -void LogCategory::setSeverity(LogSeverity severity) -{ - severity_ = severity; -} - -/** - * \brief Retrieve the default log category - * - * The default log category is named "default" and is used by the LOG() macro - * when no log category is specified. - * - * \return A reference to the default log category - */ -const LogCategory &LogCategory::defaultCategory() -{ - static const LogCategory *category = new LogCategory("default"); - return *category; -} - -/** - * \class LogMessage - * \brief Internal log message representation. - * - * The LogMessage class models a single message in the log. It serves as a - * helper to provide the std::ostream API for logging, and must never be used - * directly. Use the LOG() macro instead access the log infrastructure. - */ - -/** - * \brief Construct a log message for a given category - * \param[in] fileName The file name where the message is logged from - * \param[in] line The line number where the message is logged from - * \param[in] category The log message category, controlling how the message - * will be displayed - * \param[in] severity The log message severity, controlling how the message - * will be displayed - * - * Create a log message pertaining to line \a line of file \a fileName. The - * \a severity argument sets the message severity to control whether it will be - * output or dropped. - */ -LogMessage::LogMessage(const char *fileName, unsigned int line, - const LogCategory &category, LogSeverity severity) - : category_(category), severity_(severity) -{ - init(fileName, line); -} - -/** - * \brief Move-construct a log message - * \param[in] other The other message - * - * The move constructor is meant to support the _log() functions. Thanks to copy - * elision it will likely never be called, but C++11 only permits copy elision, - * it doesn't enforce it unlike C++17. To avoid potential link errors depending - * on the compiler type and version, and optimization level, the move - * constructor is defined even if it will likely never be called, and ensures - * that the destructor of the \a other message will not output anything to the - * log by setting the severity to LogInvalid. - */ -LogMessage::LogMessage(LogMessage &&other) - : msgStream_(std::move(other.msgStream_)), category_(other.category_), - severity_(other.severity_) -{ - other.severity_ = LogInvalid; -} - -void LogMessage::init(const char *fileName, unsigned int line) -{ - /* Log the timestamp, severity and file information. */ - timestamp_ = utils::clock::now(); - - std::ostringstream ossFileInfo; - ossFileInfo << utils::basename(fileName) << ":" << line; - fileInfo_ = ossFileInfo.str(); -} - -LogMessage::~LogMessage() -{ - /* Don't print anything if we have been moved to another LogMessage. */ - if (severity_ == LogInvalid) - return; - - msgStream_ << std::endl; - - if (severity_ >= category_.severity()) - Logger::instance()->write(*this); - - if (severity_ == LogSeverity::LogFatal) { - Logger::instance()->backtrace(); - std::abort(); - } -} - -/** - * \fn std::ostream& LogMessage::stream() - * - * Data is added to a LogMessage through the stream returned by this function. - * The stream implements the std::ostream API and can be used for logging as - * std::cout. - * - * \return A reference to the log message stream - */ - -/** - * \fn LogMessage::timestamp() - * \brief Retrieve the timestamp of the log message - * \return The timestamp of the message - */ - -/** - * \fn LogMessage::severity() - * \brief Retrieve the severity of the log message - * \return The severity of the message - */ - -/** - * \fn LogMessage::category() - * \brief Retrieve the category of the log message - * \return The category of the message - */ - -/** - * \fn LogMessage::fileInfo() - * \brief Retrieve the file info of the log message - * \return The file info of the message - */ - -/** - * \fn LogMessage::msg() - * \brief Retrieve the message text of the log message - * \return The message text of the message, as a string - */ - -/** - * \class Loggable - * \brief Base class to support log message extensions - * - * The Loggable class allows classes to extend log messages without any change - * to the way the LOG() macro is invoked. By inheriting from Loggable and - * implementing the logPrefix() virtual method, a class can specify extra - * information to be automatically added to messages logged from class member - * methods. - */ - -Loggable::~Loggable() -{ -} - -/** - * \fn Loggable::logPrefix() - * \brief Retrieve a string to be prefixed to the log message - * - * This method allows classes inheriting from the Loggable class to extend the - * logger with an object-specific prefix output right before the log message - * contents. - * - * \return A string to be prefixed to the log message - */ - -/** - * \brief Create a temporary LogMessage object to log a message - * \param[in] category The log message category - * \param[in] severity The log message severity - * \param[in] fileName The file name where the message is logged from - * \param[in] line The line number where the message is logged from - * - * This method is used as a backeng by the LOG() macro to create a log message - * for locations inheriting from the Loggable class. - * - * \return A log message - */ -LogMessage Loggable::_log(const LogCategory *category, LogSeverity severity, - const char *fileName, unsigned int line) const -{ - LogMessage msg(fileName, line, - category ? *category : LogCategory::defaultCategory(), - severity); - - msg.stream() << logPrefix() << ": "; - return msg; -} - -/** - * \brief Create a temporary LogMessage object to log a message - * \param[in] category The log message category - * \param[in] severity The log message severity - * \param[in] fileName The file name where the message is logged from - * \param[in] line The line number where the message is logged from - * - * This function is used as a backeng by the LOG() macro to create a log - * message for locations not inheriting from the Loggable class. - * - * \return A log message - */ -LogMessage _log(const LogCategory *category, LogSeverity severity, - const char *fileName, unsigned int line) -{ - return LogMessage(fileName, line, - category ? *category : LogCategory::defaultCategory(), - severity); -} - -/** - * \def LOG_DECLARE_CATEGORY(name) - * \hideinitializer - * \brief Declare a category of log messages - * - * This macro is used to declare a log category defined in another compilation - * unit by the LOG_DEFINE_CATEGORY() macro. - * - * The LOG_DECLARE_CATEGORY() macro must be used in the libcamera namespace. - * - * \sa LogCategory - */ - -/** - * \def LOG_DEFINE_CATEGORY(name) - * \hideinitializer - * \brief Define a category of log messages - * - * This macro is used to define a log category that can then be used with the - * LOGC() macro. Category names shall be unique, if a category is shared between - * compilation units, it shall be defined in one compilation unit only and - * declared with LOG_DECLARE_CATEGORY() in the other compilation units. - * - * The LOG_DEFINE_CATEGORY() macro must be used in the libcamera namespace. - * - * \sa LogCategory - */ - -/** - * \def LOG(category, severity) - * \hideinitializer - * \brief Log a message - * \param[in] category Category (optional) - * \param[in] severity Severity - * - * Return an std::ostream reference to which a message can be logged using the - * iostream API. The \a category, if specified, sets the message category. When - * absent the default category is used. The \a severity controls whether the - * message is printed or discarded, depending on the log level for the category. - * - * If the severity is set to Fatal, execution is aborted and the program - * terminates immediately after printing the message. - * - * \warning Logging from the destructor of a global object, either directly or - * indirectly, results in undefined behaviour. - * - * \todo Allow logging from destructors of global objects to the largest - * possible extent - */ - -/** - * \def ASSERT(condition) - * \hideinitializer - * \brief Abort program execution if assertion fails - * - * If \a condition is false, ASSERT() logs an error message with the Fatal log - * level and aborts program execution. - * - * If the macro NDEBUG is defined before including log.h, ASSERT() generates no - * code. - * - * Using conditions that have side effects with ASSERT() is not recommended, as - * these effects would depend on whether NDEBUG is defined or not. Similarly, - * ASSERT() should not be used to check for errors that can occur under normal - * conditions as those checks would then be removed when compiling with NDEBUG. - */ - -} /* namespace libcamera */ |