[libcamera-devel] [PATCH 4/5] libcamera: base: log: Add coloring to the log output

Umang Jain umang.jain at ideasonboard.com
Sun May 29 14:25:22 CEST 2022


Hi Laurent,

Thank you for the patch

On 5/26/22 00:25, Laurent Pinchart via libcamera-devel wrote:
> 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 <laurent.pinchart at ideasonboard.com>
> ---
>   Documentation/environment_variables.rst |  21 +++--
>   include/libcamera/logging.h             |   4 +-
>   src/libcamera/base/log.cpp              | 114 +++++++++++++++++++-----
>   src/libcamera/camera_manager.cpp        |   2 +-
>   4 files changed, 109 insertions(+), 32 deletions(-)
>
> diff --git a/Documentation/environment_variables.rst b/Documentation/environment_variables.rst
> index fa703a726845..7028c024b14e 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 <Notes about debugging_>`__).
> +
>   LIBCAMERA_IPA_CONFIG_PATH
>      Define custom search locations for IPA configurations (`more <IPA configuration_>`__).
>   
> @@ -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 degault configuration of the


s/degault/default

> +libcamera logger.
>   
> -The ``LIBCAMERA_LOG_LEVELS`` variable accepts a comma-separated list of
> -'category:level' pairs.
> +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.
> +
> +Log levels are controlled through the ``LIBCAMERA_LOG_LEVELS`` variable, which
> +accepts a comma-separated list of 'category:level' pairs.
>   
>   The `level <Log levels_>`__ 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 c36882b91974..cd842f67d553 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 26f1420703b9..a9f5bbbd36f7 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,70 @@ bool LogOutput::isValid() const
>   	}
>   }
>   
> +namespace {
> +
> +constexpr const char *kColorReset = "\033[0m";
> +constexpr const char *kColorRed = "\033[31m";
> +constexpr const char *kColorGreen = "\033[32m";
> +constexpr const char *kColorYellow = "\033[33m";
> +constexpr const char *kColorBlue = "\033[34m";
> +constexpr const char *kColorMagenta = "\033[35m";
> +constexpr const char *kColorCyan = "\033[36m";
> +constexpr const char *kColorWhite = "\033[37m";
> +constexpr const char *kColorGrey = "\033[90m";
> +constexpr const char *kColorBrightRed = "\033[91m";
> +constexpr const char *kColorBrightGreen = "\033[92m";
> +constexpr const char *kColorBrightYellow = "\033[93m";
> +constexpr const char *kColorBrightBlue = "\033[94m";
> +constexpr const char *kColorBrightMagenta = "\033[95m";
> +constexpr const char *kColorBrightCyan = "\033[96m";
> +constexpr const char *kColorBrightWhite = "\033[97m";
> +
> +} /* 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 *msgColor = color_ ? kColorReset : "";
> +	const char *severityColor = "";
> +	LogSeverity severity = msg.severity();
>   	std::string str;
>   
> +	if (color_) {
> +		if (static_cast<unsigned int>(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() + " "
> +		    + msgColor + msg.msg();
>   		writeStream(str);
>   		break;
>   	default:
> @@ -253,8 +298,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 +343,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 +494,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<LogOutput> output = std::make_shared<LogOutput>(path);
> +	std::shared_ptr<LogOutput> output =
> +		std::make_shared<LogOutput>(path, color);
>   	if (!output->isValid())
>   		return -EINVAL;
>   
> @@ -455,14 +514,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<LogOutput> output = std::make_shared<LogOutput>(stream);
> +	std::shared_ptr<LogOutput> output =
> +		std::make_shared<LogOutput>(stream, color);
>   	std::atomic_store(&output_, output);
>   	return 0;
>   }
> @@ -514,10 +575,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 disabled coloring.
>    */
>   Logger::Logger()
>   {
> -	logSetStream(&std::cerr);
> +	bool color = !utils::secure_getenv("LIBCAMERA_LOG_NO_COLOR");
> +	logSetStream(&std::cerr, color);
>   
>   	parseLogFile();
>   	parseLogLevels();
> @@ -543,7 +609,7 @@ void Logger::parseLogFile()
>   		return;
>   	}
>   
> -	logSetFile(file);
> +	logSetFile(file, false);
>   }
>   
>   /**
> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> index 70d73822193b..d934596e4145 100644
> --- a/src/libcamera/camera_manager.cpp
> +++ b/src/libcamera/camera_manager.cpp
> @@ -290,7 +290,7 @@ CameraManager::~CameraManager()
>    */
>   int CameraManager::start()
>   {
> -	LOG(Camera, Info) << "libcamera " << version_;
> +	LOG(Camera, Debug) << "libcamera " << version_;


unrelated maybe ? Should be mentioned in commit message?

Minors....

Reviewed-by: Umang Jain <umang.jain at ideasonboard.com>

>   
>   	int ret = _d()->start();
>   	if (ret)


More information about the libcamera-devel mailing list