[libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log categories
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Tue Jan 22 14:54:32 CET 2019
Hi Jacopo,
On Tue, Jan 22, 2019 at 09:06:39AM +0100, Jacopo Mondi wrote:
> Hi Laurent,
> great, very very nice, kudos!
>
> On Mon, Jan 21, 2019 at 09:56:03PM +0200, Laurent Pinchart wrote:
> > Log categories are used to group log messages by topic. Introduce
> > support for categories by making the LOG() macro variadic. Support for
> > configuring log level per category will be introduced in a subsequent
> > commit.
> >
> > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > ---
> > Documentation/Doxyfile.in | 2 +-
> > src/libcamera/include/log.h | 56 ++++++++++-
> > src/libcamera/log.cpp | 193 +++++++++++++++++++++++++++++-------
> > 3 files changed, 210 insertions(+), 41 deletions(-)
> >
> > diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> > index dd74b7295d50..b78fb3a0b0b6 100644
> > --- a/Documentation/Doxyfile.in
> > +++ b/Documentation/Doxyfile.in
> > @@ -2050,7 +2050,7 @@ INCLUDE_FILE_PATTERNS = *.h
> > # recursively expanded use the := operator instead of the = operator.
> > # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
> >
> > -PREDEFINED =
> > +PREDEFINED = __DOXYGEN__
> >
>
> Here you pre-define __DOXYGEN__
>
> > # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
> > # tag can be used to specify a list of macro names that should be expanded. The
> > diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h
> > index cc3f9404a3d4..ad8f8452c333 100644
> > --- a/src/libcamera/include/log.h
> > +++ b/src/libcamera/include/log.h
> > @@ -19,26 +19,74 @@ enum LogSeverity {
> > LogFatal,
> > };
> >
> > +class LogCategory
> > +{
> > +public:
> > + explicit LogCategory(const char *name);
> > + ~LogCategory();
> > +
> > + const char *name() const { return name_; }
> > + LogSeverity severity() const { return severity_; }
> > + void setSeverity(LogSeverity severity);
> > +
> > + static const LogCategory &defaultCategory();
> > +
> > +private:
> > + const char *name_;
> > + LogSeverity severity_;
> > +};
> > +
> > +#define LOG_DECLARE_CATEGORY(name) \
> > +extern const LogCategory &_LOG_CATEGORY(name)();
> > +
> > +#define LOG_DEFINE_CATEGORY(name) \
> > +const LogCategory &_LOG_CATEGORY(name)() \
> > +{ \
> > + static LogCategory category(#name); \
> > + return category; \
> > +}
> > +
> > class LogMessage
> > {
> > public:
> > LogMessage(const char *fileName, unsigned int line,
> > LogSeverity severity);
> > + LogMessage(const char *fileName, unsigned int line,
> > + const LogCategory &category, LogSeverity severity);
> > LogMessage(const LogMessage &) = delete;
> > ~LogMessage();
> >
> > - std::ostream &stream() { return msgStream; }
> > + std::ostream &stream() { return msgStream_; }
> >
> > private:
> > - std::ostringstream msgStream;
> > + void init(const char *fileName, unsigned int line);
> > +
> > + std::ostringstream msgStream_;
> > + const LogCategory &category_;
> > LogSeverity severity_;
> > };
> >
> > -#define LOG(severity) LogMessage(__FILE__, __LINE__, Log##severity).stream()
> > +#ifndef __DOXYGEN__
>
> And we get here only if it is not defined.
> How does that work?
__DOXYGEN__ is only defined when doxygen parses the code. When the code
is compiled, it isn't. The effect is that the _LOG_CATEGORY, _LOG1,
_LOG2 and _LOG_MACRO macros are hidden from doxygen, and the LOG macro
that doxygen sees is a fake one with two parameters in order to let the
documentation describe them with \param.
> > +#define _LOG_CATEGORY(name) logCategory##name
> > +
> > +#define _LOG1(severity) \
> > + LogMessage(__FILE__, __LINE__, Log##severity).stream()
> > +#define _LOG2(category, severity) \
> > + LogMessage(__FILE__, __LINE__, _LOG_CATEGORY(category)(), Log##severity).stream()
> > +
> > +/*
> > + * Expand the LOG() macro to _LOG1() or _LOG2() based on the number of
> > + * arguments.
> > + */
> > +#define _LOG_MACRO(_1, _2, NAME, ...) NAME
> > +#define LOG(...) _LOG_MACRO(__VA_ARGS__, _LOG2, _LOG1)(__VA_ARGS__)
>
> Took me a lot to get around this, and without the comment, it would
> have take even more! Very very nice :)
>
> > +#else /* __DOXYGEN___ */
> > +#define LOG(category, severity)
> > +#endif /* __DOXYGEN__ */
> >
> > #ifndef NDEBUG
> > #define ASSERT(condition) static_cast<void>(({ \
> > - if (!(condition)) \
> > + if (!(condition)) \
> > LOG(Fatal) << "assertion \"" #condition "\" failed"; \
> > }))
> > #else
> > diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp
> > index 74cba383363d..20503fdcfcc1 100644
> > --- a/src/libcamera/log.cpp
> > +++ b/src/libcamera/log.cpp
> > @@ -37,32 +37,64 @@ namespace libcamera {
> > */
> >
> > /**
> > - * \def LOG(severity)
> > - * \brief Log a message
> > - *
> > - * Return an std::ostream reference to which a message can be logged using the
> > - * iostream API. The \a severity controls whether the message is printed or
> > - * dropped, depending on the global log level.
> > + * \class LogCategory
> > + * \brief A category of log message
> > *
> > - * If the severity is set to Fatal, execution is aborted and the program
> > - * terminates immediately after printing the message.
> > + * The LogCategory class represents a category of log messages, related to an
> > + * area of the library. It groups all messages belonging to the same category,
> > + * and is used to control the log level per group.
> > */
> >
> > /**
> > - * \def ASSERT(condition)
> > - * \brief Abort program execution if assertion fails
> > + * \brief Construct a log category
> > + * \param[in] name The category name
> > + */
> > +LogCategory::LogCategory(const char *name)
> > + : name_(name), severity_(LogSeverity::LogInfo)
> > +{
> > +}
> > +
> > +LogCategory::~LogCategory()
> > +{
> > +}
> > +
> > +/**
> > + * \fn LogCategory::name()
> > + * \brief Retrieve the log category name
> > + * \return The log category name
> > + */
> > +
> > +/**
> > + * \fn LogCategory::severity()
> > + * \brief Retrieve the severity of the log category
> > + * \sa setSeverity()
> > + * \return Return the severity of the log category
> > + */
> > +
> > +/**
> > + * \brief Set the severity of the log category
> > *
> > - * If \a condition is false, ASSERT() logs an error message with the Fatal log
> > - * level and aborts program execution.
> > + * Messages of severity higher than or equal to the severity of the log category
> > + * are printed, other messages are discarded.
> > + */
> > +void LogCategory::setSeverity(LogSeverity severity)
> > +{
> > + severity_ = severity;
> > +}
> > +
> > +/**
> > + * \brief Retrieve the default log category
> > *
> > - * If the macro NDEBUG is defined before including log.h, ASSERT() generates no
> > - * code.
> > + * The default log category is named "default" and is used by the LOG() macro
> > + * when no log category is specified.
> > *
> > - * Using conditions that have side effects with ASSERT() is not recommended, as
> > - * these effects would depend on whether NDEBUG is defined or not. Similarly,
> > - * ASSERT() should not be used to check for errors that can occur under normal
> > - * conditions as those checks would then be removed when compiling with NDEBUG.
> > + * \return A pointer to the default log category
> > */
> > +const LogCategory &LogCategory::defaultCategory()
> > +{
> > + static LogCategory category("default");
> > + return category;
> > +}
> >
> > static const char *log_severity_name(LogSeverity severity)
> > {
> > @@ -90,10 +122,11 @@ static const char *log_severity_name(LogSeverity severity)
> > */
> >
> > /**
> > - * \param fileName The file name where the message is logged from
> > - * \param line The line number where the message is logged from
> > - * \param severity The log message severity, controlling how the message will be
> > - * displayed
> > + * \brief Construct a log message for the default category
> > + * \param[in] fileName The file name where the message is logged from
> > + * \param[in] line The line number where the message is logged from
> > + * \param[in] severity The log message severity, controlling how the message
> > + * will be displayed
> > *
> > * Create a log message pertaining to line \a line of file \a fileName. The
> > * \a severity argument sets the message severity to control whether it will be
> > @@ -101,29 +134,57 @@ static const char *log_severity_name(LogSeverity severity)
> > */
> > LogMessage::LogMessage(const char *fileName, unsigned int line,
> > LogSeverity severity)
> > - : severity_(severity)
> > + : category_(LogCategory::defaultCategory()), severity_(severity)
> > +{
> > + init(fileName, line);
> > +}
> > +
> > +/**
> > + * \brief Construct a log message for a given category
> > + * \param[in] fileName The file name where the message is logged from
> > + * \param[in] line The line number where the message is logged from
> > + * \param[in] category The log message category, controlling how the message
> > + * will be displayed
> > + * \param[in] severity The log message severity, controlling how the message
> > + * will be displayed
> > + *
> > + * Create a log message pertaining to line \a line of file \a fileName. The
> > + * \a severity argument sets the message severity to control whether it will be
> > + * output or dropped.
> > + */
> > +LogMessage::LogMessage(const char *fileName, unsigned int line,
> > + const LogCategory &category, LogSeverity severity)
> > + : category_(category), severity_(severity)
> > +{
> > + init(fileName, line);
> > +}
> > +
> > +void LogMessage::init(const char *fileName, unsigned int line)
> > {
> > /* Log the timestamp, severity and file information. */
> > struct timespec timestamp;
> > clock_gettime(CLOCK_MONOTONIC, ×tamp);
> > - msgStream.fill('0');
> > - msgStream << "[" << timestamp.tv_sec / (60 * 60) << ":"
> > - << std::setw(2) << (timestamp.tv_sec / 60) % 60 << ":"
> > - << std::setw(2) << timestamp.tv_sec % 60 << "."
> > - << std::setw(9) << timestamp.tv_nsec << "]";
> > - msgStream.fill(' ');
> > -
> > - msgStream << " " << log_severity_name(severity);
> > - msgStream << " " << basename(fileName) << ":" << line << " ";
> > + msgStream_.fill('0');
> > + msgStream_ << "[" << timestamp.tv_sec / (60 * 60) << ":"
> > + << std::setw(2) << (timestamp.tv_sec / 60) % 60 << ":"
> > + << std::setw(2) << timestamp.tv_sec % 60 << "."
> > + << std::setw(9) << timestamp.tv_nsec << "]";
> > + msgStream_.fill(' ');
> > +
> > + msgStream_ << " " << log_severity_name(severity_);
> > + msgStream_ << " " << category_.name();
> > + msgStream_ << " " << basename(fileName) << ":" << line << " ";
> > }
> >
> > LogMessage::~LogMessage()
> > {
> > - msgStream << std::endl;
> > + msgStream_ << std::endl;
> >
> > - std::string msg(msgStream.str());
> > - fwrite(msg.data(), msg.size(), 1, stderr);
> > - fflush(stderr);
> > + if (severity_ >= category_.severity()) {
> > + std::string msg(msgStream_.str());
> > + fwrite(msg.data(), msg.size(), 1, stderr);
> > + fflush(stderr);
> > + }
> >
> > if (severity_ == LogSeverity::LogFatal)
> > std::abort();
> > @@ -139,4 +200,64 @@ LogMessage::~LogMessage()
> > * \return A reference to the log message stream
> > */
> >
> > +/**
> > + * \def LOG_DECLARE_CATEGORY(name)
> > + * \hideinitializer
> > + * \brief Declare a category of log messages
> > + *
> > + * This macro is used to declare a log category defined in another compilation
> > + * unit by the LOG_DEFINE_CATEGORY() macro.
> > + *
> > + * The LOG_DECLARE_CATEGORY() macro must be used in the libcamera namespace.
> > + *
> > + * \sa LogCategory
> > + */
> > +
> > +/**
> > + * \def LOG_DEFINE_CATEGORY(name)
> > + * \hideinitializer
> > + * \brief Define a category of log messages
> > + *
> > + * This macro is used to define a log category that can then be used with the
> > + * LOGC() macro. Category names shall be unique, if a category is shared between
> > + * compilation units, it shall be defined in one compilation unit only and
> > + * declared with LOG_DECLARE_CATEGORY() in the other compilation units.
> > + *
> > + * The LOG_DEFINE_CATEGORY() macro must be used in the libcamera namespace.
> > + *
> > + * \sa LogCategory
> > + */
> > +
> > +/**
> > + * \def LOG(category, severity)
> > + * \hideinitializer
> > + * \brief Log a message
> > + * \param[in] category Category (optional)
> > + * \param[in] severity Severity
> > + *
> > + * Return an std::ostream reference to which a message can be logged using the
> > + * iostream API. The \a category, if specified, sets the message category. When
> > + * absent the default category is used. The \a severity controls whether the
> > + * message is printed or discarded, depending on the log level for the category.
> > + *
> > + * If the severity is set to Fatal, execution is aborted and the program
> > + * terminates immediately after printing the message.
> > + */
> > +
> > +/**
> > + * \def ASSERT(condition)
> > + * \brief Abort program execution if assertion fails
> > + *
> > + * If \a condition is false, ASSERT() logs an error message with the Fatal log
> > + * level and aborts program execution.
> > + *
> > + * If the macro NDEBUG is defined before including log.h, ASSERT() generates no
> > + * code.
> > + *
> > + * Using conditions that have side effects with ASSERT() is not recommended, as
> > + * these effects would depend on whether NDEBUG is defined or not. Similarly,
> > + * ASSERT() should not be used to check for errors that can occur under normal
> > + * conditions as those checks would then be removed when compiling with NDEBUG.
> > + */
> > +
>
> Thanks
> Reviewed-by: Jacopo Mondi <jacopo at jmondi.org>
>
> > } /* namespace libcamera */
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list