
The logging system is an indispensable tool for logging application operation. For those who do not want to implement it independently, there are already countless libraries in C ++ (Log4cplus, Apache log4cxx, Boost.Log, etc.), but Easylogging ++ is easy to use and compact, does not require third-party libraries or installation. All its code is contained in a single header file, which simply needs to be included in the application code.
This article offers a brief overview of the Easylogging ++ functionality and examples of using this library.
Let's start right away with a short example that should demonstrate how easy it is to start using Easylogging ++:
#include "easylogging++.h" _INITIALIZE_EASYLOGGINGPP int main(int argv, char* argc[]) { LOG(INFO) << " Habrahabr"; return 0; }
The library is connected with one header, no lib is required.
In the above short commentary example, the _INITIALIZE_EASYLOGGINGPP macro is worth calling. It is required to initialize some static variables and set up a handler for crashes. The call must be made one and only once, before using the library.
')
Reference:
- Library website: easylogging.org
- License: MIT
- Language: C ++ 11
- Depends on: --
- Platforms: Windows, Mac OSX, Debian (Ubuntu, Mint), Fedora
- Compilers: GCC 4.7+, Visual C ++ 11.0+, Intel C ++ 13.0+, MinGW, Cygwin
What for:
Bribes simplicity and intuitiveness of the library. We were unable to find other implementations that fit in a single header file (which is easy to use from project to project) and provide a very complete set of features:
- Cross platform
- Multithreading
- Setting the issue format
- Delivery of custom data types (there is a ready implementation for STL containers)
- Switching log files (rolling)
- Conditional and periodic issue
- Checks and handling program crashes
- Calculation of the execution time of methods
Format setting
Despite the fact that in our opinion the main advantage of the library is its autonomy, compactness and simplicity, sometimes there is still the need to change something in its behavior. For example, by default the output format looks like this:
11/06/2014 11:23:29,218 INFO [default] Simple info message 11/06/2014 11:23:29,218 ERROR [default] Simple error message 11/06/2014 11:23:29,218 WARN [default] This is just a warning
To begin, Easylogging provides the developer with the opportunity to customize the set of output fields, as well as their order.
The example below demonstrates how you can put the message type at the beginning, then the date in a modified format, then the signature of the method from which the call was made and the message itself:
Setting fields el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Format, "%level %datetime{%H:%m:%s} (%func): %msg"); LOG(INFO) << " ";
The result on the screen will look like this:
INFO 11:33:58 (int main(int, char**)):
In addition to standard things in the format description, such as message type, date or file name (cpp), you can use your own macros to output additional information to the log. For example:
Own fields const char* getMode(void) { switch(mode) { case 1: return "Service"; default: return "App"; } return ""; // just in case } int main(int argv, char* argc[]) { el::Helpers::installCustomFormatSpecifier(el::CustomFormatSpecifier("%mode", getMode)); el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Format, "%level %mode: %msg"); LOG(INFO) << " "; return 0; }
There are also a number of flags that change various aspects of Easylogging behavior. By default, for example, all output is duplicated in stdout. Besides the fact that it is easy to completely turn off, we can, on the contrary, make it all the more visual by setting the flag to activate the color output:
Setting flags int main(int argv, char* argc[]) { el::Loggers::reconfigureAllLoggers(el::ConfigurationType::ToStandardOutput, "true"); el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); LOG(INFO) << " "; LOG(ERROR) << " "; LOG(WARNING) << " "; return 0; }
The result on the screen will look like this:

