Wednesday, August 27, 2014

C++ Logging: EasyLogging++

The Basics Of EasyLogging++

I wanted a basic logging library for C++. The first one I looked at required I first install Java to be able to compile it. Eh? For a modern C++ library? So then I added “header-only” to be key requirements. And shortly after that I sadly started to resign myself to writing my own. Then, luckily, I stumbled across EasyLogging++.

I retrofitted a couple of projects today, stripping out the ad hoc logging, and replacing it with this. The documentation is good, but gets lost in the details some times, and I felt a simpler tutorial was needed. This is my attempt at it:

Here is the Hello World example:
 
#define _ELPP_THREAD_SAFE
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP

int main(int,char**){
LOG(INFO)<<"Hello World!";
}
 
I’ve decided to request it be thread-safe, right from this first example, because most C++11 apps use threads. Remove that line if you definitely have a single-threaded application. (Speaking of which, this library is C++11 only; but there is a link on their website to an earlier version that supports older C++).

The above program prints “Hello World!” to stdout. But, obviously, you want to log to a file. And for a project of any worthwhile size you will end up with a logging configuration file anyway, so let’s add one now. Here is “logging.conf”:
 
-- default 
* GLOBAL:
    TO_FILE = true
    FILENAME = "info.%datetime{%Y%M%d}.log"
    TO_STANDARD_OUTPUT   =  false
* WARNING:
    TO_STANDARD_OUTPUT   =  true
* ERROR:
    TO_STANDARD_OUTPUT   =  true
* FATAL:
    TO_STANDARD_OUTPUT   =  true
 
Here I am saying I want to have one log file per day, using the YYYYMMMDD datestamp in the log filename, and that it will store messages of all log levels. I’m also saying that I want TRACE and INFO messages to only go to the file, but WARNING, ERROR and FATAL to go to both the file and stdout. There may be more elegant ways to do that, but the above works.

You use the config file by adding just one line at the start of main():
 
#define _ELPP_THREAD_SAFE
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP

int main(int,char**){
el::Loggers::configureFromGlobal("logging.conf");
LOG(INFO)<<"Hello World!";
}
 
That code will write “Hello World\n” to e.g. “info.20140827.log”
 
And that is all you need to know; all your other questions will be answered by the documentation. Do please spend some time with the documentation as there is a lot of functionality in this library (e.g. Conditional logging, Occasional Logging, log output of STL containers, log output for your own classes, datestamps to customizable sub-second accuracy, run-time disabling of certain log levels, and even more.)

One feature it does not have, that I wanted, is an asynchronous log queue. I.e. a thread grabs the lock just long enough to push a string on to a queue, with another dedicated thread doing the actual writing to disk. This makes sure your worker threads do not get caught up waiting for a lock because another thread is waiting for disk I/O to finish. However another feature, that EasyLogging++ does have, lessens the impact of this: it only flushes to disk every N log messages. So effectively strings are being pushed to a queue, and it is only once every N times that a thread gets caught waiting for disk I/O to finish. N defaults to 256; I reduced it in my config file to 5, which gives a fair balance between thread wait and log latency (and the risk of losing log messsages). This is not as good as an asynchronous log queue, but I can live with it.
Written with StackEdit.

8 comments:

Anonymous said...

Nice :)

Darren Cook said...

Note that instead of `#define _ELPP_THREAD_SAFE` you could put `-D_ELPP_THREAD_SAFE` in your Makefile. This might be better if you have multiple source files (though I believe if you have the #define before every single place you write `#include "easylogging++.h"` that you cannot go wrong).

Thanks to Majid for some corrections on my initial post.

route66 said...

Thanks for the clear explanation and overview of Easylogging++ basic features. Now I feel much more comfortable with digging into this manual.
Thank you for this great post!

Anonymous said...

Great tutorial. Quick question though: When you specify that timestamp, the log file is stuck to that file for the life of the program?

So if you have log.20150210.out, and then it goes past midnight to 20150211, the file I guess wouldn't start using that new file unless the program was restarted?

Darren Cook said...

Anon: Regarding daily log renaming, yes, it was not possible when I wrote the tutorial, but a feature to support it has since been added. I will write a blog post on how to use it.

Darren Cook said...

The log rotating article is now up: http://darrendev.blogspot.com/2015/06/easylogging-how-to-get-one-log-file-per.html (I think my last comment may have been wrong - the support might have already been in the library.)
The blog post also shows a complete EasyLogging++ test program.

Anonymous said...

Why do you need #include "easylogging++.h" twice?

Darren Cook said...

Oops, good catch, just fixed it.