[libcamera-devel,3/6] libcamera: log: Add log categories

Message ID 20190121005930.10112-4-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • Extend the logger with categories and configuration
Related show

Commit Message

Laurent Pinchart Jan. 21, 2019, 12:59 a.m. UTC
Log categories are used to group log messages by topic. Introduce
support for categories with a new LOGC() macro. Support for configuring
log level per category will be introduced in a subsequent commit.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/include/log.h |  45 +++++++-
 src/libcamera/log.cpp       | 217 ++++++++++++++++++++++++++++++------
 2 files changed, 222 insertions(+), 40 deletions(-)

Patch

diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h
index cc3f9404a3d4..c7644291724d 100644
--- a/src/libcamera/include/log.h
+++ b/src/libcamera/include/log.h
@@ -19,26 +19,63 @@  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_CATEGORY(name) logCategory##name
+
+#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()
+#define LOG(severity) \
+	LogMessage(__FILE__, __LINE__, Log##severity).stream()
+#define LOGC(category, severity) \
+	LogMessage(__FILE__, __LINE__, LOG_CATEGORY(category)(), Log##severity).stream()
 
 #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..8f3d1c181245 100644
--- a/src/libcamera/log.cpp
+++ b/src/libcamera/log.cpp
@@ -37,32 +37,64 @@  namespace libcamera {
  */
 
 /**
- * \def LOG(severity)
- * \brief Log a message
+ * \class LogCategory
+ * \brief A category of log 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.
- *
- * 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,88 @@  LogMessage::~LogMessage()
  * \return A reference to the log message stream
  */
 
+/**
+ * \def LOG_CATEGORY(name)
+ * \brief Name of the log category symbol for a category name
+ */
+
+/**
+ * \def LOG_DECLARE_CATEGORY(name)
+ * \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_DECLARE_CATEGORY(name)
+ * \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)
+ * \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(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
+ * discarded, depending on the global log level.
+ *
+ * If the severity is set to Fatal, execution is aborted and the program
+ * terminates immediately after printing the message.
+ */
+
+/**
+ * \def LOGC(category, severity)
+ * \brief Log a message with a category
+ *
+ * Return an std::ostream reference to which a message can be logged using the
+ * iostream API. The \a category and \a severity controls whether the message is
+ * printed or discarded, depending on if the \a category is enabled and on the
+ * log level for the \a 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.
+ */
+
 } /* namespace libcamera */