[{"id":469,"web_url":"https://patchwork.libcamera.org/comment/469/","msgid":"<20190122080639.brnzytwhmzo3ozta@uno.localdomain>","date":"2019-01-22T08:06:39","subject":"Re: [libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log\n\tcategories","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent,\n   great, very very nice, kudos!\n\nOn Mon, Jan 21, 2019 at 09:56:03PM +0200, Laurent Pinchart wrote:\n> Log categories are used to group log messages by topic. Introduce\n> support for categories by making the LOG() macro variadic. Support for\n> configuring log level per category will be introduced in a subsequent\n> commit.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  Documentation/Doxyfile.in   |   2 +-\n>  src/libcamera/include/log.h |  56 ++++++++++-\n>  src/libcamera/log.cpp       | 193 +++++++++++++++++++++++++++++-------\n>  3 files changed, 210 insertions(+), 41 deletions(-)\n>\n> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in\n> index dd74b7295d50..b78fb3a0b0b6 100644\n> --- a/Documentation/Doxyfile.in\n> +++ b/Documentation/Doxyfile.in\n> @@ -2050,7 +2050,7 @@ INCLUDE_FILE_PATTERNS  = *.h\n>  # recursively expanded use the := operator instead of the = operator.\n>  # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n>\n> -PREDEFINED             =\n> +PREDEFINED             = __DOXYGEN__\n>\n\nHere you pre-define __DOXYGEN__\n\n\n>  # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n>  # tag can be used to specify a list of macro names that should be expanded. The\n> diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h\n> index cc3f9404a3d4..ad8f8452c333 100644\n> --- a/src/libcamera/include/log.h\n> +++ b/src/libcamera/include/log.h\n> @@ -19,26 +19,74 @@ enum LogSeverity {\n>  \tLogFatal,\n>  };\n>\n> +class LogCategory\n> +{\n> +public:\n> +\texplicit LogCategory(const char *name);\n> +\t~LogCategory();\n> +\n> +\tconst char *name() const { return name_; }\n> +\tLogSeverity severity() const { return severity_; }\n> +\tvoid setSeverity(LogSeverity severity);\n> +\n> +\tstatic const LogCategory &defaultCategory();\n> +\n> +private:\n> +\tconst char *name_;\n> +\tLogSeverity severity_;\n> +};\n> +\n> +#define LOG_DECLARE_CATEGORY(name)\t\t\t\t\t\\\n> +extern const LogCategory &_LOG_CATEGORY(name)();\n> +\n> +#define LOG_DEFINE_CATEGORY(name)\t\t\t\t\t\\\n> +const LogCategory &_LOG_CATEGORY(name)()\t\t\t\t\\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +\tstatic LogCategory category(#name);\t\t\t\t\\\n> +\treturn category;\t\t\t\t\t\t\\\n> +}\n> +\n>  class LogMessage\n>  {\n>  public:\n>  \tLogMessage(const char *fileName, unsigned int line,\n>  \t\t   LogSeverity severity);\n> +\tLogMessage(const char *fileName, unsigned int line,\n> +\t\t   const LogCategory &category, LogSeverity severity);\n>  \tLogMessage(const LogMessage &) = delete;\n>  \t~LogMessage();\n>\n> -\tstd::ostream &stream() { return msgStream; }\n> +\tstd::ostream &stream() { return msgStream_; }\n>\n>  private:\n> -\tstd::ostringstream msgStream;\n> +\tvoid init(const char *fileName, unsigned int line);\n> +\n> +\tstd::ostringstream msgStream_;\n> +\tconst LogCategory &category_;\n>  \tLogSeverity severity_;\n>  };\n>\n> -#define LOG(severity) LogMessage(__FILE__, __LINE__, Log##severity).stream()\n> +#ifndef __DOXYGEN__\n\nAnd we get here only if it is not defined.\nHow does that work?\n\n> +#define _LOG_CATEGORY(name) logCategory##name\n> +\n> +#define _LOG1(severity) \\\n> +\tLogMessage(__FILE__, __LINE__, Log##severity).stream()\n> +#define _LOG2(category, severity) \\\n> +\tLogMessage(__FILE__, __LINE__, _LOG_CATEGORY(category)(), Log##severity).stream()\n> +\n> +/*\n> + * Expand the LOG() macro to _LOG1() or _LOG2() based on the number of\n> + * arguments.\n> + */\n> +#define _LOG_MACRO(_1, _2, NAME, ...) NAME\n> +#define LOG(...) _LOG_MACRO(__VA_ARGS__, _LOG2, _LOG1)(__VA_ARGS__)\n\nTook me a lot to get around this, and without the comment, it would\nhave take even more! Very very nice :)\n\n> +#else /* __DOXYGEN___ */\n> +#define LOG(category, severity)\n> +#endif /* __DOXYGEN__ */\n>\n>  #ifndef NDEBUG\n>  #define ASSERT(condition) static_cast<void>(({\t\t\t\t\\\n> -\tif (!(condition))\t\t\t\t\t\t\t\\\n> +\tif (!(condition))\t\t\t\t\t\t\\\n>  \t\tLOG(Fatal) << \"assertion \\\"\" #condition \"\\\" failed\";\t\\\n>  }))\n>  #else\n> diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp\n> index 74cba383363d..20503fdcfcc1 100644\n> --- a/src/libcamera/log.cpp\n> +++ b/src/libcamera/log.cpp\n> @@ -37,32 +37,64 @@ namespace libcamera {\n>   */\n>\n>  /**\n> - * \\def LOG(severity)\n> - * \\brief Log a message\n> - *\n> - * Return an std::ostream reference to which a message can be logged using the\n> - * iostream API. The \\a severity controls whether the message is printed or\n> - * dropped, depending on the global log level.\n> + * \\class LogCategory\n> + * \\brief A category of log message\n>   *\n> - * If the severity is set to Fatal, execution is aborted and the program\n> - * terminates immediately after printing the message.\n> + * The LogCategory class represents a category of log messages, related to an\n> + * area of the library. It groups all messages belonging to the same category,\n> + * and is used to control the log level per group.\n>   */\n>\n>  /**\n> - * \\def ASSERT(condition)\n> - * \\brief Abort program execution if assertion fails\n> + * \\brief Construct a log category\n> + * \\param[in] name The category name\n> + */\n> +LogCategory::LogCategory(const char *name)\n> +\t: name_(name), severity_(LogSeverity::LogInfo)\n> +{\n> +}\n> +\n> +LogCategory::~LogCategory()\n> +{\n> +}\n> +\n> +/**\n> + * \\fn LogCategory::name()\n> + * \\brief Retrieve the log category name\n> + * \\return The log category name\n> + */\n> +\n> +/**\n> + * \\fn LogCategory::severity()\n> + * \\brief Retrieve the severity of the log category\n> + * \\sa setSeverity()\n> + * \\return Return the severity of the log category\n> + */\n> +\n> +/**\n> + * \\brief Set the severity of the log category\n>   *\n> - * If \\a condition is false, ASSERT() logs an error message with the Fatal log\n> - * level and aborts program execution.\n> + * Messages of severity higher than or equal to the severity of the log category\n> + * are printed, other messages are discarded.\n> + */\n> +void LogCategory::setSeverity(LogSeverity severity)\n> +{\n> +\tseverity_ = severity;\n> +}\n> +\n> +/**\n> + * \\brief Retrieve the default log category\n>   *\n> - * If the macro NDEBUG is defined before including log.h, ASSERT() generates no\n> - * code.\n> + * The default log category is named \"default\" and is used by the LOG() macro\n> + * when no log category is specified.\n>   *\n> - * Using conditions that have side effects with ASSERT() is not recommended, as\n> - * these effects would depend on whether NDEBUG is defined or not. Similarly,\n> - * ASSERT() should not be used to check for errors that can occur under normal\n> - * conditions as those checks would then be removed when compiling with NDEBUG.\n> + * \\return A pointer to the default log category\n>   */\n> +const LogCategory &LogCategory::defaultCategory()\n> +{\n> +\tstatic LogCategory category(\"default\");\n> +\treturn category;\n> +}\n>\n>  static const char *log_severity_name(LogSeverity severity)\n>  {\n> @@ -90,10 +122,11 @@ static const char *log_severity_name(LogSeverity severity)\n>   */\n>\n>  /**\n> - * \\param fileName The file name where the message is logged from\n> - * \\param line The line number where the message is logged from\n> - * \\param severity The log message severity, controlling how the message will be\n> - * displayed\n> + * \\brief Construct a log message for the default category\n> + * \\param[in] fileName The file name where the message is logged from\n> + * \\param[in] line The line number where the message is logged from\n> + * \\param[in] severity The log message severity, controlling how the message\n> + * will be displayed\n>   *\n>   * Create a log message pertaining to line \\a line of file \\a fileName. The\n>   * \\a severity argument sets the message severity to control whether it will be\n> @@ -101,29 +134,57 @@ static const char *log_severity_name(LogSeverity severity)\n>   */\n>  LogMessage::LogMessage(const char *fileName, unsigned int line,\n>  \t\t       LogSeverity severity)\n> -\t: severity_(severity)\n> +\t: category_(LogCategory::defaultCategory()), severity_(severity)\n> +{\n> +\tinit(fileName, line);\n> +}\n> +\n> +/**\n> + * \\brief Construct a log message for a given category\n> + * \\param[in] fileName The file name where the message is logged from\n> + * \\param[in] line The line number where the message is logged from\n> + * \\param[in] category The log message category, controlling how the message\n> + * will be displayed\n> + * \\param[in] severity The log message severity, controlling how the message\n> + * will be displayed\n> + *\n> + * Create a log message pertaining to line \\a line of file \\a fileName. The\n> + * \\a severity argument sets the message severity to control whether it will be\n> + * output or dropped.\n> + */\n> +LogMessage::LogMessage(const char *fileName, unsigned int line,\n> +\t\t       const LogCategory &category, LogSeverity severity)\n> +\t: category_(category), severity_(severity)\n> +{\n> +\tinit(fileName, line);\n> +}\n> +\n> +void LogMessage::init(const char *fileName, unsigned int line)\n>  {\n>  \t/* Log the timestamp, severity and file information. */\n>  \tstruct timespec timestamp;\n>  \tclock_gettime(CLOCK_MONOTONIC, &timestamp);\n> -\tmsgStream.fill('0');\n> -\tmsgStream << \"[\" << timestamp.tv_sec / (60 * 60) << \":\"\n> -\t\t  << std::setw(2) << (timestamp.tv_sec / 60) % 60 << \":\"\n> -\t\t  << std::setw(2) << timestamp.tv_sec % 60 << \".\"\n> -\t\t  << std::setw(9) << timestamp.tv_nsec << \"]\";\n> -\tmsgStream.fill(' ');\n> -\n> -\tmsgStream << \" \" << log_severity_name(severity);\n> -\tmsgStream << \" \" << basename(fileName) << \":\" << line << \" \";\n> +\tmsgStream_.fill('0');\n> +\tmsgStream_ << \"[\" << timestamp.tv_sec / (60 * 60) << \":\"\n> +\t\t   << std::setw(2) << (timestamp.tv_sec / 60) % 60 << \":\"\n> +\t\t   << std::setw(2) << timestamp.tv_sec % 60 << \".\"\n> +\t\t   << std::setw(9) << timestamp.tv_nsec << \"]\";\n> +\tmsgStream_.fill(' ');\n> +\n> +\tmsgStream_ << \" \" << log_severity_name(severity_);\n> +\tmsgStream_ << \" \" << category_.name();\n> +\tmsgStream_ << \" \" << basename(fileName) << \":\" << line << \" \";\n>  }\n>\n>  LogMessage::~LogMessage()\n>  {\n> -\tmsgStream << std::endl;\n> +\tmsgStream_ << std::endl;\n>\n> -\tstd::string msg(msgStream.str());\n> -\tfwrite(msg.data(), msg.size(), 1, stderr);\n> -\tfflush(stderr);\n> +\tif (severity_ >= category_.severity()) {\n> +\t\tstd::string msg(msgStream_.str());\n> +\t\tfwrite(msg.data(), msg.size(), 1, stderr);\n> +\t\tfflush(stderr);\n> +\t}\n>\n>  \tif (severity_ == LogSeverity::LogFatal)\n>  \t\tstd::abort();\n> @@ -139,4 +200,64 @@ LogMessage::~LogMessage()\n>   * \\return A reference to the log message stream\n>   */\n>\n> +/**\n> + * \\def LOG_DECLARE_CATEGORY(name)\n> + * \\hideinitializer\n> + * \\brief Declare a category of log messages\n> + *\n> + * This macro is used to declare a log category defined in another compilation\n> + * unit by the LOG_DEFINE_CATEGORY() macro.\n> + *\n> + * The LOG_DECLARE_CATEGORY() macro must be used in the libcamera namespace.\n> + *\n> + * \\sa LogCategory\n> + */\n> +\n> +/**\n> + * \\def LOG_DEFINE_CATEGORY(name)\n> + * \\hideinitializer\n> + * \\brief Define a category of log messages\n> + *\n> + * This macro is used to define a log category that can then be used with the\n> + * LOGC() macro. Category names shall be unique, if a category is shared between\n> + * compilation units, it shall be defined in one compilation unit only and\n> + * declared with LOG_DECLARE_CATEGORY() in the other compilation units.\n> + *\n> + * The LOG_DEFINE_CATEGORY() macro must be used in the libcamera namespace.\n> + *\n> + * \\sa LogCategory\n> + */\n> +\n> +/**\n> + * \\def LOG(category, severity)\n> + * \\hideinitializer\n> + * \\brief Log a message\n> + * \\param[in] category Category (optional)\n> + * \\param[in] severity Severity\n> + *\n> + * Return an std::ostream reference to which a message can be logged using the\n> + * iostream API. The \\a category, if specified, sets the message category. When\n> + * absent the default category is used. The  \\a severity controls whether the\n> + * message is printed or discarded, depending on the log level for the category.\n> + *\n> + * If the severity is set to Fatal, execution is aborted and the program\n> + * terminates immediately after printing the message.\n> + */\n> +\n> +/**\n> + * \\def ASSERT(condition)\n> + * \\brief Abort program execution if assertion fails\n> + *\n> + * If \\a condition is false, ASSERT() logs an error message with the Fatal log\n> + * level and aborts program execution.\n> + *\n> + * If the macro NDEBUG is defined before including log.h, ASSERT() generates no\n> + * code.\n> + *\n> + * Using conditions that have side effects with ASSERT() is not recommended, as\n> + * these effects would depend on whether NDEBUG is defined or not. Similarly,\n> + * ASSERT() should not be used to check for errors that can occur under normal\n> + * conditions as those checks would then be removed when compiling with NDEBUG.\n> + */\n> +\n\nThanks\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\n\n>  } /* namespace libcamera */\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay12.mail.gandi.net (relay12.mail.gandi.net\n\t[217.70.178.232])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A630160C81\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 22 Jan 2019 09:06:27 +0100 (CET)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay12.mail.gandi.net (Postfix) with ESMTPSA id C6F1920000C;\n\tTue, 22 Jan 2019 08:06:26 +0000 (UTC)"],"Date":"Tue, 22 Jan 2019 09:06:39 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190122080639.brnzytwhmzo3ozta@uno.localdomain>","References":"<20190121195606.8526-1-laurent.pinchart@ideasonboard.com>\n\t<20190121195606.8526-2-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"7eklnj37iknqco3y\"","Content-Disposition":"inline","In-Reply-To":"<20190121195606.8526-2-laurent.pinchart@ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log\n\tcategories","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 22 Jan 2019 08:06:27 -0000"}},{"id":493,"web_url":"https://patchwork.libcamera.org/comment/493/","msgid":"<20190122135432.GF4455@pendragon.ideasonboard.com>","date":"2019-01-22T13:54:32","subject":"Re: [libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log\n\tcategories","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Tue, Jan 22, 2019 at 09:06:39AM +0100, Jacopo Mondi wrote:\n> Hi Laurent,\n>    great, very very nice, kudos!\n> \n> On Mon, Jan 21, 2019 at 09:56:03PM +0200, Laurent Pinchart wrote:\n> > Log categories are used to group log messages by topic. Introduce\n> > support for categories by making the LOG() macro variadic. Support for\n> > configuring log level per category will be introduced in a subsequent\n> > commit.\n> >\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  Documentation/Doxyfile.in   |   2 +-\n> >  src/libcamera/include/log.h |  56 ++++++++++-\n> >  src/libcamera/log.cpp       | 193 +++++++++++++++++++++++++++++-------\n> >  3 files changed, 210 insertions(+), 41 deletions(-)\n> >\n> > diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in\n> > index dd74b7295d50..b78fb3a0b0b6 100644\n> > --- a/Documentation/Doxyfile.in\n> > +++ b/Documentation/Doxyfile.in\n> > @@ -2050,7 +2050,7 @@ INCLUDE_FILE_PATTERNS  = *.h\n> >  # recursively expanded use the := operator instead of the = operator.\n> >  # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n> >\n> > -PREDEFINED             =\n> > +PREDEFINED             = __DOXYGEN__\n> >\n> \n> Here you pre-define __DOXYGEN__\n> \n> >  # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n> >  # tag can be used to specify a list of macro names that should be expanded. The\n> > diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h\n> > index cc3f9404a3d4..ad8f8452c333 100644\n> > --- a/src/libcamera/include/log.h\n> > +++ b/src/libcamera/include/log.h\n> > @@ -19,26 +19,74 @@ enum LogSeverity {\n> >  \tLogFatal,\n> >  };\n> >\n> > +class LogCategory\n> > +{\n> > +public:\n> > +\texplicit LogCategory(const char *name);\n> > +\t~LogCategory();\n> > +\n> > +\tconst char *name() const { return name_; }\n> > +\tLogSeverity severity() const { return severity_; }\n> > +\tvoid setSeverity(LogSeverity severity);\n> > +\n> > +\tstatic const LogCategory &defaultCategory();\n> > +\n> > +private:\n> > +\tconst char *name_;\n> > +\tLogSeverity severity_;\n> > +};\n> > +\n> > +#define LOG_DECLARE_CATEGORY(name)\t\t\t\t\t\\\n> > +extern const LogCategory &_LOG_CATEGORY(name)();\n> > +\n> > +#define LOG_DEFINE_CATEGORY(name)\t\t\t\t\t\\\n> > +const LogCategory &_LOG_CATEGORY(name)()\t\t\t\t\\\n> > +{\t\t\t\t\t\t\t\t\t\\\n> > +\tstatic LogCategory category(#name);\t\t\t\t\\\n> > +\treturn category;\t\t\t\t\t\t\\\n> > +}\n> > +\n> >  class LogMessage\n> >  {\n> >  public:\n> >  \tLogMessage(const char *fileName, unsigned int line,\n> >  \t\t   LogSeverity severity);\n> > +\tLogMessage(const char *fileName, unsigned int line,\n> > +\t\t   const LogCategory &category, LogSeverity severity);\n> >  \tLogMessage(const LogMessage &) = delete;\n> >  \t~LogMessage();\n> >\n> > -\tstd::ostream &stream() { return msgStream; }\n> > +\tstd::ostream &stream() { return msgStream_; }\n> >\n> >  private:\n> > -\tstd::ostringstream msgStream;\n> > +\tvoid init(const char *fileName, unsigned int line);\n> > +\n> > +\tstd::ostringstream msgStream_;\n> > +\tconst LogCategory &category_;\n> >  \tLogSeverity severity_;\n> >  };\n> >\n> > -#define LOG(severity) LogMessage(__FILE__, __LINE__, Log##severity).stream()\n> > +#ifndef __DOXYGEN__\n> \n> And we get here only if it is not defined.\n> How does that work?\n\n__DOXYGEN__ is only defined when doxygen parses the code. When the code\nis compiled, it isn't. The effect is that the _LOG_CATEGORY, _LOG1,\n_LOG2 and _LOG_MACRO macros are hidden from doxygen, and the LOG macro\nthat doxygen sees is a fake one with two parameters in order to let the\ndocumentation describe them with \\param.\n\n> > +#define _LOG_CATEGORY(name) logCategory##name\n> > +\n> > +#define _LOG1(severity) \\\n> > +\tLogMessage(__FILE__, __LINE__, Log##severity).stream()\n> > +#define _LOG2(category, severity) \\\n> > +\tLogMessage(__FILE__, __LINE__, _LOG_CATEGORY(category)(), Log##severity).stream()\n> > +\n> > +/*\n> > + * Expand the LOG() macro to _LOG1() or _LOG2() based on the number of\n> > + * arguments.\n> > + */\n> > +#define _LOG_MACRO(_1, _2, NAME, ...) NAME\n> > +#define LOG(...) _LOG_MACRO(__VA_ARGS__, _LOG2, _LOG1)(__VA_ARGS__)\n> \n> Took me a lot to get around this, and without the comment, it would\n> have take even more! Very very nice :)\n> \n> > +#else /* __DOXYGEN___ */\n> > +#define LOG(category, severity)\n> > +#endif /* __DOXYGEN__ */\n> >\n> >  #ifndef NDEBUG\n> >  #define ASSERT(condition) static_cast<void>(({\t\t\t\t\\\n> > -\tif (!(condition))\t\t\t\t\t\t\t\\\n> > +\tif (!(condition))\t\t\t\t\t\t\\\n> >  \t\tLOG(Fatal) << \"assertion \\\"\" #condition \"\\\" failed\";\t\\\n> >  }))\n> >  #else\n> > diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp\n> > index 74cba383363d..20503fdcfcc1 100644\n> > --- a/src/libcamera/log.cpp\n> > +++ b/src/libcamera/log.cpp\n> > @@ -37,32 +37,64 @@ namespace libcamera {\n> >   */\n> >\n> >  /**\n> > - * \\def LOG(severity)\n> > - * \\brief Log a message\n> > - *\n> > - * Return an std::ostream reference to which a message can be logged using the\n> > - * iostream API. The \\a severity controls whether the message is printed or\n> > - * dropped, depending on the global log level.\n> > + * \\class LogCategory\n> > + * \\brief A category of log message\n> >   *\n> > - * If the severity is set to Fatal, execution is aborted and the program\n> > - * terminates immediately after printing the message.\n> > + * The LogCategory class represents a category of log messages, related to an\n> > + * area of the library. It groups all messages belonging to the same category,\n> > + * and is used to control the log level per group.\n> >   */\n> >\n> >  /**\n> > - * \\def ASSERT(condition)\n> > - * \\brief Abort program execution if assertion fails\n> > + * \\brief Construct a log category\n> > + * \\param[in] name The category name\n> > + */\n> > +LogCategory::LogCategory(const char *name)\n> > +\t: name_(name), severity_(LogSeverity::LogInfo)\n> > +{\n> > +}\n> > +\n> > +LogCategory::~LogCategory()\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\fn LogCategory::name()\n> > + * \\brief Retrieve the log category name\n> > + * \\return The log category name\n> > + */\n> > +\n> > +/**\n> > + * \\fn LogCategory::severity()\n> > + * \\brief Retrieve the severity of the log category\n> > + * \\sa setSeverity()\n> > + * \\return Return the severity of the log category\n> > + */\n> > +\n> > +/**\n> > + * \\brief Set the severity of the log category\n> >   *\n> > - * If \\a condition is false, ASSERT() logs an error message with the Fatal log\n> > - * level and aborts program execution.\n> > + * Messages of severity higher than or equal to the severity of the log category\n> > + * are printed, other messages are discarded.\n> > + */\n> > +void LogCategory::setSeverity(LogSeverity severity)\n> > +{\n> > +\tseverity_ = severity;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Retrieve the default log category\n> >   *\n> > - * If the macro NDEBUG is defined before including log.h, ASSERT() generates no\n> > - * code.\n> > + * The default log category is named \"default\" and is used by the LOG() macro\n> > + * when no log category is specified.\n> >   *\n> > - * Using conditions that have side effects with ASSERT() is not recommended, as\n> > - * these effects would depend on whether NDEBUG is defined or not. Similarly,\n> > - * ASSERT() should not be used to check for errors that can occur under normal\n> > - * conditions as those checks would then be removed when compiling with NDEBUG.\n> > + * \\return A pointer to the default log category\n> >   */\n> > +const LogCategory &LogCategory::defaultCategory()\n> > +{\n> > +\tstatic LogCategory category(\"default\");\n> > +\treturn category;\n> > +}\n> >\n> >  static const char *log_severity_name(LogSeverity severity)\n> >  {\n> > @@ -90,10 +122,11 @@ static const char *log_severity_name(LogSeverity severity)\n> >   */\n> >\n> >  /**\n> > - * \\param fileName The file name where the message is logged from\n> > - * \\param line The line number where the message is logged from\n> > - * \\param severity The log message severity, controlling how the message will be\n> > - * displayed\n> > + * \\brief Construct a log message for the default category\n> > + * \\param[in] fileName The file name where the message is logged from\n> > + * \\param[in] line The line number where the message is logged from\n> > + * \\param[in] severity The log message severity, controlling how the message\n> > + * will be displayed\n> >   *\n> >   * Create a log message pertaining to line \\a line of file \\a fileName. The\n> >   * \\a severity argument sets the message severity to control whether it will be\n> > @@ -101,29 +134,57 @@ static const char *log_severity_name(LogSeverity severity)\n> >   */\n> >  LogMessage::LogMessage(const char *fileName, unsigned int line,\n> >  \t\t       LogSeverity severity)\n> > -\t: severity_(severity)\n> > +\t: category_(LogCategory::defaultCategory()), severity_(severity)\n> > +{\n> > +\tinit(fileName, line);\n> > +}\n> > +\n> > +/**\n> > + * \\brief Construct a log message for a given category\n> > + * \\param[in] fileName The file name where the message is logged from\n> > + * \\param[in] line The line number where the message is logged from\n> > + * \\param[in] category The log message category, controlling how the message\n> > + * will be displayed\n> > + * \\param[in] severity The log message severity, controlling how the message\n> > + * will be displayed\n> > + *\n> > + * Create a log message pertaining to line \\a line of file \\a fileName. The\n> > + * \\a severity argument sets the message severity to control whether it will be\n> > + * output or dropped.\n> > + */\n> > +LogMessage::LogMessage(const char *fileName, unsigned int line,\n> > +\t\t       const LogCategory &category, LogSeverity severity)\n> > +\t: category_(category), severity_(severity)\n> > +{\n> > +\tinit(fileName, line);\n> > +}\n> > +\n> > +void LogMessage::init(const char *fileName, unsigned int line)\n> >  {\n> >  \t/* Log the timestamp, severity and file information. */\n> >  \tstruct timespec timestamp;\n> >  \tclock_gettime(CLOCK_MONOTONIC, &timestamp);\n> > -\tmsgStream.fill('0');\n> > -\tmsgStream << \"[\" << timestamp.tv_sec / (60 * 60) << \":\"\n> > -\t\t  << std::setw(2) << (timestamp.tv_sec / 60) % 60 << \":\"\n> > -\t\t  << std::setw(2) << timestamp.tv_sec % 60 << \".\"\n> > -\t\t  << std::setw(9) << timestamp.tv_nsec << \"]\";\n> > -\tmsgStream.fill(' ');\n> > -\n> > -\tmsgStream << \" \" << log_severity_name(severity);\n> > -\tmsgStream << \" \" << basename(fileName) << \":\" << line << \" \";\n> > +\tmsgStream_.fill('0');\n> > +\tmsgStream_ << \"[\" << timestamp.tv_sec / (60 * 60) << \":\"\n> > +\t\t   << std::setw(2) << (timestamp.tv_sec / 60) % 60 << \":\"\n> > +\t\t   << std::setw(2) << timestamp.tv_sec % 60 << \".\"\n> > +\t\t   << std::setw(9) << timestamp.tv_nsec << \"]\";\n> > +\tmsgStream_.fill(' ');\n> > +\n> > +\tmsgStream_ << \" \" << log_severity_name(severity_);\n> > +\tmsgStream_ << \" \" << category_.name();\n> > +\tmsgStream_ << \" \" << basename(fileName) << \":\" << line << \" \";\n> >  }\n> >\n> >  LogMessage::~LogMessage()\n> >  {\n> > -\tmsgStream << std::endl;\n> > +\tmsgStream_ << std::endl;\n> >\n> > -\tstd::string msg(msgStream.str());\n> > -\tfwrite(msg.data(), msg.size(), 1, stderr);\n> > -\tfflush(stderr);\n> > +\tif (severity_ >= category_.severity()) {\n> > +\t\tstd::string msg(msgStream_.str());\n> > +\t\tfwrite(msg.data(), msg.size(), 1, stderr);\n> > +\t\tfflush(stderr);\n> > +\t}\n> >\n> >  \tif (severity_ == LogSeverity::LogFatal)\n> >  \t\tstd::abort();\n> > @@ -139,4 +200,64 @@ LogMessage::~LogMessage()\n> >   * \\return A reference to the log message stream\n> >   */\n> >\n> > +/**\n> > + * \\def LOG_DECLARE_CATEGORY(name)\n> > + * \\hideinitializer\n> > + * \\brief Declare a category of log messages\n> > + *\n> > + * This macro is used to declare a log category defined in another compilation\n> > + * unit by the LOG_DEFINE_CATEGORY() macro.\n> > + *\n> > + * The LOG_DECLARE_CATEGORY() macro must be used in the libcamera namespace.\n> > + *\n> > + * \\sa LogCategory\n> > + */\n> > +\n> > +/**\n> > + * \\def LOG_DEFINE_CATEGORY(name)\n> > + * \\hideinitializer\n> > + * \\brief Define a category of log messages\n> > + *\n> > + * This macro is used to define a log category that can then be used with the\n> > + * LOGC() macro. Category names shall be unique, if a category is shared between\n> > + * compilation units, it shall be defined in one compilation unit only and\n> > + * declared with LOG_DECLARE_CATEGORY() in the other compilation units.\n> > + *\n> > + * The LOG_DEFINE_CATEGORY() macro must be used in the libcamera namespace.\n> > + *\n> > + * \\sa LogCategory\n> > + */\n> > +\n> > +/**\n> > + * \\def LOG(category, severity)\n> > + * \\hideinitializer\n> > + * \\brief Log a message\n> > + * \\param[in] category Category (optional)\n> > + * \\param[in] severity Severity\n> > + *\n> > + * Return an std::ostream reference to which a message can be logged using the\n> > + * iostream API. The \\a category, if specified, sets the message category. When\n> > + * absent the default category is used. The  \\a severity controls whether the\n> > + * message is printed or discarded, depending on the log level for the category.\n> > + *\n> > + * If the severity is set to Fatal, execution is aborted and the program\n> > + * terminates immediately after printing the message.\n> > + */\n> > +\n> > +/**\n> > + * \\def ASSERT(condition)\n> > + * \\brief Abort program execution if assertion fails\n> > + *\n> > + * If \\a condition is false, ASSERT() logs an error message with the Fatal log\n> > + * level and aborts program execution.\n> > + *\n> > + * If the macro NDEBUG is defined before including log.h, ASSERT() generates no\n> > + * code.\n> > + *\n> > + * Using conditions that have side effects with ASSERT() is not recommended, as\n> > + * these effects would depend on whether NDEBUG is defined or not. Similarly,\n> > + * ASSERT() should not be used to check for errors that can occur under normal\n> > + * conditions as those checks would then be removed when compiling with NDEBUG.\n> > + */\n> > +\n> \n> Thanks\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> \n> >  } /* namespace libcamera */","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 16F5A60B1B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 22 Jan 2019 14:54:33 +0100 (CET)","from pendragon.ideasonboard.com\n\t(dfj612yyyyyyyyyyyyyby-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00::2])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7D75A53E;\n\tTue, 22 Jan 2019 14:54:32 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1548165272;\n\tbh=aN6KTevWnkmaL1Skp4WQIc5shR9RX5CNuvC+xYneS7o=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Ucj/1ZHucFsLsuWbNFAuQyyKSNkrYwauVCtvryy9UCTS6Ls7PLFuDH5Oi9HLaGjjU\n\tlvVChHwNFe5V+feofz/pUlN+23wkZbAukTWwMsA1A0Whoxco8KQ/MB2SttlESykXXk\n\tLjdeIE5LitUcxSiqBK9M0c9Hvpyjw3TH9kuE3ukU=","Date":"Tue, 22 Jan 2019 15:54:32 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190122135432.GF4455@pendragon.ideasonboard.com>","References":"<20190121195606.8526-1-laurent.pinchart@ideasonboard.com>\n\t<20190121195606.8526-2-laurent.pinchart@ideasonboard.com>\n\t<20190122080639.brnzytwhmzo3ozta@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20190122080639.brnzytwhmzo3ozta@uno.localdomain>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log\n\tcategories","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 22 Jan 2019 13:54:33 -0000"}}]