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

Laurent Pinchart laurent.pinchart at ideasonboard.com
Sun May 29 10:59:09 CEST 2022


Hi Kieran,

On Sat, May 28, 2022 at 11:31:29PM +0100, Kieran Bingham wrote:
> Quoting Laurent Pinchart via libcamera-devel (2022-05-26 10:09:17)
> > On Thu, May 26, 2022 at 01:25:02AM +0300, 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
> > > +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";
> > 
> > clang complains about unused variables, so I'll have to drop a few, or
> > add [[maybe_unused]]. I think I'll go for the former in this case.
> 
> Having these as handy to reference would help for anyone who wants to
> tweak the colour schemes.
> 
> I'd almost go as far to say maybe they should be #define'd to keep the
> compiler ignorant and still available ...
> 
> Or pragma out the unused variable warning just for this table...

I've thought about all that, but I'm not sure if it's worth it given how
easy the information is to find:
https://en.wikipedia.org/wiki/ANSI_escape_code#Colors.

> > > +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_;
> > >  
> > >       int ret = _d()->start();

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list