From patchwork Mon Jan 21 19:56:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 312 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 166CC60C80 for ; Mon, 21 Jan 2019 20:56:12 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8E1B0598 for ; Mon, 21 Jan 2019 20:56:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548100571; bh=VM3Q2pVwkCvcGuehptx0GCQga9z7hTCZaMZYMbB1X6I=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HVQ6hlnOuzSvN9gHRynRBu5nRQqwz4t9NX1niP4fr81n525624dWLAlM+XLNfegm9 Brt6OLsFry5DMm3QCiSesi4APXQUaXPooKTmgpY4f4mt9rKpugTifvUs+s0telfM/k ptflaf79iqa+9iY69eCZpK5gEhhDm2xHhA0Iyr1Y= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 21 Jan 2019 21:56:03 +0200 Message-Id: <20190121195606.8526-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> References: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/4] libcamera: log: Add log categories X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jan 2019 19:56:12 -0000 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 Reviewed-by: Jacopo Mondi --- 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__ # 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__ +#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__) +#else /* __DOXYGEN___ */ +#define LOG(category, severity) +#endif /* __DOXYGEN__ */ #ifndef NDEBUG #define ASSERT(condition) static_cast(({ \ - 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. + */ + } /* namespace libcamera */ From patchwork Mon Jan 21 19:56:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 313 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4992860C80 for ; Mon, 21 Jan 2019 20:56:12 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DD49A53E for ; Mon, 21 Jan 2019 20:56:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548100572; bh=dmViCUYBJ4hTrZt5n/jXyOvaBhWx3PVqButVID1Lw4c=; h=From:To:Subject:Date:In-Reply-To:References:From; b=S2IEuYNE9uTSq59FVWRkXCOWUloc6SmoNRVtqznJKLr29gSQ+Jz3s4oik+vZsWgQj kqQFQSDWJLkU4l8ZjdXS4hQrq/EGuie4qLTxJjKKTMEtgRTUiLpGszvjqcqrdEh4cP Kt4GZTNBdjw8VgrS5YSCX5lZpvZabi+tPFmL6vRo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 21 Jan 2019 21:56:04 +0200 Message-Id: <20190121195606.8526-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> References: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/4] libcamera: log: Get log levels from the environment X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jan 2019 19:56:12 -0000 Set the log level for each log category from the environment variable LIBCAMERA_LOG_LEVELS. The variable contains a comma-separated list of category:level pairs, and category names can include wildcards. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- src/libcamera/log.cpp | 202 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp index 20503fdcfcc1..23bb9edb9d14 100644 --- a/src/libcamera/log.cpp +++ b/src/libcamera/log.cpp @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include "log.h" #include "utils.h" @@ -17,10 +19,208 @@ /** * \file log.h * \brief Logging infrastructure + * + * libcamera includes a logging infrastructure used through the library that + * allows inspection of internal operation in a user-configurable way. The log + * messages are grouped in categories that represent areas of libcamera, and + * output of messages for each category can be controlled by independent log + * levels. + * + * The levels are configurable through the LIBCAMERA_LOG_LEVELS environment + * variable that contains a comma-separated list of 'category=level' pairs. + * + * The category names are strings and can include a wildcard ('*') character at + * the end to match multiple categories. + * + * The level are either numeric values, or strings containing the log level + * name. The available log levels are DEBUG, INFO, WARN, ERROR and FATAL. Log + * message with a level higher than or equal to the configured log level for + * their category are output to the log, while other messages are silently + * discarded. */ namespace libcamera { +/** + * \brief Message logger + * + * The Logger class handles log configuration. + */ +class Logger +{ +public: + static Logger *instance(); + +private: + Logger(); + + void parseLogLevels(); + static LogSeverity parseLogLevel(const std::string &level); + + friend LogCategory; + void registerCategory(LogCategory *category); + void unregisterCategory(LogCategory *category); + + std::unordered_set categories_; + std::list> levels_; +}; + +/** + * \brief Retrieve the logger instance + * + * The Logger is a singleton and can't be constructed manually. This function + * shall instead be used to retrieve the single global instance of the logger. + * + * \return The logger instance + */ +Logger *Logger::instance() +{ + static Logger instance; + return &instance; +} + +/** + * \brief Construct a logger + */ +Logger::Logger() +{ + parseLogLevels(); +} + +/** + * \brief Parse the log levels from the environment + * + * The logr levels are stored in LIBCAMERA_LOG_LEVELS environement variable as a list + * of "category=level" pairs, separated by commas (','). Parse the variable and + * store the levels to configure all log categories. + */ +void Logger::parseLogLevels() +{ + const char *debug = secure_getenv("LIBCAMERA_LOG_LEVELS"); + if (!debug) + return; + + for (const char *pair = debug; *debug != '\0'; pair = debug) { + const char *comma = strchrnul(debug, ','); + size_t len = comma - pair; + + /* Skip over the comma. */ + debug = *comma == ',' ? comma + 1 : comma; + + /* Skip to the next pair if the pair is empty. */ + if (!len) + continue; + + std::string category; + std::string level; + + const char *colon = static_cast(memchr(pair, ':', len)); + if (!colon) { + /* 'x' is a shortcut for '*:x'. */ + category = "*"; + level = std::string(pair, len); + } else { + category = std::string(pair, colon - pair); + level = std::string(colon + 1, comma - colon - 1); + } + + /* Both the category and the level must be specified. */ + if (category.empty() || level.empty()) + continue; + + LogSeverity severity = parseLogLevel(level); + if (severity == -1) + continue; + + levels_.push_back({ category, severity }); + } +} + +/** + * \brief Parse a log level string into a LogSeverity + * \param[in] level The log level string + * + * Log levels can be specified as an integer value in the range from LogDebug to + * LogFatal, or as a string corresponding to the severity name in uppercase. Any + * other value is invalid. + * + * \return The log severity, or -1 if the string is invalid + */ +LogSeverity Logger::parseLogLevel(const std::string &level) +{ + static const char *const names[] = { + "DEBUG", + "INFO", + "WARN", + "ERROR", + "FATAL", + }; + + int severity; + + if (std::isdigit(level[0])) { + char *endptr; + severity = strtoul(level.c_str(), &endptr, 10); + if (*endptr != '\0' || severity > LogFatal) + severity = -1; + } else { + severity = -1; + for (unsigned int i = 0; i < ARRAY_SIZE(names); ++i) { + if (names[i] == level) { + severity = i; + break; + } + } + } + + return static_cast(severity); +} + +/** + * \brief Register a log category with the logger + * \param[in] category The log category + * + * Log categories must have unique names. If a category with the same name + * already exists this function performs no operation. + */ +void Logger::registerCategory(LogCategory *category) +{ + categories_.insert(category); + + const std::string &name = category->name(); + for (const std::pair &level : levels_) { + bool match = true; + + for (unsigned int i = 0; i < level.first.size(); ++i) { + if (level.first[i] == '*') + break; + + if (i >= name.size() || + name[i] != level.first[i]) { + match = false; + break; + } + } + + if (match) { + category->setSeverity(level.second); + break; + } + } +} + +/** + * \brief Unregister a log category from the logger + * \param[in] category The log category + * + * If the \a category hasn't been registered with the logger this function + * performs no operation. + */ +void Logger::unregisterCategory(LogCategory *category) +{ + categories_.erase(category); +} + /** * \enum LogSeverity * Log message severity @@ -52,10 +252,12 @@ namespace libcamera { LogCategory::LogCategory(const char *name) : name_(name), severity_(LogSeverity::LogInfo) { + Logger::instance()->registerCategory(this); } LogCategory::~LogCategory() { + Logger::instance()->unregisterCategory(this); } /** From patchwork Mon Jan 21 19:56:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 314 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 96D8C60C80 for ; Mon, 21 Jan 2019 20:56:12 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 363B0598 for ; Mon, 21 Jan 2019 20:56:12 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548100572; bh=ZBIQlpmFV6blC8Lxb42ENmxmm8zQx0Kl4Dg2Hl+zhrU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=dLLVtAD+5LjPFZYBI9uogPHuP9a1UDHKNx5nLQxfWiQFHXPw8yGrHZdDR2Dp9vHoe ck93rW61kcPORYW8If5TEk+BxKUmr85xwgJ4J1FS01fep9x5/uEud+XuIYp4sBBBMM al3juM64ZYX5QIrcc3zhFy2rQ3rZFnFoC7g/3vaw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 21 Jan 2019 21:56:05 +0200 Message-Id: <20190121195606.8526-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> References: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/4] libcamera: log: Get log output file from the environment X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jan 2019 19:56:12 -0000 If the LIBCAMERA_LOG_FILE environment variable is set, open the file it points to and redirect the logger output to it. Signed-off-by: Laurent Pinchart --- src/libcamera/log.cpp | 47 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp index 23bb9edb9d14..241dc5395976 100644 --- a/src/libcamera/log.cpp +++ b/src/libcamera/log.cpp @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -37,6 +39,12 @@ * message with a level higher than or equal to the configured log level for * their category are output to the log, while other messages are silently * discarded. + * + * By default log messages are output to stderr. They can be redirected to a log + * file by setting the LIBCAMERA_LOG_FILE environment variable to the name of + * the file. The file must be writable and is truncated if it exists. If any + * error occurs when opening the file, the file is ignored and the log is output + * to stderr. */ namespace libcamera { @@ -51,9 +59,12 @@ class Logger public: static Logger *instance(); + void write(const std::string &msg); + private: Logger(); + void parseLogFile(); void parseLogLevels(); static LogSeverity parseLogLevel(const std::string &level); @@ -63,6 +74,9 @@ private: std::unordered_set categories_; std::list> levels_; + + std::ofstream file_; + std::ostream *output_; }; /** @@ -79,14 +93,44 @@ Logger *Logger::instance() return &instance; } +/** + * \brief Write a message to the configured logger output + * \param[in] msg The message string + */ +void Logger::write(const std::string &msg) +{ + output_->write(msg.c_str(), msg.size()); + output_->flush(); +} + /** * \brief Construct a logger */ Logger::Logger() + : output_(&std::cerr) { + parseLogFile(); parseLogLevels(); } +/** + * \brief Parse the log output file from the environment + * + * If the LIBCAMERA_LOG_FILE environment variable is set, open the file it + * points to and redirect the logger output to it. Errors are silently ignored + * and don't affect the logger output (set to stderr). + */ +void Logger::parseLogFile() +{ + const char *file = secure_getenv("LIBCAMERA_LOG_FILE"); + if (!file) + return; + + file_.open(file); + if (file_.good()) + output_ = &file_; +} + /** * \brief Parse the log levels from the environment * @@ -384,8 +428,7 @@ LogMessage::~LogMessage() if (severity_ >= category_.severity()) { std::string msg(msgStream_.str()); - fwrite(msg.data(), msg.size(), 1, stderr); - fflush(stderr); + Logger::instance()->write(msg); } if (severity_ == LogSeverity::LogFatal) From patchwork Mon Jan 21 19:56:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 315 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F1CCC60C80 for ; Mon, 21 Jan 2019 20:56:12 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 826C253E for ; Mon, 21 Jan 2019 20:56:12 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1548100572; bh=6dX3hk3W0z2Ut3NOG96dYTLfEC1HfHzHY4pKqUnymW4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=IAlrD+pcl2Q+WM5+PS0KlkiyeiW+upc07wnK3iiRXbQgtTDP9P5hi7hGM2pnVpLBZ 7N05fUYNtIVVY4VHZwUShzJo4aX1b71Ep4KSb5ZesJkCvzsGqeMIJ3JNZpWGKkm6sY YUpm5Xf5RWw6B5sCQ4xsXz1A5z7HT0CQxwpfvZM0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 21 Jan 2019 21:56:06 +0200 Message-Id: <20190121195606.8526-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> References: <20190121195606.8526-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 4/4] libcamera: Use log categories X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jan 2019 19:56:13 -0000 Use log categories in the whole existing code base. Signed-off-by: Laurent Pinchart --- src/libcamera/camera_manager.cpp | 14 ++++--- src/libcamera/device_enumerator.cpp | 25 +++++++----- src/libcamera/event_dispatcher.cpp | 4 ++ src/libcamera/event_dispatcher_poll.cpp | 28 +++++++------ src/libcamera/media_device.cpp | 52 +++++++++++++++---------- src/libcamera/media_object.cpp | 7 +++- src/libcamera/pipeline_handler.cpp | 5 ++- src/libcamera/timer.cpp | 7 +++- src/libcamera/v4l2_device.cpp | 25 +++++++----- 9 files changed, 107 insertions(+), 60 deletions(-) diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index d76eaa7ace86..372b5a59ebfc 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -21,6 +21,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(Camera) + /** * \class CameraManager * \brief Provide access and manage all cameras in the system @@ -101,8 +103,9 @@ int CameraManager::start() break; } - LOG(Debug) << "Pipeline handler \"" << factory->name() - << "\" matched"; + LOG(Camera, Debug) + << "Pipeline handler \"" << factory->name() + << "\" matched"; pipes_.push_back(pipe); } } @@ -176,8 +179,9 @@ void CameraManager::addCamera(std::shared_ptr camera) { for (std::shared_ptr c : cameras_) { if (c->name() == camera->name()) { - LOG(Warning) << "Registering camera with duplicate name '" - << camera->name() << "'"; + LOG(Camera, Warning) + << "Registering camera with duplicate name '" + << camera->name() << "'"; break; } } @@ -216,7 +220,7 @@ CameraManager *CameraManager::instance() void CameraManager::setEventDispatcher(std::unique_ptr dispatcher) { if (dispatcher_) { - LOG(Warning) << "Event dispatcher is already set"; + LOG(Camera, Warning) << "Event dispatcher is already set"; return; } diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index 55c510e3b79a..22d4edee1cb2 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -42,6 +42,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(DeviceEnumerator) + /** * \class DeviceMatch * \brief Description of a media device search pattern @@ -155,7 +157,8 @@ DeviceEnumerator::~DeviceEnumerator() { for (MediaDevice *dev : devices_) { if (dev->busy()) - LOG(Error) << "Removing media device while still in use"; + LOG(DeviceEnumerator, Error) + << "Removing media device while still in use"; delete dev; } @@ -205,13 +208,15 @@ int DeviceEnumerator::addDevice(const std::string &devnode) ret = media->populate(); if (ret < 0) { - LOG(Info) << "Unable to populate media device " << devnode << - " (" << strerror(-ret) << "), skipping"; + LOG(DeviceEnumerator, Info) + << "Unable to populate media device " << devnode + << " (" << strerror(-ret) << "), skipping"; return ret; } - LOG(Debug) << "New media device \"" << media->driver() - << "\" created from " << devnode; + LOG(DeviceEnumerator, Debug) + << "New media device \"" << media->driver() + << "\" created from " << devnode; /* Associate entities to device node paths. */ for (MediaEntity *entity : media->entities()) { @@ -251,8 +256,9 @@ MediaDevice *DeviceEnumerator::search(const DeviceMatch &dm) continue; if (dm.match(dev)) { - LOG(Debug) << "Successful match for media device \"" - << dev->driver() << "\""; + LOG(DeviceEnumerator, Debug) + << "Successful match for media device \"" + << dev->driver() << "\""; return dev; } } @@ -330,8 +336,9 @@ int DeviceEnumeratorUdev::enumerate() dev = udev_device_new_from_syspath(udev_, syspath); if (!dev) { - LOG(Warning) << "Failed to get device for '" << - syspath << "', skipping"; + LOG(DeviceEnumerator, Warning) + << "Failed to get device for '" + << syspath << "', skipping"; continue; } diff --git a/src/libcamera/event_dispatcher.cpp b/src/libcamera/event_dispatcher.cpp index 5462730214d6..f7c40734095e 100644 --- a/src/libcamera/event_dispatcher.cpp +++ b/src/libcamera/event_dispatcher.cpp @@ -7,12 +7,16 @@ #include +#include "log.h" + /** * \file event_dispatcher.h */ namespace libcamera { +LOG_DEFINE_CATEGORY(Event) + /** * \class EventDispatcher * \brief Interface to manage the libcamera events and timers diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp index 2072ae0aa3da..eefac54ca6da 100644 --- a/src/libcamera/event_dispatcher_poll.cpp +++ b/src/libcamera/event_dispatcher_poll.cpp @@ -22,6 +22,8 @@ namespace libcamera { +LOG_DECLARE_CATEGORY(Event) + static const char *notifierType(EventNotifier::Type type) { if (type == EventNotifier::Read) @@ -53,8 +55,9 @@ void EventDispatcherPoll::registerEventNotifier(EventNotifier *notifier) EventNotifier::Type type = notifier->type(); if (set.notifiers[type] && set.notifiers[type] != notifier) { - LOG(Warning) << "Ignoring duplicate " << notifierType(type) - << " notifier for fd " << notifier->fd(); + LOG(Event, Warning) + << "Ignoring duplicate " << notifierType(type) + << " notifier for fd " << notifier->fd(); return; } @@ -74,8 +77,9 @@ void EventDispatcherPoll::unregisterEventNotifier(EventNotifier *notifier) return; if (set.notifiers[type] != notifier) { - LOG(Warning) << notifierType(type) << " notifier for fd " - << notifier->fd() << " is not registered"; + LOG(Event, Warning) + << notifierType(type) << " notifier for fd " + << notifier->fd() << " is not registered"; return; } @@ -141,9 +145,10 @@ void EventDispatcherPoll::processEvents() timeout.tv_nsec = 0; } - LOG(Debug) << "timeout " << timeout.tv_sec << "." - << std::setfill('0') << std::setw(9) - << timeout.tv_nsec; + LOG(Event, Debug) + << "timeout " << timeout.tv_sec << "." + << std::setfill('0') << std::setw(9) + << timeout.tv_nsec; } /* Wait for events and process notifiers and timers. */ @@ -151,7 +156,7 @@ void EventDispatcherPoll::processEvents() nextTimer ? &timeout : nullptr, nullptr); if (ret < 0) { ret = -errno; - LOG(Warning) << "poll() failed with " << strerror(-ret); + LOG(Event, Warning) << "poll() failed with " << strerror(-ret); } else if (ret > 0) { processNotifiers(pollfds); } @@ -201,9 +206,10 @@ void EventDispatcherPoll::processNotifiers(const std::vector &pol * notifier immediately. */ if (pfd.revents & POLLNVAL) { - LOG(Warning) << "Disabling " << notifierType(event.type) - << " due to invalid file descriptor " - << pfd.fd; + LOG(Event, Warning) + << "Disabling " << notifierType(event.type) + << " due to invalid file descriptor " + << pfd.fd; unregisterEventNotifier(notifier); continue; } diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp index 0ee55060aa9b..eb2e68bb73d0 100644 --- a/src/libcamera/media_device.cpp +++ b/src/libcamera/media_device.cpp @@ -27,6 +27,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(MediaDevice) + /** * \class MediaDevice * \brief The MediaDevice represents a Media Controller device with its full @@ -139,15 +141,16 @@ bool MediaDevice::acquire() int MediaDevice::open() { if (fd_ != -1) { - LOG(Error) << "MediaDevice already open"; + LOG(MediaDevice, Error) << "MediaDevice already open"; return -EBUSY; } int ret = ::open(devnode_.c_str(), O_RDWR); if (ret < 0) { ret = -errno; - LOG(Error) << "Failed to open media device at " << devnode_ - << ": " << strerror(-ret); + LOG(MediaDevice, Error) + << "Failed to open media device at " + << devnode_ << ": " << strerror(-ret); return ret; } fd_ = ret; @@ -156,8 +159,9 @@ int MediaDevice::open() ret = ioctl(fd_, MEDIA_IOC_DEVICE_INFO, &info); if (ret) { ret = -errno; - LOG(Error) << "Failed to get media device info " - << ": " << strerror(-ret); + LOG(MediaDevice, Error) + << "Failed to get media device info " + << ": " << strerror(-ret); return ret; } @@ -227,8 +231,9 @@ int MediaDevice::populate() ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology); if (ret < 0) { ret = -errno; - LOG(Error) << "Failed to enumerate topology: " - << strerror(-ret); + LOG(MediaDevice, Error) + << "Failed to enumerate topology: " + << strerror(-ret); return ret; } @@ -445,8 +450,9 @@ bool MediaDevice::addObject(MediaObject *object) { if (objects_.find(object->id()) != objects_.end()) { - LOG(Error) << "Element with id " << object->id() - << " already enumerated."; + LOG(MediaDevice, Error) + << "Element with id " << object->id() + << " already enumerated."; return false; } @@ -568,8 +574,9 @@ bool MediaDevice::populatePads(const struct media_v2_topology &topology) MediaEntity *mediaEntity = dynamic_cast (object(entity_id)); if (!mediaEntity) { - LOG(Error) << "Failed to find entity with id: " - << entity_id; + LOG(MediaDevice, Error) + << "Failed to find entity with id: " + << entity_id; return false; } @@ -604,8 +611,9 @@ bool MediaDevice::populateLinks(const struct media_v2_topology &topology) MediaPad *source = dynamic_cast (object(source_id)); if (!source) { - LOG(Error) << "Failed to find pad with id: " - << source_id; + LOG(MediaDevice, Error) + << "Failed to find pad with id: " + << source_id; return false; } @@ -613,8 +621,9 @@ bool MediaDevice::populateLinks(const struct media_v2_topology &topology) MediaPad *sink = dynamic_cast (object(sink_id)); if (!sink) { - LOG(Error) << "Failed to find pad with id: " - << sink_id; + LOG(MediaDevice, Error) + << "Failed to find pad with id: " + << sink_id; return false; } @@ -665,14 +674,17 @@ int MediaDevice::setupLink(const MediaLink *link, unsigned int flags) int ret = ioctl(fd_, MEDIA_IOC_SETUP_LINK, &linkDesc); if (ret) { ret = -errno; - LOG(Error) << "Failed to setup link: " << strerror(-ret); + LOG(MediaDevice, Error) + << "Failed to setup link: " + << strerror(-ret); return ret; } - LOG(Debug) << source->entity()->name() << "[" - << source->index() << "] -> " - << sink->entity()->name() << "[" - << sink->index() << "]: " << flags; + LOG(MediaDevice, Debug) + << source->entity()->name() << "[" + << source->index() << "] -> " + << sink->entity()->name() << "[" + << sink->index() << "]: " << flags; return 0; } diff --git a/src/libcamera/media_object.cpp b/src/libcamera/media_object.cpp index 7d075384a996..a8d92e5f3664 100644 --- a/src/libcamera/media_object.cpp +++ b/src/libcamera/media_object.cpp @@ -39,6 +39,8 @@ namespace libcamera { +LOG_DECLARE_CATEGORY(MediaDevice) + /** * \class MediaObject * \brief Base class for all media objects @@ -334,8 +336,9 @@ int MediaEntity::setDeviceNode(const std::string &devnode) int ret = ::access(devnode.c_str(), R_OK | W_OK); if (ret < 0) { ret = -errno; - LOG(Error) << "Device node " << devnode << " can't be accessed: " - << strerror(-ret); + LOG(MediaDevice, Error) + << "Device node " << devnode << " can't be accessed: " + << strerror(-ret); return ret; } diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index f2e08a6a7315..c24feeafc503 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -24,6 +24,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(Pipeline) + /** * \class PipelineHandler * \brief Create and manage cameras based on a set of media devices @@ -120,7 +122,8 @@ void PipelineHandlerFactory::registerType(PipelineHandlerFactory *factory) factories.push_back(factory); - LOG(Debug) << "Registered pipeline handler \"" << factory->name() << "\""; + LOG(Pipeline, Debug) + << "Registered pipeline handler \"" << factory->name() << "\""; } /** diff --git a/src/libcamera/timer.cpp b/src/libcamera/timer.cpp index 306825fde3c0..e964a94bc48d 100644 --- a/src/libcamera/timer.cpp +++ b/src/libcamera/timer.cpp @@ -20,6 +20,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(Timer) + /** * \class Timer * \brief Single-shot timer interface @@ -54,8 +56,9 @@ void Timer::start(unsigned int msec) interval_ = msec; deadline_ = tp.tv_sec * 1000000000ULL + tp.tv_nsec + msec * 1000000ULL; - LOG(Debug) << "Starting timer " << this << " with interval " << msec - << ": deadline " << deadline_; + LOG(Timer, Debug) + << "Starting timer " << this << " with interval " + << msec << ": deadline " << deadline_; CameraManager::instance()->eventDispatcher()->registerTimer(this); } diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 54b53de37510..59c5ac984891 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -20,6 +20,8 @@ */ namespace libcamera { +LOG_DEFINE_CATEGORY(V4L2) + /** * \struct V4L2Capability * \brief struct v4l2_capability object wrapper and helpers @@ -106,15 +108,16 @@ int V4L2Device::open() int ret; if (isOpen()) { - LOG(Error) << "Device already open"; + LOG(V4L2, Error) << "Device already open"; return -EBUSY; } ret = ::open(devnode_.c_str(), O_RDWR); if (ret < 0) { ret = -errno; - LOG(Error) << "Failed to open V4L2 device '" << devnode_ - << "': " << strerror(-ret); + LOG(V4L2, Error) + << "Failed to open V4L2 device '" << devnode_ + << "': " << strerror(-ret); return ret; } fd_ = ret; @@ -122,22 +125,24 @@ int V4L2Device::open() ret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_); if (ret < 0) { ret = -errno; - LOG(Error) << "Failed to query device capabilities: " - << strerror(-ret); + LOG(V4L2, Error) + << "Failed to query device capabilities: " + << strerror(-ret); return ret; } - LOG(Debug) << "Opened '" << devnode_ << "' " - << caps_.bus_info() << ": " << caps_.driver() - << ": " << caps_.card(); + LOG(V4L2, Debug) + << "Opened '" << devnode_ << "' " + << caps_.bus_info() << ": " << caps_.driver() + << ": " << caps_.card(); if (!caps_.isCapture() && !caps_.isOutput()) { - LOG(Debug) << "Device is not a supported type"; + LOG(V4L2, Debug) << "Device is not a supported type"; return -EINVAL; } if (!caps_.hasStreaming()) { - LOG(Error) << "Device does not support streaming I/O"; + LOG(V4L2, Error) << "Device does not support streaming I/O"; return -EINVAL; }