[{"id":2286,"web_url":"https://patchwork.libcamera.org/comment/2286/","msgid":"<20190716074437.GA22355@pendragon.ideasonboard.com>","date":"2019-07-16T07:44:37","subject":"Re: [libcamera-devel] [PATCH v2 1/4] libcamera: logging: add syslog,\n\tstream, and nowhere logging targets","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Tue, Jul 16, 2019 at 04:05:05PM +0900, Paul Elder wrote:\n> Allow logging to syslog, or any given ostream, or to nowhere. The\n> logging API is updated to accomodate these new logging destinations.\n> LogMessage is modified to allow this.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> ---\n> Changes in v2:\n> - make LogMessage's message component getters return the data directly,\n>   and not as a string (eg. severity, category, timestamp)\n> - move LogMessage's message component formatting (converting to string)\n>   to Logger\n> - add LogOutput to abstract away log output destinations from the\n>   Logger, and to prevent anticipated concurrency issues (changing the\n>   log output between commencing and completing a previous write)\n> - upgrade documentation\n> - other minor changes\n> \n>  include/libcamera/logging.h |  13 +-\n>  src/libcamera/include/log.h |   8 +\n>  src/libcamera/log.cpp       | 458 +++++++++++++++++++++++++++++-------\n>  3 files changed, 397 insertions(+), 82 deletions(-)\n> \n> diff --git a/include/libcamera/logging.h b/include/libcamera/logging.h\n> index a8495cf..2b6dd3f 100644\n> --- a/include/libcamera/logging.h\n> +++ b/include/libcamera/logging.h\n> @@ -9,8 +9,17 @@\n>  \n>  namespace libcamera {\n>  \n> -void logSetFile(const char *file);\n> -int logSetLevel(const char *category, const char *level);\n> +enum LoggingTarget {\n> +\tLoggingTargetNone,\n> +\tLoggingTargetSyslog,\n> +\tLoggingTargetFile,\n> +\tLoggingTargetStream,\n> +};\n> +\n> +int logSetFile(const char *path);\n> +int logSetStream(std::ostream *stream);\n> +int logSetTarget(LoggingTarget target);\n> +void logSetLevel(const char *category, const char *level);\n>  \n>  } /* namespace libcamera */\n>  \n> diff --git a/src/libcamera/include/log.h b/src/libcamera/include/log.h\n> index 802836d..9b203f9 100644\n> --- a/src/libcamera/include/log.h\n> +++ b/src/libcamera/include/log.h\n> @@ -60,12 +60,20 @@ public:\n>  \n>  \tstd::ostream &stream() { return msgStream_; }\n>  \n> +\tconst struct timespec &timestamp() const { return timestamp_; }\n> +\tLogSeverity severity() const { return severity_; }\n> +\tconst LogCategory &category() const { return category_; }\n> +\tconst std::string &fileInfo() const { return fileInfo_; }\n> +\tconst std::string msg() const { return msgStream_.str(); }\n> +\n>  private:\n>  \tvoid init(const char *fileName, unsigned int line);\n>  \n>  \tstd::ostringstream msgStream_;\n>  \tconst LogCategory &category_;\n>  \tLogSeverity severity_;\n> +\tstruct timespec timestamp_;\n> +\tstd::string fileInfo_;\n>  };\n>  \n>  class Loggable\n> diff --git a/src/libcamera/log.cpp b/src/libcamera/log.cpp\n> index 709c669..3108ef3 100644\n> --- a/src/libcamera/log.cpp\n> +++ b/src/libcamera/log.cpp\n> @@ -15,8 +15,11 @@\n>  #include <iostream>\n>  #include <list>\n>  #include <string.h>\n> +#include <syslog.h>\n>  #include <unordered_set>\n>  \n> +#include <libcamera/logging.h>\n> +\n>  #include \"utils.h\"\n>  \n>  /**\n> @@ -48,8 +51,172 @@\n>   * to stderr.\n>   */\n>  \n> +/**\n> + * \\file logging.h\n> + * \\brief Logging management\n> + *\n> + * API to change the logging output destination and log levels programatically.\n> + */\n> +\n>  namespace libcamera {\n>  \n> +static int log_severity_to_syslog(LogSeverity severity)\n> +{\n> +\tswitch (severity) {\n> +\tcase LogDebug:\n> +\t\treturn LOG_DEBUG;\n> +\tcase LogInfo:\n> +\t\treturn LOG_INFO;\n> +\tcase LogWarning:\n> +\t\treturn LOG_WARNING;\n> +\tcase LogError:\n> +\t\treturn LOG_ERR;\n> +\tcase LogFatal:\n> +\t\treturn LOG_ALERT;\n> +\tdefault:\n> +\t\treturn LOG_NOTICE;\n> +\t}\n> +}\n> +\n> +static std::string log_timespec_to_string(const struct timespec &timestamp)\n> +{\n> +\tstd::ostringstream ossTimestamp;\n> +\tossTimestamp.fill('0');\n> +\tossTimestamp << \"[\" << 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> +\tossTimestamp.fill(' ');\n\nI think you can drop this line.\n\n> +\treturn ossTimestamp.str();\n> +}\n> +\n> +static const char *log_severity_name(LogSeverity severity)\n> +{\n> +\tstatic const char *const names[] = {\n> +\t\t\"  DBG\",\n> +\t\t\" INFO\",\n> +\t\t\" WARN\",\n> +\t\t\"  ERR\",\n> +\t\t\"FATAL\",\n> +\t};\n> +\n> +\tif (static_cast<unsigned int>(severity) < ARRAY_SIZE(names))\n> +\t\treturn names[severity];\n> +\telse\n> +\t\treturn \"UNKWN\";\n> +}\n> +\n> +/**\n> + * \\brief Log output\n> + *\n> + * The LogOutput class models a log output desination\n\ns/desination/destination/\n\n> + */\n> +class LogOutput\n> +{\n> +public:\n> +\tLogOutput(const char *path);\n> +\tLogOutput(std::ostream *stream);\n> +\tLogOutput();\n> +\t~LogOutput();\n> +\n> +\tbool good() const;\n> +\tvoid write(const LogMessage &msg);\n> +\n> +private:\n> +\tvoid writeSyslog(const LogMessage &msg);\n> +\tvoid writeStream(const LogMessage &msg);\n> +\n> +\tstd::ostream *stream_;\n> +\tLoggingTarget target_;\n> +};\n> +\n> +/**\n> + * \\brief Construct a log output based on a file\n> + * \\param[in] path Full path to log file\n> + */\n> +LogOutput::LogOutput(const char *path)\n> +\t: target_(LoggingTargetFile)\n> +{\n> +\tstream_ = new std::ofstream(path);\n> +}\n> +\n> +/**\n> + * \\brief Construct a log output based on a stream\n> + * \\param[in] stream Stream to send log output to\n> + */\n> +LogOutput::LogOutput(std::ostream *stream)\n> +\t: stream_(stream), target_(LoggingTargetStream)\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Construct a log output to syslog\n> + */\n> +LogOutput::LogOutput()\n> +\t: stream_(nullptr), target_(LoggingTargetSyslog)\n> +{\n> +\topenlog(\"libcamera\", LOG_PID, 0);\n> +}\n> +\n> +LogOutput::~LogOutput()\n> +{\n> +\tswitch (target_) {\n> +\tcase LoggingTargetFile:\n> +\t\tdelete stream_;\n> +\t\tbreak;\n> +\tcase LoggingTargetSyslog:\n> +\t\tcloselog();\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tbreak;\n> +\t}\n> +}\n> +\n> +/**\n> + * \\brief Check if log output is a valid stream\n\nMissing \\return, and the documentation doesn't really match the\nimplementation. I would document it as\n\n * \\brief Check if the log output is valid\n * \\return True if the log output is valid\n\n> + */\n> +bool LogOutput::good() const\n\nThen I think we can rename this to isValid()\n\n> +{\n> +\treturn stream_ && stream_->good();\n\nAnd implement it as\n\n\tswitch (target_) {\n\tcase LoggingTargetFile:\n\t\treturn stream_->good();\n\tcase LoggingTargetStream:\n\t\treturn stream_ != nulptr;\n\tdefault:\n\t\treturn true;\n\t}\n\n> +}\n> +\n> +/**\n> + * \\brief Write message to log output\n> + * \\param[in] msg Message to write\n> + */\n> +void LogOutput::write(const LogMessage &msg)\n> +{\n> +\tswitch (target_) {\n> +\tcase LoggingTargetSyslog:\n> +\t\twriteSyslog(msg);\n> +\t\tbreak;\n> +\tcase LoggingTargetStream:\n> +\tcase LoggingTargetFile:\n> +\t\twriteStream(msg);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tbreak;\n> +\t}\n> +}\n> +\n> +void LogOutput::writeSyslog(const LogMessage &msg)\n> +{\n> +\tstd::string str = std::string(log_severity_name(msg.severity())) + \" \" +\n> +\t      \t\t  msg.category().name() + \" \" + msg.fileInfo() + \" \" +\n> +\t\t\t  msg.msg();\n> +\tsyslog(log_severity_to_syslog(msg.severity()), \"%s\", str.c_str());\n> +}\n> +\n> +void LogOutput::writeStream(const LogMessage &msg)\n> +{\n> +\tstd::string str = std::string(log_timespec_to_string(msg.timestamp()) +\n> +\t      \t\t  log_severity_name(msg.severity()) + \" \" +\n> +\t\t\t  msg.category().name() + \" \" + msg.fileInfo() + \" \" +\n> +\t\t\t  msg.msg());\n> +\tstream_->write(str.c_str(), str.size());\n> +\tstream_->flush();\n> +}\n> +\n>  /**\n>   * \\brief Message logger\n>   *\n> @@ -60,7 +227,12 @@ class Logger\n>  public:\n>  \tstatic Logger *instance();\n>  \n> -\tvoid write(const std::string &msg);\n> +\tvoid write(const LogMessage &msg);\n> +\n> +\tint logSetFile(const char *path);\n> +\tint logSetStream(std::ostream *stream);\n> +\tint logSetTarget(LoggingTarget target);\n> +\tvoid logSetLevel(const char *category, const char *level);\n>  \n>  private:\n>  \tLogger();\n> @@ -69,52 +241,91 @@ private:\n>  \tvoid parseLogLevels();\n>  \tstatic LogSeverity parseLogLevel(const std::string &level);\n>  \n> -\tfriend int logSetFile(const char *file);\n> -\tfriend void logSetLevel(const char *category, const char *level);\n> -\n>  \tfriend LogCategory;\n>  \tvoid registerCategory(LogCategory *category);\n>  \tvoid unregisterCategory(LogCategory *category);\n>  \n> +\tvoid writeSyslog(const LogMessage &msg);\n> +\tvoid writeStream(const LogMessage &msg);\n> +\n>  \tstd::unordered_set<LogCategory *> categories_;\n>  \tstd::list<std::pair<std::string, LogSeverity>> levels_;\n>  \n> -\tstd::ofstream file_;\n> -\tstd::ostream *output_;\n> +\tstd::shared_ptr<LogOutput> output_;\n>  };\n>  \n>  /**\n> - * \\brief Set the log file\n> - * \\param[in] file Full path to the log file\n> + * \\enum LoggingTarget\n> + * \\brief Log destination type\n> + * \\var LoggingTargetNone\n> + * \\brief No logging destination\n> + * \\sa Logger::logSetTarget\n> + * \\var LoggingTargetSyslog\n> + * \\brief Log to syslog\n> + * \\sa Logger::logSetTarget\n> + * \\var LoggingTargetFile\n> + * \\brief Log to file\n> + * \\sa Logger::logSetFile\n> + * \\var LoggingTargetStream\n> + * \\brief Log to stream\n> + * \\sa Logger::logSetStream\n> + */\n> +\n> +/**\n> + * \\brief Direct logging to a file\n> + * \\param[in] path Full path to the log file\n>   *\n> - * This function sets the logging output file to \\a file. The previous log file,\n> - * if any, is closed, and all new log messages will be written to the new log\n> - * file.\n> + * This function directs the log output to the file identified by \\a path. The\n> + * previous log target, if any, is closed, and all new log messages will be\n> + * written to the new log file.\n>   *\n> - * If \\a file is a null pointer, the log is directed to stderr. If the\n> - * function returns an error, the log file is not changed.\n> + * If the function returns an error, the log target is not changed.\n>   *\n>   * \\return Zero on success, or a negative error code otherwise.\n\nWhile at it, s/\\.$// (and the same below)\n\n>   */\n> -int logSetFile(const char *file)\n> +int logSetFile(const char *path)\n>  {\n> -\tLogger *logger = Logger::instance();\n> -\n> -\tif (!file) {\n> -\t\tlogger->output_ = &std::cerr;\n> -\t\tlogger->file_.close();\n> -\t\treturn 0;\n> -\t}\n> +\treturn Logger::instance()->logSetFile(path);\n> +}\n>  \n> -\tstd::ofstream logFile(file);\n> -\tif (!logFile.good())\n> -\t\treturn -EINVAL;\n> +/**\n> + * \\brief Direct logging to a stream\n> + * \\param[in] stream Stream to send log output to\n> + *\n> + * This function directs the log output to \\a stream. The previous log target,\n> + * if any, is closed, and all new log messages will be written to the new log\n> + * stream.\n> + *\n> + * If the function returns an error, the log file is not changed.\n> + *\n> + * \\return Zero on success, or a negative error code otherwise.\n> + */\n> +int logSetStream(std::ostream *stream)\n> +{\n> +\treturn Logger::instance()->logSetStream(stream);\n> +}\n>  \n> -\tif (logger->output_ != &std::cerr)\n> -\t\tlogger->file_.close();\n> -\tlogger->file_ = std::move(logFile);\n> -\tlogger->output_ = &logger->file_;\n> -\treturn 0;\n> +/**\n> + * \\brief Set the logging target\n> + * \\param[in] target Logging destination\n> + *\n> + * This function sets the logging output to the target specified by \\a target.\n> + * The allowed values of \\a target are LoggingTargetNone and\n> + * LoggingTargetSyslog. LoggingTargetNone will send the log output to nowhere,\n> + * and LoggingTargetSyslog will send the log output to syslog. The previous\n> + * log target, if any, is closed, and all new log messages will be written to\n> + * the new log destination.\n> + *\n> + * LoggingTargetFile and LoggingTargetStream are not valid values for \\a target.\n> + * Use logSetFile() and logSetStream() instead, respectively.\n> + *\n> + * If the function returns an error, the log file is not changed.\n> + *\n> + * \\return Zero on success, or a negative error code otherwise.\n> + */\n> +int logSetTarget(LoggingTarget target)\n> +{\n> +\treturn Logger::instance()->logSetTarget(target);\n>  }\n>  \n>  /**\n> @@ -134,15 +345,7 @@ int logSetFile(const char *file)\n>   */\n>  void logSetLevel(const char *category, const char *level)\n>  {\n> -\tLogger *logger = Logger::instance();\n> -\n> -\tLogSeverity severity = Logger::parseLogLevel(level);\n> -\tif (severity == LogInvalid)\n> -\t\treturn;\n> -\n> -\tfor (LogCategory *c : logger->categories_)\n> -\t\tif (!strcmp(c->name(), category))\n> -\t\t\tc->setSeverity(severity);\n> +\tLogger::instance()->logSetLevel(category, level);\n>  }\n>  \n>  /**\n> @@ -161,19 +364,103 @@ Logger *Logger::instance()\n>  \n>  /**\n>   * \\brief Write a message to the configured logger output\n> - * \\param[in] msg The message string\n> + * \\param[in] msg The message object\n> + */\n> +void Logger::write(const LogMessage &msg)\n> +{\n> +\tstd::shared_ptr<LogOutput> output = std::atomic_load(&output_);\n> +\tif (!output)\n> +\t\treturn;\n> +\n> +\toutput->write(msg);\n> +}\n> +\n> +/**\n> + * \\brief Set the log file\n> + * \\param[in] path Full path to the log file\n> + *\n> + * \\sa libcamera::logSetFile()\n> + *\n> + * \\return Zero on success, or a negative error code otherwise.\n> + */\n> +int Logger::logSetFile(const char *path)\n> +{\n> +\tstd::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(path);\n> +\tif (!output->good())\n> +\t\treturn -EINVAL;\n> +\n> +\tstd::atomic_store(&output_, output);\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Set the log stream\n> + * \\param[in] stream Stream to send log output to\n> + *\n> + * \\sa libcamera::logSetStream()\n> + *\n> + * \\return Zero on success, or a negative error code otherwise.\n>   */\n> -void Logger::write(const std::string &msg)\n> +int Logger::logSetStream(std::ostream *stream)\n>  {\n> -\toutput_->write(msg.c_str(), msg.size());\n> -\toutput_->flush();\n> +\tstd::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(stream);\n> +\tstd::atomic_store(&output_, output);\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Set the log target\n> + * \\param[in] target Log destination\n> + *\n> + * \\sa libcamera::logSetTarget()\n> + *\n> + * \\return Zero on success, or a negative error code otherwise.\n> + */\n> +int Logger::logSetTarget(enum LoggingTarget target)\n> +{\n> +\tstd::shared_ptr<LogOutput> output;\n> +\n> +\tswitch (target) {\n> +\tcase LoggingTargetSyslog:\n> +\t\toutput = std::make_shared<LogOutput>();\n> +\t\tstd::atomic_store(&output_, output);\n> +\t\tbreak;\n> +\tcase LoggingTargetNone:\n> +\t\toutput = nullptr;\n> +\t\tstd::atomic_store(&output_, output);\n\nstd::atomic_store(&output_, std::shared_ptr<LogOutput>());\n\n> +\t\tbreak;\n> +\tdefault:\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Set the log level\n> + * \\param[in] category Logging category\n> + * \\param[in] level Log level\n> + *\n> + * \\sa libcamera::logSetLevel()\n> + */\n> +void Logger::logSetLevel(const char *category, const char *level)\n> +{\n> +\tLogSeverity severity = parseLogLevel(level);\n> +\tif (severity == LogInvalid)\n> +\t\treturn;\n> +\n> +\tfor (LogCategory *c : categories_) {\n> +\t\tif (!strcmp(c->name(), category)) {\n> +\t\t\tc->setSeverity(severity);\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n>  }\n>  \n>  /**\n>   * \\brief Construct a logger\n>   */\n>  Logger::Logger()\n> -\t: output_(&std::cerr)\n>  {\n>  \tparseLogFile();\n>  \tparseLogLevels();\n> @@ -183,18 +470,24 @@ Logger::Logger()\n>   * \\brief Parse the log output file from the environment\n>   *\n>   * If the LIBCAMERA_LOG_FILE environment variable is set, open the file it\n> - * points to and redirect the logger output to it. Errors are silently ignored\n> - * and don't affect the logger output (set to stderr).\n> + * points to and redirect the logger output to it. If environment variable is\n\ns/If environment/If the environment/\n\n> + * set to \"syslog\", then the logger output will be directed to syslog. Errors\n> + * are silently ignored and don't affect the logger output (set to stderr).\n>   */\n>  void Logger::parseLogFile()\n>  {\n>  \tconst char *file = utils::secure_getenv(\"LIBCAMERA_LOG_FILE\");\n> -\tif (!file)\n> +\tif (!file) {\n> +\t\tlogSetStream(&std::cerr);\n>  \t\treturn;\n> +\t}\n> +\n> +\tif (!strcmp(file, \"syslog\")) {\n> +\t\tlogSetTarget(LoggingTargetSyslog);\n> +\t\treturn;\n> +\t}\n>  \n> -\tfile_.open(file);\n> -\tif (file_.good())\n> -\t\toutput_ = &file_;\n> +\tlogSetFile(file);\n>  }\n>  \n>  /**\n> @@ -408,22 +701,6 @@ const LogCategory &LogCategory::defaultCategory()\n>  \treturn category;\n>  }\n>  \n> -static const char *log_severity_name(LogSeverity severity)\n> -{\n> -\tstatic const char *const names[] = {\n> -\t\t\"  DBG\",\n> -\t\t\" INFO\",\n> -\t\t\" WARN\",\n> -\t\t\"  ERR\",\n> -\t\t\"FATAL\",\n> -\t};\n> -\n> -\tif (static_cast<unsigned int>(severity) < ARRAY_SIZE(names))\n> -\t\treturn names[severity];\n> -\telse\n> -\t\treturn \"UNKWN\";\n> -}\n> -\n>  /**\n>   * \\class LogMessage\n>   * \\brief Internal log message representation.\n> @@ -493,18 +770,11 @@ LogMessage::LogMessage(LogMessage &&other)\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> +\tclock_gettime(CLOCK_MONOTONIC, &timestamp_);\n>  \n> -\tmsgStream_ << \" \" << log_severity_name(severity_);\n> -\tmsgStream_ << \" \" << category_.name();\n> -\tmsgStream_ << \" \" << utils::basename(fileName) << \":\" << line << \" \";\n> +\tstd::ostringstream ossFileInfo;\n> +\tossFileInfo << utils::basename(fileName) << \":\" << line;\n> +\tfileInfo_ = ossFileInfo.str();\n>  }\n>  \n>  LogMessage::~LogMessage()\n> @@ -515,10 +785,8 @@ LogMessage::~LogMessage()\n>  \n>  \tmsgStream_ << std::endl;\n>  \n> -\tif (severity_ >= category_.severity()) {\n> -\t\tstd::string msg(msgStream_.str());\n> -\t\tLogger::instance()->write(msg);\n> -\t}\n> +\tif (severity_ >= category_.severity())\n> +\t\tLogger::instance()->write(*this);\n>  \n>  \tif (severity_ == LogSeverity::LogFatal)\n>  \t\tstd::abort();\n> @@ -534,6 +802,36 @@ LogMessage::~LogMessage()\n>   * \\return A reference to the log message stream\n>   */\n>  \n> +/**\n> + * \\fn LogMessage::timestamp()\n> + * \\brief Retrieve the timestamp of the log message\n> + * \\return The timestamp of the message\n> + */\n> +\n> +/**\n> + * \\fn LogMessage::severity()\n> + * \\brief Retrieve the severity of the log message\n> + * \\return The severity of the message\n> + */\n> +\n> +/**\n> + * \\fn LogMessage::category()\n> + * \\brief Retrieve the category of the log message\n> + * \\return The category of the message\n> + */\n> +\n> +/**\n> + * \\fn LogMessage::fileInfo()\n> + * \\brief Retrieve the file info of the log message\n> + * \\return The file info of the message\n> + */\n> +\n> +/**\n> + * \\fn LogMessage::msg()\n> + * \\brief Getter for the message text of the log message\n\ns/Getter for/Retrieve/\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> + * \\return The message text of the message, as a string\n> + */\n> +\n>  /**\n>   * \\class Loggable\n>   * \\brief Base class to support log message extensions","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 69DB561AD3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 16 Jul 2019 09:45:07 +0200 (CEST)","from pendragon.ideasonboard.com (unknown\n\t[IPv6:2a00:79e1:abc:3602:59ec:6c:1869:337])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id EB84C564;\n\tTue, 16 Jul 2019 09:45:05 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1563263107;\n\tbh=TPtpUms4DgCS5eEqq7o4R3Tj0Qq8bgllgnlX8zRcE7U=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=PO3I+dFnw3VPZOREHIQEzaEQTpkzeHWmwItw8O8DkYpTLPW990ahTA+Xg0M4xvPcE\n\tze2rK5HRQlUZbtwq+df/UFtxxzzIZCbdvZ9Z+psJgdP4gXXgpC2wkEH8wHX+iaiHyF\n\tBsVnocKTBHqghkHpX+1bTfD2AvOhWC903gg3T61s=","Date":"Tue, 16 Jul 2019 10:44:37 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190716074437.GA22355@pendragon.ideasonboard.com>","References":"<20190716070508.24589-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20190716070508.24589-1-paul.elder@ideasonboard.com>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v2 1/4] libcamera: logging: add syslog,\n\tstream, and nowhere logging targets","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, 16 Jul 2019 07:45:07 -0000"}}]