summaryrefslogtreecommitdiffstats
path: root/src/lib/log/logger.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/log/logger.h')
-rw-r--r--src/lib/log/logger.h369
1 files changed, 369 insertions, 0 deletions
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
new file mode 100644
index 0000000..78a82ff
--- /dev/null
+++ b/src/lib/log/logger.h
@@ -0,0 +1,369 @@
+// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <atomic>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+#include <string>
+
+#include <exceptions/exceptions.h>
+#include <log/logger_level.h>
+#include <log/message_types.h>
+#include <log/log_formatter.h>
+#include <log/output_option.h>
+
+namespace isc {
+namespace log {
+
+namespace interprocess {
+// Forward declaration to hide implementation details from normal
+// applications.
+class InterprocessSync;
+}
+
+/// \page LoggingApi Logging API
+/// \section LoggingApiOverview Overview
+/// Kea logging uses the concepts of the widely-used Java logging
+/// package log4j (https://logging.apache.org/log4j/), albeit implemented
+/// in C++ using an open-source port. Features of the system are:
+///
+/// - Within the code objects - known as loggers - can be created and
+/// used to log messages. These loggers have names; those with the
+/// same name share characteristics (such as output destination).
+/// - Loggers have a hierarchical relationship, with each logger being
+/// the child of another logger, except for the top of the hierarchy, the
+/// root logger. If a logger does not log a message, it is passed to the
+/// parent logger.
+/// - Messages can be logged at severity levels of FATAL, ERROR, WARN, INFO
+/// or DEBUG. The DEBUG level has further sub-levels numbered 0 (least
+/// informative) to 99 (most informative).
+/// - Each logger has a severity level set associated with it. When a
+/// message is logged, it is output only if it is logged at a level equal
+/// to the logger severity level or greater, e.g. if the logger's severity
+/// is WARN, only messages logged at WARN, ERROR or FATAL will be output.
+///
+/// \section LoggingApiLoggerNames Kea Logger Names
+/// Within Kea, the root logger root logger is given the name of the
+/// program (via the stand-alone function setRootLoggerName()). Other loggers
+/// are children of the root logger and are named "<program>.<sublogger>".
+/// This name appears in logging output, allowing users to identify both
+/// the Kea program and the component within the program that generated
+/// the message.
+///
+/// When creating a logger, the abbreviated name "<sublogger>" can be used;
+/// the program name will be prepended to it when the logger is created.
+/// In this way, individual libraries can have their own loggers without
+/// worrying about the program in which they are used, but:
+/// - The origin of the message will be clearly identified.
+/// - The same component can have different options (e.g. logging severity)
+/// in different programs at the same time.
+///
+/// \section LoggingApiLoggingMessages Logging Messages
+/// Instead of embedding the text of messages within the code, each message
+/// is referred to using a symbolic name. The logging code uses this name as
+/// a key in a dictionary from which the message text is obtained. Such a
+/// system allows for the optional replacement of message text at run time.
+/// More details about the message dictionary (and the compiler used to create
+/// the symbol definitions) can be found in other modules in the src/lib/log
+/// directory.
+///
+/// \section LoggingApiImplementationIssues Implementation Issues
+/// Owing to the way that the logging is implemented, notably that loggers can
+/// be declared as static external objects, there is a restriction on the
+/// length of the name of a logger component (i.e. the length of
+/// the string passed to the Logger constructor) to a maximum of 31 characters.
+/// There is no reason for this particular value other than limiting the amount
+/// of memory used. It is defined by the constant Logger::MAX_LOGGER_NAME_SIZE,
+/// and can be made larger (or smaller) if so desired.
+
+class LoggerImpl; // Forward declaration of the implementation class
+
+/// \brief Bad Interprocess Sync
+///
+/// Exception thrown if a bad InterprocessSync object (such as null) is
+/// used.
+class BadInterprocessSync : public isc::Exception {
+public:
+ BadInterprocessSync(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+/// \brief Logger Name Error
+///
+/// Exception thrown if a logger name is too short or too long.
+class LoggerNameError : public isc::Exception {
+public:
+ LoggerNameError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+/// \brief Logger Name is null
+///
+/// Exception thrown if a logger name is null
+class LoggerNameNull : public isc::Exception {
+public:
+ LoggerNameNull(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+/// \brief Logging Not Initialized
+///
+/// Exception thrown if an attempt is made to access a logging function
+/// if the logging system has not been initialized.
+class LoggingNotInitialized : public isc::Exception {
+public:
+ LoggingNotInitialized(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+/// \brief Logger Class
+///
+/// This class is the main class used for logging. Use comprises:
+///
+/// 1. Constructing a logger by instantiating it with a specific name. (If the
+/// same logger is in multiple functions within a file, overhead can be
+/// minimized by declaring it as a file-wide static variable.)
+/// 2. Using the error(), info() etc. methods to log an error. (However, it is
+/// recommended to use the LOG_ERROR, LOG_INFO etc. macros defined in macros.h.
+/// These will avoid the potentially-expensive evaluation of arguments if the
+/// severity is such that the message will be suppressed.)
+
+class Logger {
+public:
+ /// Maximum size of a logger name
+ static const size_t MAX_LOGGER_NAME_SIZE = 31;
+
+ /// \brief Constructor
+ ///
+ /// Creates/attaches to a logger of a specific name.
+ ///
+ /// \param name Name of the logger. If the name is that of the root name,
+ /// this creates an instance of the root logger; otherwise it creates a
+ /// child of the root logger.
+ ///
+ /// \note The name of the logger may be no longer than MAX_LOGGER_NAME_SIZE
+ /// else the program will throw an exception. This restriction allows
+ /// loggers to be declared statically: the name is stored in a fixed-size
+ /// array to avoid the need to allocate heap storage during program
+ /// initialization (which causes problems on some operating systems).
+ ///
+ /// \note Note also that there is no constructor taking a std::string. This
+ /// minimizes the possibility of initializing a static logger with a
+ /// string, so leading to problems mentioned above.
+ Logger(const char* name) : loggerptr_(0), initialized_(false) {
+
+ // Validate the name of the logger.
+ if (name) {
+ // Name not null, is it too short or too long?
+ size_t namelen = std::strlen(name);
+ if ((namelen == 0) || (namelen > MAX_LOGGER_NAME_SIZE)) {
+ isc_throw(LoggerNameError, "'" << name << "' is not a valid "
+ << "name for a logger: valid names must be between 1 "
+ << "and " << MAX_LOGGER_NAME_SIZE << " characters in "
+ << "length");
+ }
+ } else {
+ isc_throw(LoggerNameNull, "logger names may not be null");
+ }
+
+ // Do the copy, ensuring a trailing null in all cases.
+ std::strncpy(name_, name, MAX_LOGGER_NAME_SIZE);
+ name_[MAX_LOGGER_NAME_SIZE] = '\0';
+ }
+
+ /// \brief Destructor
+ virtual ~Logger();
+
+ /// \brief Version
+ static std::string getVersion();
+
+ /// \brief The formatter used to replace placeholders
+ typedef isc::log::Formatter<Logger> Formatter;
+
+ /// \brief Get Name of Logger
+ ///
+ /// \return The full name of the logger (including the root name)
+ virtual std::string getName();
+
+ /// \brief Set Severity Level for Logger
+ ///
+ /// Sets the level at which this logger will log messages. If none is set,
+ /// the level is inherited from the parent.
+ ///
+ /// \param severity Severity level to log
+ /// \param dbglevel If the severity is DEBUG, this is the debug level.
+ /// This can be in the range 1 to 100 and controls the verbosity. A value
+ /// outside these limits is silently coerced to the nearest boundary.
+ virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
+
+ /// \brief Get Severity Level for Logger
+ ///
+ /// \return The current logging level of this logger. In most cases though,
+ /// the effective logging level is what is required.
+ virtual isc::log::Severity getSeverity();
+
+ /// \brief Get Effective Severity Level for Logger
+ ///
+ /// \return The effective severity level of the logger. This is the same
+ /// as getSeverity() if the logger has a severity level set, but otherwise
+ /// is the severity of the parent.
+ virtual isc::log::Severity getEffectiveSeverity();
+
+ /// \brief Return DEBUG Level
+ ///
+ /// \return Current setting of debug level. This is returned regardless of
+ /// whether the severity is set to debug.
+ virtual int getDebugLevel();
+
+ /// \brief Get Effective Debug Level for Logger
+ ///
+ /// \return The effective debug level of the logger. This is the same
+ /// as getDebugLevel() if the logger has a debug level set, but otherwise
+ /// is the debug level of the parent.
+ virtual int getEffectiveDebugLevel();
+
+ /// \brief Returns if Debug Message Should Be Output
+ ///
+ /// \param dbglevel Level for which debugging is checked. Debugging is
+ /// enabled only if the logger has DEBUG enabled and if the dbglevel
+ /// checked is less than or equal to the debug level set for the logger.
+ virtual bool isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
+
+ /// \brief Is INFO Enabled?
+ virtual bool isInfoEnabled();
+
+ /// \brief Is WARNING Enabled?
+ virtual bool isWarnEnabled();
+
+ /// \brief Is ERROR Enabled?
+ virtual bool isErrorEnabled();
+
+ /// \brief Is FATAL Enabled?
+ virtual bool isFatalEnabled();
+
+ /// \brief Output Debug Message
+ ///
+ /// \param dbglevel Debug level, ranging between 0 and 99. Higher numbers
+ /// are used for more verbose output.
+ /// \param ident Message identification.
+ Formatter debug(int dbglevel, const MessageID& ident);
+
+ /// \brief Output Informational Message
+ ///
+ /// \param ident Message identification.
+ Formatter info(const MessageID& ident);
+
+ /// \brief Output Warning Message
+ ///
+ /// \param ident Message identification.
+ Formatter warn(const MessageID& ident);
+
+ /// \brief Output Error Message
+ ///
+ /// \param ident Message identification.
+ Formatter error(const MessageID& ident);
+
+ /// \brief Output Fatal Message
+ ///
+ /// \param ident Message identification.
+ Formatter fatal(const MessageID& ident);
+
+ /// \brief Replace the interprocess synchronization object
+ ///
+ /// If this method is called with null as the argument, it throws a
+ /// BadInterprocessSync exception.
+ ///
+ /// \note This method is intended to be used only within this log library
+ /// and its tests. Normal application shouldn't use it (in fact,
+ /// normal application shouldn't even be able to instantiate
+ /// InterprocessSync objects).
+ ///
+ /// \param sync The logger uses this synchronization object for
+ /// synchronizing output of log messages. It should be deletable and
+ /// the ownership is transferred to the logger. If null is passed,
+ /// a BadInterprocessSync exception is thrown.
+ void setInterprocessSync(isc::log::interprocess::InterprocessSync* sync);
+
+ /// @brief Check if this logger has an appender of the given type.
+ ///
+ /// @param destination the appender type to be checked: console, file or syslog
+ ///
+ /// @return true if an appender of the given type is found, false otherwise
+ bool hasAppender(OutputOption::Destination const destination);
+
+ /// \brief Equality
+ ///
+ /// Check if two instances of this logger refer to the same stream.
+ ///
+ /// \return true if the logger objects are instances of the same logger.
+ bool operator==(Logger& other);
+
+private:
+ friend class isc::log::Formatter<Logger>;
+
+ /// \brief Raw output function
+ ///
+ /// This is used by the formatter to output formatted output.
+ ///
+ /// \param severity Severity of the message being output.
+ /// \param message Text of the message to be output.
+ void output(const Severity& severity, const std::string& message);
+
+ /// \brief Copy Constructor
+ ///
+ /// Disabled (marked private) as it makes no sense to copy the logger -
+ /// just create another one of the same name.
+ Logger(const Logger&);
+
+ /// \brief Assignment Operator
+ ///
+ /// Disabled (marked private) as it makes no sense to copy the logger -
+ /// just create another one of the same name.
+ Logger& operator=(const Logger&);
+
+ /// \brief Initialize Implementation
+ ///
+ /// Returns the logger pointer. If not yet set, the implementation class is
+ /// initialized.
+ ///
+ /// The main reason for this is to allow loggers to be declared statically
+ /// before the underlying logging system is initialized. However, any
+ /// attempt to access a logging method on any logger before initialization -
+ /// regardless of whether is is statically or automatically declared - will
+ /// cause a "LoggingNotInitialized" exception to be thrown.
+ ///
+ /// \return Returns pointer to implementation
+ LoggerImpl* getLoggerPtr();
+
+ /// \brief Initialize Underlying Implementation and Set loggerptr_
+ void initLoggerImpl();
+
+ ///< Pointer to underlying logger
+ LoggerImpl* loggerptr_;
+
+ ///< Copy of the logger name
+ char name_[MAX_LOGGER_NAME_SIZE + 1];
+
+ ///< Mutex to protect the internal state
+ std::mutex mutex_;
+
+ ///< Flag which indicates if logger is initialized
+ std::atomic<bool> initialized_;
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // LOGGER_H