I'm searching for a logging library in C++ for several days now, but somehow I'm not very happy with the existing solutions like boost logging or Pantheios. Originally I'm a Java Developer. I would like to have a logging library with a logger which behaves more like an object. I would like to do the following things:
Logger(filepath, filename)
log(serverity, message)
method to log different messages in the text fileThe outstanding problem of this features is that I do not know in advance how many of these logging objects will exists or if these files will have the same filepath. Maybe I could handle this with boost but I don't get the example in the "Text multi-file backend" part of the documentation. Especially what will this code snippets from the example do:
Snippet 1.
// Set up the file naming pattern
backend->set_file_name_composer
(
sinks::file::as_file_name_composer(expr::stream << "logs/" << expr::attr< std::string >("RequestID") << ".log")
);
Snippet 2.
// Set the formatter
sink->set_formatter
(
expr::stream
<< "[RequestID: " << expr::attr< std::string >("RequestID")
<< "] " << expr::smessage
);
This code raises 4 question (or issues) in my head:
Maybe my thoughts are too naive. Is there even a way to get something like I mentioned at the beginning of my post?
If you're new with Boost.Log you should read about the library design first, it is quite different from Java. Despite the difference, it is possible to configure the library in a similar way to log4j, and this answer will help to get you started.
Now, to your questions:
- Does that mean that I just have to set the attribute RequestID and than the logger will decide in which file to put the message? How would I do that?
In the particular case of text_multifile_backend
the sink will decide to what file every log record will be written. The set_file_name_composer
call sets a function object that composes the log file name, and as you can see, it involves the RequestID attribute. Naturally, you can use whatever attribute(s) you like, including channels. You should also know that text_multifile_backend
is not the only way (and probably not the most efficient way) to achieve what you want. If the number of different log files is limited, it is typically better to add several text file sinks, one for each file, and set up filtering so that each sink receives its own log records. This approach is described in the answer I linked above.
Regarding adding attributes, there are different ways depending on the use case and the attribute set you want to add it to. In the case of channels, this attribute is automatically provided by the logger, you just create the logger with the channel name and every log record you make through that logger will have it attached as an attribute. The RequestID attribute from the example you pointed to could be added in any possible way. Here are a few common examples:
- Is it even possible with boost to have logging files in different paths?
Of course. As I said, this can be done by adding more than one file sink to the core. By its nature, text_multifile_backend
is already able to write more than one file.
- What will happen if different threads access the same file?
Boost.Log has support for multithreading. On the sinks level, sink frontends implement thread synchronization. For instance, the synchronous_sink
frontend will block contending threads from writing to a single file concurrently. Log records can be written to different sinks concurrently though.
Loggers also have single-threaded and multi-threaded versions, and the latter do additional locking to protect their internal structures from concurrent access. This protection, however, does not extend on sinks (i.e. even if you use an _mt
logger, the sink frontend still has to synchronize threads).
- Will this code in init_logging() effect the application-wide behaviour of the boost logging library? Is this done by some kind of ... global variables?
There are a number of singletons in Boost.Log, yes. Most notably, the logging core, in which you register all sinks and global and thread-specific attributes. Adding a new sink will have effect on the whole application as records from all loggers will start going to that sink (this is why you should generally configure the sink before adding it to the core). Loggers themselves are not related to sinks and in which sink the log records end up is defined solely by filters. But as I mentioned, it is possible to associate loggers and sinks with help of attributes and filters and manage them in a related manner. You will have to write a wrapper class that provides the interface you described and along with Boost.Log logger creates and configures the corresponding sink.