/******************************************************************************
*
* This file is part of Log4Qt library.
*
* Copyright (C) 2007 - 2020 Log4Qt contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#ifndef LOG4QT_H
#define LOG4QT_H
#include "log4qtshared.h"
/*!
* \page Log4Qt/Log4j
*
* %Log4Qt is a C++ port of the Apache Software Foundation Log4j package
* using the Qt Framework.
*
* The documentation describes classes and methods that have been added or
* changed compared to Log4j.
*
* The following sections are describing the implementation in more detail:
* - \ref Changes "Differences to Log4j"
* - \ref Ownership "Object ownership"
* - \ref LogLog "Logging within the package"
* - \ref Init "Initialization procedure"
* - \ref Env "Environment Variables"
* - \ref Undocumented "Undocumented functions"
* - \ref Assumptions "Assumptions"
*
*/
/*!
* \page Changes Differences to Log4j
*
* The following fundamental differences exist between %Log4Qt and Log4j:
*
* - As a JAVA package Log4j does not have to manage object ownership and
* lifetime in the same way then it is required in C++. For details on
* how object ownership is handled see \ref Ownership "Object ownership".
* - The package uses itself for its internal logging similar to Log4j 1.3.
* For details see \ref LogLog "Logging within the package".
* - The configuration using system properties was replaced with a combination
* of environment variables and application settings. For details see
* \ref Env "Environment Variables".
* - Custom levels are not supported.
* - Multiple Logger Repositories are not supported
*
* The following classes have been changed:
*
* - \ref Log4Qt::AppenderSkeleton "AppenderSkeleton"
* - The procedure of checking, if logging is possible, originally used by
* \ref Log4Qt::WriterAppender "WriterAppender" was generalised and is used
* in \ref Log4Qt::AppenderSkeleton "AppenderSkeleton" and derived classes
* (\ref Log4Qt::AppenderSkeleton::checkEntryConditions() "checkEntryConditions()").
* - The \ref Log4Qt::AppenderSkeleton::doAppend() "doAppend()" member function will
* check the entry conditions by calling the sub-class specific
* \ref Log4Qt::AppenderSkeleton::checkEntryConditions() "checkEntryConditions()".
* If successful the sub-class specific
* \ref Log4Qt::AppenderSkeleton::append() "append()" function is called.
*
* - Configurator
* - Configure functions return a boolean indicating, if the configuration
* was successful.
* - Configure errors are accessible over
* \ref Log4Qt::ConfiguratorHelper::configureError()
* "ConfiguratorHelper::configureError()".
* - Watching for configuration file changes is a function performed
* centrally by the \ref Log4Qt::ConfiguratorHelper "ConfiguratorHelper".
* The class provides Q_SIGNALS to notify on configuration change and errors.
* - The class \ref Log4Qt::PropertyConfigurator "PropertyConfigurator" was
* extended to be able to read configuration data from a QSettings object.
*
* - \ref Log4Qt::Level "Level"
* - A new value \ref Log4Qt::Level::NULL_INT "Level::NULL_INT" was
* introduced to indicate there is no level set.
*
* - \ref Log4Qt::Logger "Logger"
* - The method \ref Log4Qt::Logger::isEnabledFor() "isEnabledFor()"
* does also take the repository threshold into account.
* - Several overloaded convenience member function are available to log
* messages with arguments of different types.
* - Two macros, \ref Log4Qt::LOG4QT_DECLARE_STATIC_LOGGER "LOG4QT_DECLARE_STATIC_LOGGER"
* and \ref Log4Qt::LOG4QT_DECLARE_QCLASS_LOGGER "LOG4QT_DECLARE_QCLASS_LOGGER",
* allows retrieving and caching of a pointer to a logger object.
*
* - \ref Log4Qt::LogManager "LogManager"
* - A QtMessage handler can be installed via
* \ref Log4Qt::LogManager::setHandleQtMessages() "setHandleQtMessages()",
* to redirect all messages created by calls to qDebug(), qWarning(),
* qCritical() and qFatal() to a logger. The logger is named Qt and can be
* accessed using \ref Log4Qt::LogManager::qtLogger() "qtLogger()".
* - The initialisation procedure is available over a public method
* (\ref Log4Qt::LogManager::startup() "startup()").
* - The LogManager provides access to the logger used internally by the
* package (\ref Log4Qt::LogManager::logLogger() "logLogger()") and to
* its default initialisation procedure
* (\ref Log4Qt::LogManager::configureLogLogger() "configureLogLogger()").
*
* - \ref Log4Qt::WriterAppender "WriterAppender"
* - The class will call \ref Log4Qt::WriterAppender::handleIoErrors()
* "handleIoErrors()" after all I/O operations. Sub-classes should
* re-implement the function to handle errors.
*
* - \ref Log4Qt::RollingFileAppender "RollingFileAppender"*
* - The class behaves different to the log4/log4cpp implementation
* on application restart the existing log files are rolled if
* appendFile is set to false to avoid data loss.
*
* The following classes have been added:
*
* - An additional appender class, \ref Log4Qt::DebugAppender "DebugAppender",
* was added. The class appends logging events to the platform specific debug
* output.
* - Various helper class have been introduced:
* - \ref Log4Qt::ClassLogger "ClassLogger": The class ClassLogger provides
* logging for a QObject derived class.
* - \ref Log4Qt::ConfiguratorHelper "ConfiguratorHelper": The class
* ConfiguratorHelper provides a configuration file watch and last error
* for configurator classes.
* - \ref Log4Qt::DateTime "DateTime": The class DateTime provides extended
* functionality for QDateTime.
* - \ref Log4Qt::LogError "LogError": The class LogError represents an error.
* - \ref Log4Qt::Factory "Factory": The class Factory provides factories
* for Appender, Filter and Layout objects.
* - \ref Log4Qt::InitialisationHelper "InitialisationHelper": The class
* InitialisationHelper performs static initialisation tasks.
* - \ref Log4Qt::PatternFormatter "PatternFormatter": The class
* PatternFormatter formats a logging event based on a pattern string.
* - \ref Log4Qt::Properties "Properties": The class Properties implements a
* JAVA property hash.
*/
/*!
* \page Ownership Object ownership
*
* In difference to the JAVA Log4j package %Log4Qt must manage ownership and
* lifetime of the objects used. This is non trivial as objects are created
* and used in different ways.
*
* In general an object can be created explicitly for example an application
* may create Loggers, Appenders and Layouts during creation of a QApplication
* object. But they can also be automatically created by the package on
* startup using a \ref Log4Qt::PropertyConfigurator "PropertyConfigurator"
* configuration file. Objects may also be created the one way and then used
* the other. Object may be used by multiple other objects. A Layout for example
* may be used by multiple Appenders. Objects are also created from multiple
* threads. The creation may happen during static initialisation and the
* deletion during static de-initialization.
*
* The parent child model used by QObject cannot be used to handle this. It
* cannot automatically delete an object that is used by multiple others as
* for example an Appender used by multiple Loggers. In addition to this
* QObjects and their children must reside in the same thread. This would
* either mean to impose restriction on how objects can be created or to move
* objects to a specific thread.
*
* To allow an automatic deletion of not required objects as Appenders,
* Layouts and Filters QSharedPointer is used
*
* The following example configures a logger and uses reference counting to
* manage the ownership of objects.
*
* \code
* // Create layout
* TTCCLayout *p_layout = new TTCCLayout();
*
* // Create appender
* ConsoleAppender *p_appender = new ConsoleAppender(p_layout, ConsoleAppender::STDOUT_TARGET);
* p_appender->activateOptions();
*
* // Get logger
* Logger *p_logger = Logger::logger("MyClass");
* p_logger->addAppender(p_appender);
*
* // ...
*
* // Remove appender from Logger
* p_logger->removeAllAppenders(); // p_appender and p_layout are deleted here
* \endcode
*
* The following example configures a logger and uses QObject ownership of
* objects.
*
* \code
* QObject *p_parent = new MyObject;
*
* // Create objects
* ConsoleAppender *p_appender = new ConsoleAppender(p_parent);
* TTCCLayout *p_layout = new TTCCLayout(p_appender);
*
* // Configure appender
* p_appender->setTarget(ConsoleAppender::STDOUT_TARGET);
* p_appender->setLayout(LayoutSharedPtr(p_layout));
* p_appender->activateOptions();
*
* // Get logger
* Logger *p_logger = Logger::logger("MyClass");
* p_logger->addAppender(AppenderSharedPtr(p_appender));
*
* // ...
*
* // Remove appender from Logger
* p_logger->removeAllAppenders();
*
* delete p_parent; // p_appender and p_layout are deleted here
* \endcode
*
*/
/*!
* \page LogLog Logging within the package
*
* The package uses itself for logging similar to Log4j 1.3. This brings much
* more flexibility over logging to stdout, stderr like in Log4j 1.2 using
* logLog. It also enables the program to capture and handle errors raised by
* the package.
*
* Using this approach introduces the issue of recursion. The following example
* explains a situation where this happens. Let's say all logger are configured
* to be additive and only the root logger has an appender set. The appender
* is a \ref Log4Qt::FileAppender "FileAppender". During the logging of an
* event an I/O error occurs. The \ref Log4Qt::FileAppender "FileAppender" logs
* an event by itself using the logger %Log4Qt::FileAppender. The event is
* passed to the root logger, which calls then the \ref Log4Qt::FileAppender
* "FileAppender". This causes another I/O error, which is logged by
* the \ref Log4Qt::FileAppender "FileAppender".
*
* To avoid an endless loop the appender will drop the event on a recursive
* invocation. This check is done by \ref Log4Qt::AppenderSkeleton
* "AppenderSkeleton" in \ref Log4Qt::AppenderSkeleton::doAppend()
* "doAppend()".
*
* The problem only occurs, if a logger, appender, layout or filter log an
* event while an event is appended. Neither the logger class nor any of the
* layout or filter classes log events during appending of an event. Most of
* the appender classes may log errors during appending. Only the
* \ref Log4Qt::ListAppender "ListAppender" and
* \ref Log4Qt::ListAppender "ConsoleAppender" are not logging events.
*
* The default configuration uses two \ref Log4Qt::ListAppender
* "ConsoleAppender", one for stderr and one for stdout. No event will be
* dropped, because no recursive invocations can occur.
*/
/*!
* \page Init Initialization procedure
*
* The package is initialised in two stages. The first stage takes place during
* static initialization. The second stage takes place when the
* \ref Log4Qt::LogManager "LogManager" singleton is created.
*
* During static initialisation the \ref Log4Qt::InitialisationHelper
* "InitialisationHelper" singleton is created . On construction it captures
* the program startup time, reads the required values from the system
* environment and registers the package types with the Qt type system.
*
* The \ref Log4Qt::LogManager "LogManager" singleton is created on first use.
* The creation is usually triggered by the request for a \ref Log4Qt::Logger
* "Logger" object. The call to \ref Log4Qt::Logger::logger()
* "Logger::logger()" is passed through to \ref Log4Qt::LogManager::logger()
* "LogManager::logger()". On creation the \ref Log4Qt::LogManager "LogManager"
* creates a \ref Log4Qt::Hierarchy "Hierarchy" object as logger repository.
*
* After the singleton is created the logging of the package is configured to
* its default by a call to \ref Log4Qt::LogManager::configureLogLogger()
* "LogManager::configureLogLogger()". The logger
* \ref Log4Qt::LogManager::logLogger() "logLogger()" is configured to be not
* additive. Messages with the level \ref Log4Qt::Level::ERROR_INT
* "Level::ERROR_INT" and \ref Log4Qt::Level::FATAL_INT "Level::FATAL_INT" are
* written to \c stderr using a ConsoleAppender. The remaining messages are
* written to \c stdout using a second ConsoleAppender. The level is read from
* the system environment or application settings using
* \ref Log4Qt::InitialisationHelper::setting()
* "InitialisationHelper::setting()" with the key \c Debug. If a level value
* is found, but it is not a valid Level string,
* \ref Log4Qt::Level::DEBUG_INT "Level::DEBUG_INT" is used. If no level string
* is found \ref Log4Qt::Level::ERROR_INT "Level::ERROR_INT" is used.
*
* Once the logging is configured the package is initialised by a call to
* \ref Log4Qt::LogManager::startup() "LogManager::startup()". The function
* will test for the setting \c DefaultInitOverride in the system environment
* and application settings using \ref Log4Qt::InitialisationHelper::setting()
* "InitialisationHelper::setting()". If the value is present and set to
* anything else then \c false, the initialisation is aborted.
* The system environment and application settings are tested for the setting
* \c Configuration. If it is found and it is a valid path to a file, the
* package is configured with the file using
* \ref Log4Qt::PropertyConfigurator::doConfigure(const QString &, LoggerRepository *)
* "PropertyConfigurator::doConfigure()". If the setting \c Configuration is
* not available and a QCoreApplication object is present, the application
* settings are tested for a group \c Properties. If the group exists,
* the package is configured with the setting using the
* \ref Log4Qt::PropertyConfigurator::doConfigure(const QSettings &properties, LoggerRepository *)
* "PropertyConfiguratordoConfigure()". If neither a configuration file nor
* configuration settings could be found, the current working directory is
* searched for the file \c "log4qt.properties". If it is found, the package
* is configured with the file using
* \ref Log4Qt::PropertyConfigurator::doConfigure(const QString &, LoggerRepository *)
* "PropertyConfigurator::doConfigure()".
*
* The following example shows how to use application settings to initialise the
* package.
*
* \code
* # file: myapplication.h
*
* #include qapplication.h
*
* class MyApplication : public QApplication
* {
* Q_OBJECT
*
* public:
* MyApplication();
* ~MyApplication();
* void setupLog4Qt();
* }
* \endcode
* \code
* # file: myapplication.cpp
*
* #include myapplication.h
*
* MyApplication::MyApplication(
* {
* // Set Application data to allow Log4Qt initialisation to read the
* // correct values
* setApplicationName("MyApplication");
* setOrganisationName("MyOrganisation");
* setOrganizationDomain("www.myorganisation.com");
*
* // Log first message, which initialises Log4Qt
* Log4Qt::Logger::logger("MyApplication")->info("Hello World");
* }
*
* MyApplication::~MyApplication()
* {
* }
*
* void MyApplication::setupLog4Qt()
* {
* QSettings s;
*
* // Set logging level for Log4Qt to TRACE
* s.beginGroup("Log4Qt");
* s.setValue("Debug", "TRACE");
*
* // Configure logging to log to the file C:/myapp.log using the level TRACE
* s.beginGroup("Properties");
* s.setValue("log4j.appender.A1", "org.apache.log4j.FileAppender");
* s.setValue("log4j.appender.A1.file", "C:/myapp.log");
* s.setValue("log4j.appender.A1.layout", "org.apache.log4j.TTCCLayout");
* s.setValue("log4j.appender.A1.layout.DateFormat", "ISO8601");
* s.setValue("log4j.rootLogger", "TRACE, A1");
*
* // Settings will become active on next application startup
* }
* \endcode
*/
/*!
* \page Env Environment Variables
*
* The package uses environment variables to control the initialization
* procedure. The environment variables replace the system property entries
* used by Log4j.
*
* For compability reasons the Log4j entry is recognised. Alternatively a
* environment variable style Log4Qt form can be used. The following entries
* are used:
*
* - LOG4QT_DEBUG
* The variable controls the \ref Log4Qt::Level "Level" value for the
* logger \ref Log4Qt::LogManager::logLogger() "LogManager::logLogger()".
* If the value is a valid \ref Log4Qt::Level "Level" string, the level for
* the is set to the level. If the value is not a valid
* \ref Log4Qt::Level "Level" string, \ref Log4Qt::Level::DEBUG_INT
* "DEBUG_INT" is used. Otherwise \ref Log4Qt::Level::ERROR_INT "ERROR_INT"
* is used.
* - \ref Log4Qt::LogManager::configureLogLogger()
* "LogManager::configureLogLogger()"
*
* - LOG4QT_DEFAULTINITOVERRIDE
* The variable controls the \ref Init "initialization procedure" performed
* by the \ref Log4Qt::LogManager "LogManager" on startup. If it is set to
* any other value then \c false the \ref Init "initialization procedure"
* is skipped.
* - \ref Log4Qt::LogManager::startup() "LogManager::startup()"
*
* - LOG4QT_CONFIGURATION
* The variable specifies the configuration file used for initialising the
* package.
* - \ref Log4Qt::LogManager::startup() "LogManager::startup()"
*
*
* Environment variables are read during static initialisation on creation of
* the \ref Log4Qt::InitialisationHelper "InitialisationHelper". They can be
* accessed by calling \ref Log4Qt::InitialisationHelper::environmentSettings()
* "InitialisationHelper::environmentSettings()".
*
* All settings can also be made in the application settings under the group
* \c %Log4Qt. For example the environment variable \c LOG4QT_DEBUG is
* equivalent to the setting \c Debug. If an environment variable is
* set it takes precedence over the application setting. Settings are only
* used, if an QApplication object is available, when the
* \ref Log4Qt::LogManager "LogManager" is
* initialised (see \ref Log4Qt::InitialisationHelper::setting()
* "InitialisationHelper::setting()" for details).
*/
/*!
* \page Undocumented Undocumented functions
*
* In general it was tried to avoid the usage of undocumented features of Qt.
* Nice to have features like for example Q_DECLARE_PRIVATE are not used. Only
* features that would have been resulted in re-coding the same functionality
* are used.
*
* - QT_WA: The macro is used to call Windows A/W functions
* - \ref Log4Qt::DebugAppender "DebugAppender"
*/
/*!
* \page Assumptions Assumptions
*
* The following assumptions are used throughout the package:
*
* - Reading / writing of bool or int is thread-safe, if declared volatile
* - \ref Log4Qt::ListAppender "ListAppender"
* - \ref Log4Qt::AppenderSkeleton "AppenderSkeleton"
* - \ref Log4Qt::ConsoleAppender "ConsoleAppender"
* - \ref Log4Qt::FileAppender "FileAppender"
* - \ref Log4Qt::Hierarchy "Hierarchy"
* - \ref Log4Qt::Level "Level"
* - \ref Log4Qt::Logger "Logger"
* - \ref Log4Qt::WriterAppender "WriterAppender"
* - \ref Log4Qt::Layout::format() "Layout::format()" is implemented reentrant
* in all sub-classes.
* - \ref Log4Qt::AppenderSkeleton "AppenderSkeleton"
* - Being able to use singleton objects during static de-initialization without
* order issues is more valuable then their destruction.
* - \ref Log4Qt::LogManager "LogManager"
* - \ref Log4Qt::LOG4QT_IMPLEMENT_INSTANCE "LOG4QT_IMPLEMENT_INSTANCE"
*/
#include
#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
# error "Log4Qt requires Qt version 5.12.0 or higher"
#endif
#if (defined(Q_CC_MSVC) && _MSC_VER < 1900)
# error "Log4Qt requires at least msvc version 14 (VS2015) or higher for used c++11 features"
#endif
#if (!defined(Q_CC_CLANG) && defined(Q_CC_GNU) && Q_CC_GNU < 408)
# error "Log4Qt requires at least gcc version 4.8 or higher for used c++11 features"
#endif
#if (defined(Q_CC_CLANG) && Q_CC_CLANG < 303)
# error "Log4Qt requires at least clang version 3.3 or higher for used c++11 features"
#endif
/*
LOG4QT_VERSION is (major << 16) + (minor << 8) + patch.
*/
#define LOG4QT_VERSION LOG4QT_VERSION_CHECK(LOG4QT_VERSION_MAJOR, LOG4QT_VERSION_MINOR, LOG4QT_VERSION_PATCH)
/*
can be used like #if (LOG4QT_VERSION >= LOG4QT_VERSION_CHECK(1, 3, 0))
*/
#define LOG4QT_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
/*!
* \brief The namespace Log4Qt %Log4Qt encloses all parts of the package.
*/
namespace Log4Qt
{
/*!
* This macro expands a numeric value of the form 0xMMmmPP (MM = major,
* mm = minor, PP = patch) that specifies Log4Qt's version number.
* This is the version against which the application is compiled.
*
* \sa \ref Log4Qt::LOG4QT_VERSION_STR "LOG4QT_VERSION_STR",
* \ref Log4Qt::LogManager::version() "LogManager::version()"
*/
/*!
* The macro expands to a string that specifies the Log4Qt's version
* number. This is the version against which the application is compiled.
*
* \sa \ref Log4Qt::LOG4QT_VERSION "LOG4QT_VERSION",
* \ref Log4Qt::LogManager::version() "LogManager::version()"
*/
enum ErrorCode
{
OK = 0,
// AppenderSkeleton, FileAppender, WriterAppender
APPENDER_ACTIVATE_MISSING_LAYOUT_ERROR,
APPENDER_ACTIVATE_MISSING_WRITER_ERROR,
APPENDER_ACTIVATE_MISSING_FILE_ERROR,
APPENDER_CLOSED_ERROR,
APPENDER_INVALID_PATTERN_ERROR,
APPENDER_NO_OPEN_FILE_ERROR,
APPENDER_NOT_ACTIVATED_ERROR,
APPENDER_OPENING_FILE_ERROR,
APPENDER_RENAMING_FILE_ERROR,
APPENDER_REMOVE_FILE_ERROR,
APPENDER_USE_INVALID_PATTERN_ERROR,
APPENDER_USE_MISSING_LAYOUT_ERROR,
APPENDER_USE_MISSING_WRITER_ERROR,
APPENDER_WRITING_FILE_ERROR,
// Level
LEVEL_INVALID_LEVEL_STRING,
// Layouts, PatternFormatter
LAYOUT_EXPECTED_DIGIT_ERROR,
LAYOUT_OPTION_IS_NOT_INTEGER_ERROR,
LAYOUT_INTEGER_IS_NOT_POSITIVE_ERROR,
// Logger
LOGGER_INVALID_LEVEL_FOR_ROOT,
// PropertyConfigurator, OptionHandler
CONFIGURATOR_OPENING_FILE_ERROR,
CONFIGURATOR_READING_FILE_ERROR,
CONFIGURATOR_INVALID_SUBSTITUTION_ERROR,
CONFIGURATOR_INVALID_OPTION_ERROR,
CONFIGURATOR_MISSING_APPENDER_ERROR,
CONFIGURATOR_UNKNOWN_APPENDER_CLASS_ERROR,
CONFIGURATOR_MISSING_LAYOUT_ERROR,
CONFIGURATOR_UNKNOWN_LAYOUT_CLASS_ERROR,
CONFIGURATOR_PROPERTY_ERROR,
CONFIGURATOR_UNKNOWN_TYPE_ERROR,
APPENDER_MISSING_DATABASE_OR_TABLE_ERROR,
APPENDER_EXEC_SQL_QUERY_ERROR,
APPENDER_INVALID_DATABASE_LAYOUT_ERROR,
APPENDER_TELNET_SERVER_NOT_RUNNING,
APPENDER_ASNC_DISPATCHER_NOT_RUNNING
};
} // namespace Log4Qt
#endif // LOG4QT_H