Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I manually flush a boost log?

I'm playing with Boost.Log in boost 1.54.0 to see if it is a viable option for my application. In general, I don't have a problem with the buffering, so I'm not looking to turn on auto_flush or anything... but I noticed that messages that are logged before I call fork() are duplicated, and I'm wondering if it's because they are buffered, the buffer gets duplicated when the process image is copied, and then both processes eventually write their buffer copies to the log file...

So basically, I'd like to just do a manual flush on the log, one time only, immediately before I call fork() in order to be sure no messages are still sitting in memory. In other words, I'm looking for something akin to fflush(), .flush(), << flush, etc. that I can use on a boost log.

I did try using << flush with the log, but I still get the duplicated messages, so I'm not 100% sure whether it's flushing and the duplicates are caused by some other problem, or if it somehow silently ignores the << flush...

Edit:

I was looking around and found that boost log is not fork-safe. So I should add that I am not trying to use the same log in parent and child processes. I have two forking scenarios - in one, the parent terminates immediately and child contineus (so that should be safe), and in the other, the child should open its own separate log file, so that should be safe also... but I'd need to figure out how to close a log file sink and then open a new one (on a different file). I suppose closing the sink may also be a way to force a flush...?

like image 246
Dave Lillethun Avatar asked Jul 26 '13 18:07

Dave Lillethun


2 Answers

Okies... I had to dig through the boost code a bit (but not too much), and I found this, which seems to work:

When you call add_file_log(strLogFilename) it returns a shared_ptr<sink> where sink is your type of sink (e.g., shared_ptr< synchronous_sink< text_file_backend > >). If you instead create your sink "manually" then of course you have a pointer to it as well... It seems the sinks and backend both have a .flush() method. I'm not sure offhand how you directly get a copy of the backend to call its flush, but the flush on the sink seems to simply call the flush on its backend(s), so that works. Here's some example code of what I found to work for me:

shared_ptr< synchronous_sink< text_file_backend > > pLogSink = add_file_log(strLogFilaname);
BOOST_LOG_TRIVIAL(debug) << "Boost log!";

// other work goes here

BOOST_LOG_TRIVIAL(debug) << "About to fork...";
if (pLogSink)
  pLogSink->flush();
pid_t pid = fork();

if (pid < 0)
  // handle error
else if (pid > 0)
  exit(EXIT_SUCCESS); // parent terminates
assert(0 == pid); // child continues

BOOST_LOG_TRIVIAL(debug) << "Fork succeeded!";

Using this method, I now see each log message only once. Of course, bear in mind this warning about mixing Boost.Log with fork()... http://boost-log.sourceforge.net/libs/log/doc/html/log/rationale/fork_support.html

In my example, it's safe only because the parent process immediately exits after forking without touching the log at all (after the fork). Thus there isn't any contention for the log.

Despite the limitations, I can see using this in a few cases: 1) daemonizing a process (which is what I'm trying to do here, actually), 2) fork-exec pattern (which does work fine with Boost.Log, according to the above URL), or 3) child process immediately closes the file sink and opens a new sink for the log that points to a different file (from the one the parent process is using) - I think this third case should be safe.

like image 133
Dave Lillethun Avatar answered Oct 03 '22 20:10

Dave Lillethun


Even simpler code (with trivial logging):

#include <boost/filesystem.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>

namespace logging = boost::log;

void InitLogging() {
  boost::filesystem::path full_path(boost::filesystem::current_path());

  auto sink = logging::add_file_log("sample.log");
  BOOST_LOG_TRIVIAL(info) << "Log initialized.";
  BOOST_LOG_TRIVIAL(info) << "Working dir: " << full_path;
  sink->flush();
}

int main() {
  InitLogging();
  return 0;
}

According to my tests flush is a blocking method. I use it only during initialization so if something wrong happen there I know where the execution was.

like image 45
Paweł Szczur Avatar answered Oct 03 '22 20:10

Paweł Szczur