QLogger is made to be compatible with Qt framework logs management, this library provide an easy (and thread-safe) way to use multiple sinks behaviour.
Tip
Latest development/pull requests will be committed into main
branch.
Each stable release have their dedicated branch:
1.0.x
: branchdev/1.0
1.1.x
: branchdev/1.1
- etc...
Table of contents:
- 1. Requirements
- 2. How to build
- 3. How to use
- 4. Documentation
- 5. Library details
- 6. License
- 7. Ressources
This library requires at least C++ 11 standard (see implementation section for more details)
Below, list of required dependencies:
Dependencies | Comments |
---|---|
Qt | Library built with Qt framework and compatible with series 5.15.x and 6.x |
This library can be use as an embedded library in a subdirectory of your project (like a git submodule for example):
- In the root CMakeLists, add instructions:
add_subdirectory(qlogger) # Or if library is put in a folder "dependencies" : add_subdirectory(dependencies/qlogger)
- In the application CMakeLists, add instructions :
# Link QLogger library
target_link_libraries(${PROJECT_NAME} PRIVATE qlogger)
This library only have to be initialized in the main
method of the application, then all calls to Qt logs methods (qDebug()
, qInfo()
, qWarning()
, etc...) of the application and used libraries will be redirected to QLogger. Multiple sinks behaviour are available:
- File rotating sink via
QLogger::QLoggerFactory::initLoggerRotating()
- Daily sink via
QLogger::QLoggerFactory::initLoggerDaily()
Example:
#include "qlogger/qloggerfactory.h"
int main(int argc, char *argv[])
{
/* Set logs */
QLogger::QLoggerFactory::instance().initLoggerRotating(QFileInfo("logs/log.txt"), 3, 1024 * 1024 * 5, true);
/* Manage application properties */
QApplication app(argc, argv);
...
return app.exec();
}
This will configure QLogger to:
- Use
logs/log.txt
as main file (once rotated, generated files will be:logs/log1.txt
andlogs/log2.txt
) - Number of logs files limited to 3
- Log file max size limited to 5 megabytes (5Mb) (1 Kb = 1024 bytes, 1Mb = 1024 * 1024 bytes)
- All logs messages will also be redirected to console (useful during development phase)
QLogger library will properly destruct (and close) all used ressources automatically when application is closed. But if user need to quit QLogger manually, just use:
QLogger::QLoggerFactory::instance().desinit();
In this case, default Qt logger will be restablished. User can still use his own log behaviour with qInstallMessageHandler()
method (QLogger must have been desinitialized !)
Tip
If log sink must be changed while one has already been initialized, caller don't need to manually call desinit()
method, each sink init method will properly take care of this.
By default, log message will be formatted as below, depending of type of build:
- Release:
[utc-date][type-log] log-msg
- Debug:
[utc-date][type-log] log-msg (cpp-file:cpp-line, cpp-function)
If source file informations must appears on logs even on release mode, please use the associated macro in your main CMakefile: QT_MESSAGELOGCONTEXT
.
So for example, when using:
const QString strValue = "nine";
const int value = 9;
qDebug() << "My value is:" << myValue;
qDebug("String [%s] converted as number is [%d]", qUtf8Printable(strValue), value);
This will log:
[2023-07-09T13:36:09.245Z][debug] My value is: 9 (mainwindow.cpp:14, onMyCustomBtnClicked)
[2023-07-09T13:36:09.246Z][debug] String [nine] converted as number is [9] (mainwindow.cpp:15, onMyCustomBtnClicked)
Format log message can also be customized by using a custom QLogger::LogFormatter
method, here an example:
#include "qlogger/qloggerfactory.h"
/*****************************/
/* Macro definitions */
/*****************************/
#define APP_LOG_FILE "logs/log.txt"
#define APP_LOG_NB_FILES 3
#define APP_LOG_SIZE (1024 * 1024 * 5) // Equals 5 megabytes (Mb)
#define APP_LOG_ENABLE_CONSOLE true
/*****************************/
/* Custom log formatter */
/*****************************/
QString logFormatter(const QLogger::LogEntry &log)
{
QString fmt = QString("[%1][%2] %3").arg(
QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs),
log.getTypeString(),
log.getMsg()
);
/* Can we add context informations ? */
if(log.contextIsAvailable()){
fmt += QString(" (%1:%2, %3)")
.arg(log.getCtxFile().fileName())
.arg(log.getCtxLine())
.arg(log.getCtxFctName());
}
/* Terminate log line */
fmt += '\n';
return fmt;
}
/*****************************/
/* Main method */
/*****************************/
int main(int argc, char *argv[])
{
/* Set logs */
QLogger::LogFormatter::setCustomFormat(logFormatter);
QLogger::QLoggerFactory::instance().initLoggerRotating(QFileInfo(APP_LOG_FILE), APP_LOG_NB_FILES, APP_LOG_SIZE, APP_LOG_ENABLE_CONSOLE);
/* Manage application properties */
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
User can also configure whether or not all messages should be logged. By default, all messages are logged but that can be changed by choosing minimum level to use (see Qt log level enum doc):
QLogger::QLoggerFactory::instance().setLevel(QtWarningMsg);
Note
This example will set logger to only manage message of level: QtWarningMsg
, QtCriticalMsg
and QtFatalMsg
In order to easily check at compilation time library version (to manage compatibility between multiple versions for example), macro QLOGGER_VERSION_ENCODE
(defined inside qloggerglobal.h file) can be used:
#if QLOGGER_VERSION >= QLOGGER_VERSION_ENCODE(2,0,0)
// Do stuff for version 2.0.0 or higher
#else
// Do stuff for earlier versions
#endif
At runtime, it is recommended to use the static method:
#include "qlogger/qloggerfactory.h"
const QVersionNumber &qloggerSemver = QLogger::QLoggerFactory::getLibraryVersion();
All classes/methods has been documented with Doxygen utility and automatically generated at online website documentation.
Note
This repository contains two kinds of documentation:
- Public API: Available via online website documentation or locally via Doxyfile
docs/fragments/Doxyfile-public-api.in
- Internal: Available locally only via
docs/fragments/Doxyfile-internal.in
To generate documentation locally, we can use:
# Run documentation generation
doxygen ./Doxyfile-name
# Under Windows OS, maybe doxygen is not added to the $PATH
"C:\Program Files\doxygen\bin\doxygen.exe" ./Doxyfile-name
Tip
You can also load the Doxyfile into Doxywizard (Doxygen GUI) and run generation.
We already have many good C++ logs libraries outside, so why to create a new one ? Since many of my custom application are built with Qt framework, I thought that having Qt framework as a dependency was already enough and didnt' want to use another just for logging. Besides, alternatives C++ logs libraries doesn't behave properly when used inside a library (.dll
under Windows, .so
under Linux) but Qt log framework allow such usage.
Qt log module allow to easily add our custom log behaviour throught qInstallMessageHandler()
. This library is just build around that extension feature.
This library use the singleton pattern to manage QLoggerFactory instance. This singleton was implemented using Meyer's Singleton pattern, this implementation use static variable initialization to ensure thread-safety at initialization. This doesn't ensure your singleton to be thread-safe... only his initialization and desininitialization are !
Note that (at least !) a C++11 compiler is required (C++11 added requirement for static variable initialization to be thread-safe).
For more informations about Meyer's Singleton, see below:
Then, library thread-safety is ensured by using a mutex whenever a log message is received (to prevent from thread-race issues when trying to write to log to file for example).
This library is licensed under MIT license.
- Qt ressources
- Singleton pattern
- Documentation utility
- C++ logs libraries: