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 --- src/libcamera/base/log.cpp | 110 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 24 deletions(-) (limited to 'src') 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