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...?
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.
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.