Any setting can be changed not only from the code: in addition, the library supports configuration files in a proprietary format (usual conf with tabs), which can be loaded during the launch or operation of applications. The library also can read parameters from the command line: to do this, you need to place the corresponding call in main:
Reading command line int main(int argv, char* argc[]) { _START_EASYLOGGINGPP(argc, argv); }
Custom types
The library allows you to log the contents of objects of any type. This can be done in two ways:
- Inherit your object from some el :: Loggable and implement the corresponding virtual method
- Implement a static method (operator) to send your object to the log
The second option seems to us more convenient, because you don’t need to “dirty” your classes with obscure ancestors and, moreover, if you decide to remove logging, you don’t have to clean all classes. Let's show how the implementation will look like in the second way:
Custom types class Dummy { public: Dummy(int i, std::string s) : m_Int(i), m_String(s){} int getInt() const{return m_Int;} std::string const& getString() const {return m_String;} private: int m_Int; std::string m_String; }; inline MAKE_LOGGABLE(Dummy, obj, os) { os << "{i:"<<obj.getInt()<<", s:"<<obj.getString()<<"}"; return os; } int main(void) { Dummy object(1, "Habrahabr"); LOG(INFO) << "Dummy: " << object; return 0; }
Log files
By default, the log file will be located in the working directory of the application and will have a path ../logs/myeasylog.log.
You can change the name of the log file or its location at any time. This can be useful if required, for example, to switch to a new file every day. Here is an example of manually switching the log file:
Manual switching int main(void) { LOG(INFO) << " "; el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Filename, "logs/20140205.log"); LOG(INFO) << " "; return 0; }
After doing this, there are two files in the logs folder: myeasylog.log and 20140205.log. Each message will be in its file. It is easy to see that it is similarly possible in the "manual" mode to perform switching of logs according to a given algorithm (for example, to make daily logs or to allocate a certain number of entries to each file)
Easylogging ++ can switch logs automatically, but solely based on the size of their file. Settings allow you to set a threshold for the file size, after which the current log will be reset. Before this event occurs, you will be given the opportunity to copy the previous log file. Below is an example where we set the 1Kb limit for the file size and before switching to the new log we save a backup of the old one:
Hidden text int log_sequence = 1; void LogsRollout(const char* fname, size_t fsize) { string fileName = fname; size_t position = fileName.find("."); string extractName = (string::npos == position)? fileName : fileName.substr(0, position); extractName = extractName + to_string(log_sequence++) + ".log"; int status = rename(fname, extractName.c_str()); if(status) { } } int main(void) { el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck); el::Loggers::reconfigureAllLoggers(el::ConfigurationType::MaxLogFileSize, "1024"); el::Helpers::installPreRollOutCallback(LogsRollout); for(int i=0; i<1024; i++) { LOG(INFO) << "Message #"<< i+1; } return 0; }
Conditional and periodic issue
The library has a macro LOG_IF declared to output a message to the log only if the specified condition is met:
Hidden text LOG_IF(true, INFO) << " "; LOG_IF(false, WARNING) << " ";
Periodic issuance can also be useful in some cases. This method means that not every message will be logged, but, for example, every 10s or 100s. In this case, two possibilities are realized:
- LOG_EVERY_N (n, LEVEL) - displays every nth message
- LOG_AFTER_N (n, LEVEL) - displays a message only after n operations
Checks and handling program crashes
The library declares a whole series of macros for issuing a message to the log and crashing only if some critical condition is not met:
CHECK, CHECK_EQ, CHECK_NE, CHECK_NOTNULL , etc. These macros are very similar to assert, only with the issuance of the log.
It is quite convenient to use them:
Hidden text CHECK(true) << " "; CHECK_LE(2, 1) << "2 < 1 ???"; CHECK_STREQ(argv[3], "1") << " 1";
This is how it falls when the condition is not met:
07:12:51,375 FATAL Check failed: [2 <= 1] 2 < 1 ??? 07:12:51,375 WARN Aborting application. Reason: Fatal log at [/home/borg/Temp/App/main.cpp:34] Aborted
If you do not change the default settings, then if the condition is not met, the program will be interrupted, and the message will appear in the log with the FATAL level.
Additionally, the default library handles application crashes. Of course, this behavior can be disabled, but sometimes it can be convenient to get logs for a number of signals, for example: SIGFPE, SIGILL, SIGSEGV, etc. For gcc, you can also issue a call stack to the log.
Hidden text #include "easylogging++.h" _INITIALIZE_EASYLOGGINGPP void CustomCrashHandler(int signal) { LOG(ERROR) << " !!"; el::Helpers::crashAbort(signal); } int main(int argv, char* argc[]) { el::Helpers::setCrashHandler(CustomCrashHandler); int a = 0; a = 1/a; return 0; }
Here so falls:
17:46:53,074 ERROR !! Aborted
findings
The Easylogging ++ library represents a less complete set of functionality that one would expect to see in such a product.
The undoubted advantages include: its compactness, ease of operation and cross-platform.
The disadvantages we would consider only the requirement for the use of C ++ 11, although, perhaps, the authors could do with the "ordinary" C ++.