[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, &timestamp);
> -	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