From 575703894624306950c0540182afc48ba3732f8c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 3 Feb 2021 19:17:46 +0100 Subject: libcamera: base: log: Add coloring to the log output Extend the logger to support coloring messages. The log level is colorized with per-level colors, and the category with a fixed color. This makes the log output more readable. Coloring is enabled by default when logging to std::cerr, and can be disabled by setting the LIBCAMERA_LOG_NO_COLOR environment variable. When logging to a file with LIBCAMERA_LOG_FILE, coloring is disabled. It can be enabled for file logging using the logSetFile() function. Signed-off-by: Laurent Pinchart Reviewed-by: Umang Jain Reviewed-by: Kieran Bingham --- Documentation/environment_variables.rst | 21 ++++-- include/libcamera/logging.h | 4 +- src/libcamera/base/log.cpp | 110 +++++++++++++++++++++++++------- 3 files changed, 104 insertions(+), 31 deletions(-) diff --git a/Documentation/environment_variables.rst b/Documentation/environment_variables.rst index fa703a72..0a7760cb 100644 --- a/Documentation/environment_variables.rst +++ b/Documentation/environment_variables.rst @@ -19,6 +19,9 @@ LIBCAMERA_LOG_LEVELS Example value: ``*:DEBUG`` +LIBCAMERA_LOG_NO_COLOR + Disable coloring of log messages (`more `__). + LIBCAMERA_IPA_CONFIG_PATH Define custom search locations for IPA configurations (`more `__). @@ -40,12 +43,20 @@ Further details Notes about debugging ~~~~~~~~~~~~~~~~~~~~~ -The environment variables ``LIBCAMERA_LOG_FILE`` and ``LIBCAMERA_LOG_LEVELS`` -are used to modify the destination and verbosity of messages provided by -libcamera. +The environment variables ``LIBCAMERA_LOG_FILE``, ``LIBCAMERA_LOG_LEVELS`` and +``LIBCAMERA_LOG_NO_COLOR`` are used to modify the default configuration of the +libcamera logger. + +By default, libcamera logs all messages to the standard error (std::cerr). +Messages are colored by default depending on the log level. Coloring can be +disabled by setting the ``LIBCAMERA_LOG_NO_COLOR`` environment variable. + +The default log destination can also be directed to a file by setting the +``LIBCAMERA_LOG_FILE`` environment variable to the log file name. This also +disables coloring. -The ``LIBCAMERA_LOG_LEVELS`` variable accepts a comma-separated list of -'category:level' pairs. +Log levels are controlled through the ``LIBCAMERA_LOG_LEVELS`` variable, which +accepts a comma-separated list of 'category:level' pairs. The `level `__ part is mandatory and can either be specified by name or by numerical index associated with each level. diff --git a/include/libcamera/logging.h b/include/libcamera/logging.h index c36882b9..cd842f67 100644 --- a/include/libcamera/logging.h +++ b/include/libcamera/logging.h @@ -16,8 +16,8 @@ enum LoggingTarget { LoggingTargetStream, }; -int logSetFile(const char *path); -int logSetStream(std::ostream *stream); +int logSetFile(const char *path, bool color = false); +int logSetStream(std::ostream *stream, bool color = false); int logSetTarget(LoggingTarget target); void logSetLevel(const char *category, const char *level); diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp index 26f14207..eefdda4b 100644 --- a/src/libcamera/base/log.cpp +++ b/src/libcamera/base/log.cpp @@ -104,8 +104,8 @@ static const char *log_severity_name(LogSeverity severity) class LogOutput { public: - LogOutput(const char *path); - LogOutput(std::ostream *stream); + LogOutput(const char *path, bool color); + LogOutput(std::ostream *stream, bool color); LogOutput(); ~LogOutput(); @@ -119,14 +119,16 @@ private: std::ostream *stream_; LoggingTarget target_; + bool color_; }; /** * \brief Construct a log output based on a file * \param[in] path Full path to log file + * \param[in] color True to output colored messages */ -LogOutput::LogOutput(const char *path) - : target_(LoggingTargetFile) +LogOutput::LogOutput(const char *path, bool color) + : target_(LoggingTargetFile), color_(color) { stream_ = new std::ofstream(path); } @@ -134,9 +136,10 @@ LogOutput::LogOutput(const char *path) /** * \brief Construct a log output based on a stream * \param[in] stream Stream to send log output to + * \param[in] color True to output colored messages */ -LogOutput::LogOutput(std::ostream *stream) - : stream_(stream), target_(LoggingTargetStream) +LogOutput::LogOutput(std::ostream *stream, bool color) + : stream_(stream), target_(LoggingTargetStream), color_(color) { } @@ -144,7 +147,7 @@ LogOutput::LogOutput(std::ostream *stream) * \brief Construct a log output to syslog */ LogOutput::LogOutput() - : stream_(nullptr), target_(LoggingTargetSyslog) + : stream_(nullptr), target_(LoggingTargetSyslog), color_(false) { openlog("libcamera", LOG_PID, 0); } @@ -179,28 +182,66 @@ bool LogOutput::isValid() const } } +namespace { + +/* + * For more information about ANSI escape codes, see + * https://en.wikipedia.org/wiki/ANSI_escape_code#Colors. + */ +constexpr const char *kColorReset = "\033[0m"; +constexpr const char *kColorBrightRed = "\033[1;31m"; +constexpr const char *kColorBrightGreen = "\033[1;32m"; +constexpr const char *kColorBrightYellow = "\033[1;33m"; +constexpr const char *kColorBrightBlue = "\033[1;34m"; +constexpr const char *kColorBrightMagenta = "\033[1;35m"; +constexpr const char *kColorBrightCyan = "\033[1;36m"; +constexpr const char *kColorBrightWhite = "\033[1;37m"; + +} /* namespace */ + /** * \brief Write message to log output * \param[in] msg Message to write */ void LogOutput::write(const LogMessage &msg) { + static const char *const severityColors[] = { + kColorBrightCyan, + kColorBrightGreen, + kColorBrightYellow, + kColorBrightRed, + kColorBrightMagenta, + }; + + const char *categoryColor = color_ ? kColorBrightWhite : ""; + const char *fileColor = color_ ? kColorBrightBlue : ""; + const char *resetColor = color_ ? kColorReset : ""; + const char *severityColor = ""; + LogSeverity severity = msg.severity(); std::string str; + if (color_) { + if (static_cast(severity) < std::size(severityColors)) + severityColor = severityColors[severity]; + else + severityColor = kColorBrightWhite; + } + switch (target_) { case LoggingTargetSyslog: - str = std::string(log_severity_name(msg.severity())) + " " + str = std::string(log_severity_name(severity)) + " " + msg.category().name() + " " + msg.fileInfo() + " " + msg.msg(); - writeSyslog(msg.severity(), str); + writeSyslog(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(); + + severityColor + log_severity_name(severity) + " " + + categoryColor + msg.category().name() + " " + + fileColor + msg.fileInfo() + " " + + resetColor + msg.msg(); writeStream(str); break; default: @@ -253,8 +294,8 @@ public: void write(const LogMessage &msg); void backtrace(); - int logSetFile(const char *path); - int logSetStream(std::ostream *stream); + int logSetFile(const char *path, bool color); + int logSetStream(std::ostream *stream, bool color); int logSetTarget(LoggingTarget target); void logSetLevel(const char *category, const char *level); @@ -298,35 +339,47 @@ bool Logger::destroyed_ = false; /** * \brief Direct logging to a file * \param[in] path Full path to the log file + * \param[in] color True to output colored messages * * 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. * + * \a color controls whether or not the messages will be colored with standard + * ANSI escape codes. This is done regardless of whether \a path refers to a + * standard file or a TTY, the caller is responsible for disabling coloring when + * not suitable for the log target. + * * 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) +int logSetFile(const char *path, bool color) { - return Logger::instance()->logSetFile(path); + return Logger::instance()->logSetFile(path, color); } /** * \brief Direct logging to a stream * \param[in] stream Stream to send log output to + * \param[in] color True to output colored messages * * 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. * + * \a color controls whether or not the messages will be colored with standard + * ANSI escape codes. This is done regardless of whether \a stream refers to a + * standard file or a TTY, the caller is responsible for disabling coloring when + * not suitable for the log target. + * * 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) +int logSetStream(std::ostream *stream, bool color) { - return Logger::instance()->logSetStream(stream); + return Logger::instance()->logSetStream(stream, color); } /** @@ -437,14 +490,16 @@ void Logger::backtrace() /** * \brief Set the log file * \param[in] path Full path to the log file + * \param[in] color True to output colored messages * * \sa libcamera::logSetFile() * * \return Zero on success, or a negative error code otherwise. */ -int Logger::logSetFile(const char *path) +int Logger::logSetFile(const char *path, bool color) { - std::shared_ptr output = std::make_shared(path); + std::shared_ptr output = + std::make_shared(path, color); if (!output->isValid()) return -EINVAL; @@ -455,14 +510,16 @@ int Logger::logSetFile(const char *path) /** * \brief Set the log stream * \param[in] stream Stream to send log output to + * \param[in] color True to output colored messages * * \sa libcamera::logSetStream() * * \return Zero on success, or a negative error code otherwise. */ -int Logger::logSetStream(std::ostream *stream) +int Logger::logSetStream(std::ostream *stream, bool color) { - std::shared_ptr output = std::make_shared(stream); + std::shared_ptr output = + std::make_shared(stream, color); std::atomic_store(&output_, output); return 0; } @@ -514,10 +571,15 @@ void Logger::logSetLevel(const char *category, const char *level) /** * \brief Construct a logger + * + * If the environment variable is not set, log to std::cerr. The log messages + * are then colored by default. This can be overridden by setting the + * LIBCAMERA_LOG_NO_COLOR environment variable to disable coloring. */ Logger::Logger() { - logSetStream(&std::cerr); + bool color = !utils::secure_getenv("LIBCAMERA_LOG_NO_COLOR"); + logSetStream(&std::cerr, color); parseLogFile(); parseLogLevels(); @@ -543,7 +605,7 @@ void Logger::parseLogFile() return; } - logSetFile(file); + logSetFile(file, false); } /** -- cgit v1.2.1