diff options
Diffstat (limited to 'src/cam')
0 files changed, 0 insertions, 0 deletions
![]() |
index : libcamera/libcamera.git | |
libcamera official repository | git repository hosting on libcamera.org |
summaryrefslogtreecommitdiff |
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2018, Google Inc.
*
* log.cpp - Logging infrastructure
*/
#include "libcamera/internal/log.h"
#if HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include <fstream>
#include <iostream>
#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unordered_set>
#include <libcamera/logging.h>
#include "libcamera/internal/thread.h"
#include "libcamera/internal/utils.h"
/**
* \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.
*
* 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.
*/
/**
* \file logging.h
* \brief Logging management
*
* API to change the logging output destination and log levels programatically.
*/
namespace libcamera {
static int log_severity_to_syslog(LogSeverity severity)
{
switch (severity) {
case LogDebug:
return LOG_DEBUG;
case LogInfo:
return LOG_INFO;
case LogWarning:
return LOG_WARNING;
case LogError:
return LOG_ERR;
case LogFatal:
return LOG_ALERT;
default:
return LOG_NOTICE;
}
}
static const char *log_severity_name(LogSeverity severity)
{
static const char *const names[] = {
"DEBUG",
" INFO",
" WARN",
"ERROR",
"FATAL",
};
if (static_cast<unsigned int>(severity) < ARRAY_SIZE(names))
return names[severity];
else
return "UNKWN";
}
/**
* \brief Log output
*
* The LogOutput class models a log output destination
*/
class LogOutput
{
public:
LogOutput(const char *path);
LogOutput(std::ostream *stream);
LogOutput();
~LogOutput();
bool isValid() const;
void write(const LogMessage &msg);
void write(const std::string &msg);
private:
void writeSyslog(LogSeverity severity, const std::string &msg);
void writeStream(const std::string &msg);
std::ostream *stream_;
LoggingTarget target_;
};
/**
* \brief Construct a log output based on a file
* \param[in] path Full path to log file
*/
LogOutput::LogOutput(const char *path)
: target_(LoggingTargetFile)
{
stream_ = new std::ofstream(path);
}
/**
* \brief Construct a log output based on a stream
* \param[in] stream Stream to send log output to
*/
LogOutput::LogOutput(std::ostream *stream)
: stream_(stream), target_(LoggingTargetStream)
{
}
/**
* \brief Construct a log output to syslog
*/
LogOutput::LogOutput()
: stream_(nullptr), target_(LoggingTargetSyslog)
{
openlog("libcamera", LOG_PID, 0);
}
LogOutput::~LogOutput()
{
switch (target_) {
case LoggingTargetFile:
delete stream_;
break;
case LoggingTargetSyslog:
closelog();
break;
default:
break;
}
}
/**
* \brief Check if the log output is valid
* \return True if the log output is valid
*/
bool LogOutput::isValid() const
{
switch (target_) {
case LoggingTargetFile:
return stream_->good();
case LoggingTargetStream:
return stream_ != nullptr;
default:
return true;
}
}
/**
* \brief Write message to log output
* \param[in] msg Message to write
*/
void LogOutput::write(const LogMessage &msg)
{
std::string str;
switch (target_) {
case LoggingTargetSyslog:
str = std::string(log_severity_name(msg.severity())) + " "
+ msg.category().name() + " " + msg.fileInfo() + " "
+ msg.msg();
writeSyslog(msg.severity(), str);
break;
case LoggingTargetStream:
case LoggingTargetFile:
str = "[" + utils::time_point_to_string(msg.timestamp()) + "] ["
+ std::to_string(Thread::currentId()) + "] "
+ log_severity_name(msg.severity()) + " "
+ msg.category().name() + " " + msg.fileInfo() + " "
+ msg.msg();
writeStream(str);
break;
default:
break;
}
}
/**
* \brief Write string to log output
* \param[in] str String to write
*/
void LogOutput::write(const std::string &str)
{
switch (target_) {
case LoggingTargetSyslog:
writeSyslog(LogDebug, str);
break;
case LoggingTargetStream:
case LoggingTargetFile:
writeStream(str);
break;
default:
break;
}
}
void LogOutput::writeSyslog(LogSeverity severity, const std::string &str)
{
syslog(log_severity_to_syslog(severity), "%s", str.c_str());
}
void LogOutput::writeStream(const std::string &str)
{
stream_->write(str.c_str(), str.size());
stream_->flush();
}
/**
* \brief Message logger
*
* The Logger class handles log configuration.
*/
class Logger
{
public:
static Logger *instance();
void write(const LogMessage &msg);
void backtrace();
int logSetFile(const char *path);
int logSetStream(std::ostream *stream);
int logSetTarget(LoggingTarget target);
void logSetLevel(const char *category, const char *level);
private:
Logger();
void parseLogFile();
void parseLogLevels();
static LogSeverity parseLogLevel(const std::string &level);
friend LogCategory;
void registerCategory(LogCategory *category);
void unregisterCategory(LogCategory *category);
std::unordered_set<LogCategory *> categories_;
std::list<std::pair<std::string, LogSeverity>> levels_;
std::shared_ptr<LogOutput> output_;
};
/**
* \enum LoggingTarget
* \brief Log destination type
* \var LoggingTargetNone
* \brief No logging destination
* \sa Logger::logSetTarget
* \var LoggingTargetSyslog
* \brief Log to syslog
* \sa Logger::logSetTarget
* \var LoggingTargetFile
* \brief Log to file
* \sa Logger::logSetFile
* \var LoggingTargetStream
* \brief Log to stream
* \sa Logger::logSetStream
*/
/**
* \brief Direct logging to a file
* \param[in] path Full path to the log file
*
* This function directs the log output to the file identified by \a path. The
* previous log target, if any, is closed, and all new log messages will be
* written to the new log file.
*
* If the function returns an error, the log target is not changed.
*
* \return Zero on success, or a negative error code otherwise
*/
int logSetFile(const char *path)
{
return Logger::instance()->logSetFile(path);
}
/**
* \brief Direct logging to a stream
* \param[in] stream Stream to send log output to
*
* This function directs the log output to \a stream. The previous log target,
* if any, is closed, and all new log messages will be written to the new log
* stream.
*
* If the function returns an error, the log file is not changed
*
* \return Zero on success, or a negative error code otherwise.
*/
int logSetStream(std::ostream *stream)
{
return Logger::instance()->logSetStream(stream);
}
/**
* \brief Set the logging target
* \param[in] target Logging destination
*
* This function sets the logging output to the target specified by \a target.
* The allowed values of \a target are LoggingTargetNone and
* LoggingTargetSyslog. LoggingTargetNone will send the log output to nowhere,
* and LoggingTargetSyslog will send the log output to syslog. The previous
* log target, if any, is closed, and all new log messages will be written to
* the new log destination.
*
* LoggingTargetFile and LoggingTargetStream are not valid values for \a target.
* Use logSetFile() and logSetStream() instead, respectively.
*
* If the function returns an error, the log file is not changed.
*
* \return Zero on success, or a negative error code otherwise.
*/
int logSetTarget(LoggingTarget target)
{
return Logger::instance()->logSetTarget(target);
}
/**
* \brief Set the log level
* \param[in] category Logging category
* \param[in] level Log level
*
* This function sets the log level of \a category to \a level.
* \a level shall be one of the following strings:
* - "DEBUG"
* - "INFO"
* - "WARN"
* - "ERROR"
* - "FATAL"
*
* "*" is not a valid \a category for this function.
*/
void logSetLevel(const char *category, const char *level)
{
Logger::instance()->logSetLevel(category, level);
}
/**
* \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 Write a message to the configured logger output
* \param[in] msg The message object
*/
void Logger::write(const LogMessage &msg)
{
std::shared_ptr<LogOutput> output = std::atomic_load(&output_);
if (!output)
return;
output->write(msg);
}
/**
* \brief Write a backtrace to the log
*/
void Logger::backtrace()
{
#if HAVE_BACKTRACE
std::shared_ptr<LogOutput> output = std::atomic_load(&output_);
if (!output)
return;
void *buffer[32];
int num_entries = ::backtrace(buffer, ARRAY_SIZE(buffer));
char **strings = backtrace_symbols(buffer, num_entries);
if (!strings)
return;
std::ostringstream msg;
msg << "Backtrace:" << std::endl;
/*
* Skip the first two entries that correspond to this method and
* ~LogMessage().
*/
for (int i = 2; i < num_entries; ++i)
msg << strings[i] << std::endl;
output->write(msg.str());
free(strings);
#endif
}
/**
* \brief Set the log file
* \param[in] path Full path to the log file
*
* \sa libcamera::logSetFile()
*
* \return Zero on success, or a negative error code otherwise.
*/
int Logger::logSetFile(const char *path)
{
std::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(path);
if (!output->isValid())
return -EINVAL;
std::atomic_store(&output_, output);
return 0;
}
/**
* \brief Set the log stream
* \param[in] stream Stream to send log output to
*
* \sa libcamera::logSetStream()
*
* \return Zero on success, or a negative error code otherwise.
*/
int Logger::logSetStream(std::ostream *stream)
{
std::shared_ptr<LogOutput> output = std::make_shared<LogOutput>(stream);
std::atomic_store(&output_, output);
return 0;
}
/**
* \brief Set the log target
* \param[in] target Log destination
*
* \sa libcamera::logSetTarget()
*
* \return Zero on success, or a negative error code otherwise.
*/
int Logger::logSetTarget(enum LoggingTarget target)
{
std::shared_ptr<LogOutput> output;
switch (target) {
case LoggingTargetSyslog:
output = std::make_shared<LogOutput>();
std::atomic_store(&output_, output);
break;
case LoggingTargetNone:
output = nullptr;
std::atomic_store(&output_, std::shared_ptr<LogOutput>());
break;
default:
return -EINVAL;
}
return 0;
}
/**
* \brief Set the log level
* \param[in] category Logging category
* \param[in] level Log level
*
* \sa libcamera::logSetLevel()
*/
void Logger::logSetLevel(const char *category, const char *level)
{
LogSeverity severity = parseLogLevel(level);
if (severity == LogInvalid)
return;
for (LogCategory *c : categories_) {
if (!strcmp(c->name(), category)) {
c->setSeverity(severity);
break;
}
}
}
/**
* \brief Construct a logger
*/
Logger::Logger()
{
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. If the environment variable
* is set to "syslog", then the logger output will be directed to syslog. Errors
* are silently ignored and don't affect the logger output (set to stderr).
*/
void Logger::parseLogFile()
{
const char *file = utils::secure_getenv("LIBCAMERA_LOG_FILE");
if (!file) {
logSetStream(&std::cerr);
return;
}
if (!strcmp(file, "syslog")) {
logSetTarget(LoggingTargetSyslog);
return;
}
logSetFile(file);
}
/**
* \brief Parse the log levels from the environment
*
* The log levels are stored in the LIBCAMERA_LOG_LEVELS environment variable