[libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log categories
Jacopo Mondi
jacopo at jmondi.org
Tue Jan 22 09:06:39 CET 2019
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?
> +#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
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.libcamera.org/pipermail/libcamera-devel/attachments/20190122/3449c718/attachment.sig>
More information about the libcamera-devel
mailing list