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 <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
@@ -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 default 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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+86
-24
@@ -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<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() + " "
|
||||
+ 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<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 +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<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 +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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user