Tuesday, June 2, 2015

Easylogging++: how to get one log file per day

I introduced EasyLogging++ before. This article will build on that to show how to rotate the logs daily.

In a nutshell, assuming your log filename already has date specifiers in it, all you have to do is run these two lines, at midnight each day:

auto L = el::Loggers::getLogger("default");
L->reconfigure();

If you have multiple loggers, repeat that for all of them.

I recommend using a config file to configure EasyLogging++; but if you are configuring it completely in your code, and your FILENAME entry does not include date specifiers, you can instead change the filename at any time with this:

Loggers::reconfigureAllLoggers(
 ConfigurationType::Filename,
 "/path/to/logs/my-new-filename.log"
 );

But, going back to the first approach, here is a complete program to show creating a new log file every 20 seconds (!). First create a logging.conf file with these contents:

-- default
* GLOBAL:
    FORMAT = "%datetime{%Y-%M-%d %H:%m:%s.%g},%level,%thread,%msg"
    Milliseconds_Width = 4
    TO_FILE = true
    FILENAME = "info.%datetime{%Y%M%d_%H%m%s}.log"
    LOG_FLUSH_THRESHOLD = 5

(The FORMAT, and Milliseconds_Width lines are optional, but useful for checking it worked.)

Here is the full code:

#define _ELPP_THREAD_SAFE
#define _ELPP_NO_DEFAULT_LOG_FILE
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP

namespace sc = std::chrono;

int main(int,char**){
el::Loggers::configureFromGlobal("logging.conf");
LOG(INFO)<<"The program has started!";

std::thread logRotatorThread([](){
const sc::seconds wakeUpDelta = sc::seconds(20);
auto nextWakeUp = sc::system_clock::now() + wakeUpDelta;

while(true){
    std::this_thread::sleep_until(nextWakeUp);
    nextWakeUp += wakeUpDelta;
    LOG(INFO) << "About to rotate log file!";
    auto L = el::Loggers::getLogger("default");
    if(L == nullptr)LOG(ERROR)<<"Oops, it is not called default!";
    else L->reconfigure();
    }

});

logRotatorThread.detach();

//Main thread
for(int n=0; n < 1000; ++n){
    LOG(TRACE) << n;
    std::this_thread::sleep_for(sc::milliseconds(100));
    }

LOG(INFO) << "Shutting down.";
return 0;
}

I compiled it with this command:

g++ -std=c++11 -Wall -Werror logtest.cpp -lpthread -o logtest

and then ran it with this command:

./logtest

It should be easy to follow. I set up a dedicated thread to call reconfigure() every 20 seconds, and then the main thread logs a counter about 10 times/second.

You’ll end up with about 5 log files, and you can examine them to see that no log commands were lost.

If I was coding for a mission-critical application, where missing even a single log line would be considered Very Bad, I might set up a mutex and a lock to make sure the main thread is not active when the call to reconfigure() happens. I don’t know for sure if that is needed, or if it is guaranteed to be safe. If you know for sure one way or the other, please leave a comment!

But, for a once/day log rotation, in most applications this is a small enough risk that I would not want the overhead of the extra mutex, and I would go with the code shown above.