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

Kieran Bingham kieran.bingham at ideasonboard.com
Sun May 29 14:34:12 CEST 2022


Quoting Laurent Pinchart (2022-05-29 09:59:09)
> 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.

Easy to find if you know what they are ;-) I knew what they are but
that's not always a given.

A reference to that link would help I guess.

--
Kieran


> 
> > > > +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