Monday, November 12, 2007

Boost Logging v2 - Your Killer Feature

Hi all,

I know everyone has his favorite feature he thinks a logging lib should provide. I've implemented what most people need. But just in case I forgot something...

Do you have a feature you can't live without? You can drop me an email, or drop a comment here - and other people can share their thoughts as well...



John Torjo said...

As a side-note, I've also looked at what this page has to say:

I've put my comments here:

Scott said...

Hi John,

I would like to use Boost Logging v2 from a library. Ideally I would like the library to honor the logging settings of the application using it. If the application does not use logging, I would like the library to just log nothing. I would prefer that the application not have to do anything at all if it does not want to use logging, and that my library doesn't impose any particular logging semantics on the application.

Basically, this is how the C# .NET logging library "log4net" behaves, and I find it very convenient.

Is there a way to get similar behavior from Boost Logging v2? I can't figure out how to do logging from the library without the application providing me with a logging object to use.

How would you recommend using Boost Logging v2 from a library designed for general consumption?



Amit said...

Hi John,
I just read the documentation for your logging library. It appears to be comprehensively thought out, and I don't mind trying it in my custom webs server.
However, there are a couple of things I would like to see added to the library:
1. Allow log settings (like level, destination etc) to be read from a configuration file at runtime. This would be very cool, since the user could change the logging level in response to problem reports etc.
2. On Win32, writes to files are not atomic by default when different processes are modifying the same file same. Perhaps build in support for fcntl locks so different instances of an app can log to the same destination? I don't know if this feature is already designed in...


John Torjo said...

1. I don't natively allow for config files. However, you can certainly implement this on top of the library.

As a side-note, take a look at formatter::named_spacer and destination::named - allows you to select at runtime destinations and how the log message is to look like.

Reading the log levels from a config file should be as simple as possible.

2. you can take a look at destination::file - it's a simple destination that allows writing to an ofstream (file). You can create your own destination::fnctl_file, which can support what you want
(the lib is extensible :P )

John Torjo said...

For Scott:

First, sorry for the late reply. There's no easy answer to this.

I don't think using the application's settings is the way to go.

The library is separate from the application. First of all, the application can have a certain set of logs, logging syntax, etc., and your library can have a different set of logs/logging syntax/etc.

The simplest case: the application uses levels, but your library doesn't. There's no point in locking you into a framework if you don't need it.

So what I suggest is, from your library, to allow a function that configures your logs.

Does that make sense?


Amit said...


What I want to do is to create my own custom level (e.g. for a webserver, levels could be "error", "session, "request") and some custom tags ("ip", "user_agent").

Further, I would like a way to specify the level at runtime, perhaps when the server starts or by calling a function. So normally, I would set the level to "session", but if I am investigating a problem, I could set it to "request".

What do you recommend as the best way to do this? I have read the dcomentation, but am having trouble absorbing everything since everything seems to be controlled by macros :(


John Torjo said...

Hi Amit,

Side-note - I've been working on making the lib less dependent on macros.

Yes, you can create your own levels. From what I understand, you don't actually want levels, you want some sort of flag that is just on or off.
If for a given value, the flag matches that value, you want to do logging.

First, you can create your own filter class to do whatever you want.

In your case, I guess you want something like this:

struct flag_filter {
int flag;
flag_filter(int flag = 0) : flag(flag) {}
bool is_enabled(int f) { return f == flag; }

// in your code, some constants
const int request = 1;
const int session = 2;
const int answer = 3;

// your macro

... whatever log_type might be
BOOST_DEFINE_LOG(g_l, log_type)
BOOST_DEFINE_LOG_FILTER(g_log_filter, flag_filter )

#define L_(f) BOOST_LOG_USE_LOG_IF_FILTER(g_l(), g_log_filter()->is_enabled(f) )

// in your code
L_(request) << "new request"...;
L_(session) << "new user" << username << ...
L_(answer) << "answering to user" << ...

Hope this makes sense...


bgshawn said...
This comment has been removed by the author.
bgshawn said...

Hi John,

Was thinking that the most difficult debugging is segmentation fault. Even using GDB tool would take sometime to find out the line of code that caused the core dump. It would be nice to be able to handle the segmentation fault signal in the program, then call a Boost Logging function that will print the filename and line of code that was executed before the call of this function. That will save many developers a lot of time trying to debug from GDB.


John Torjo said...

Hi Shawn,

I certainly agree with you. However, I'm really no expert at Linux/ catching signals/etc.

If you'd like to provide the code for catching the segmentation fault signal and getting info on it (eventually logging it), I'd be happy to include it in the lib ;)


Vladimir said...

Hi John,

It would be great to have a rotating feature in Logging library. When a log size will exceed maximim size, logger should open new log. This feature is very necessary for me.

Digitalghost said...

Hey John,

I haven't looked through the code yet, but going through the documentation, it doesn't look like ther is an easy way to write the data out to a buffer in memory. This is an important feature for me because the machine that is doing the logging has to pass memory to another machine that has a console. It might be possible to pull it off using string streams or something, but I have to be careful with overhead. It would be ideal if there was a buffered output that would fill a circular buffer that can be emptied periodically (boost has a circular buffer class now :-) ).

jasedit said...

Is there any method by which you can use a logger/filter with levels without using the macro?

I've got a macro currently defined that takes an argument for the logger level that reads like this:

#define LOG(lvl) BOOST_LOG_USE_LOG_IF_LEVEL(g_l(), g_log_filter(), lvl)

The problem is that a user wants to make a statement along the lines of

LOG(isSuccess ? debug : error) << "Function result: " << isSuccess ? "success" : "failure";

With macros, this configuration cannot work, since the macro uses the argument verbatim, instead of evaluating it. Any suggestions?

Norm said...

The file "out.txt" is always
created when creating a boost logger:

#define L_(lvl) BOOST_LOG_USE_LOG_IF_LEVEL( g_l(), g_log_level(), lvl )
BOOST_DEFINE_LOG_FILTER(g_log_level, boost::logging::level::holder )
BOOST_DEFINE_LOG(g_l, logger_type)

How do we avoid creating the empty file "out.txt"?

Thank you.

jowdjbrown said...

Do you have a feature you can't live without? You can drop me an email, or drop a comment here - and other people can share their thoughts as elo