Boost C++ LibrariesSourceForge.net Logo

PrevUpHomeNext

Tutorial

Step 1: Picking sinks
Step 2: Creating loggers and writing logs
Step 3: Getting deeper. Attributes.
Step 4: Formatting
Step 5: Filtering

In this section we shall walk through the essential steps to get started with the library. After reading it you should be able to initialize the library and add logging to your application. The code of this tutorial is also available in a single example resided in the libs/log/examples/basic_usage directory. You may feel free to play around with it, compile and see the result.

The simple form

The first thing you'll have to do is to decide where and how you want logs to be stored. In terms of the library you have to construct logging sinks and register them into the logging core. This should be done only once somewhere in the startup code of your application. The library provides support for several different logging sinks, such as STL streams and syslog where available, and a simplified interface to register them like that:

logging::init_log_to_file("sample.log");

That would implicitly do all necessary actions to enable logging to a file named "sample.log". There are a number of other functions that initialize the library for, e.g. logging to console or syslog or even read the configuration from file. These functions can be combined if needed, for example:

logging::init_log_to_file("sample.log");
logging::init_log_to_console();

This call sequence will enable both logging to a text file and to console, effectively registering two sinks to the logging core.

The advanced form

If you need a more comprehensive control over sinks configuration, you may consider registering them manually. The init_log_to_file function call in the section above can be expanded to this:

// Construct the sink
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
boost::shared_ptr< text_sink > pSink = boost::make_shared< text_sink >();

// Add a stream to write log to
pSink->locked_backend()->add_stream(
    boost::make_shared< std::ofstream >("sample.log"));

// Register the sink in the logging core
logging::core::get()->add_sink(pSink);

Ok, the first thing you may have noticed about sinks is that they are composed of two classes: the frontend and the backend. The frontend (which is the synchronous_sink class template in the snippet above) is responsible for various common tasks for all sinks, such as thread synchronization model and filtering. The backend (the text_ostream_backend class above) implements everything specific to the sink nature, text formatting and writing to a file being in this case. Every log record first gets to the frontend, which decides if it is going to be stored and, if it is, passes the record to the backend. There are a number of both frontends and backends provided by the library out of box that may be used with each other. This approach significantly reduces backends complexity (which is one of the most probable areas of the library extension) and improves code reusability.

The synchronous_sink class template above indicates that the sink is synchronous, that is, it allows several threads to log simultaneously and will block in case of contention. This means that the backend text_ostream_backend need not to worry about multithreading at all. There are two other sink frontends available out of box: unlocked_sink and asynchronous_sink. The unlocked_sink makes no synchronization at all and asynchronous_sink performs writing in a separate thread. The simplified functions like init_log_to_file and init_log_to_console always register a synchronous sink.

The text_ostream_backend class implements storing text records into STL-compatible streams. We have used a file stream above but we could have used any type of stream. For example, adding output to console could look as follows:

// We have to provide an empty deleter to avoid destroying the global stream
boost::shared_ptr< std::ostream > pStream(&std::clog, logging::empty_deleter());
pSink->locked_backend()->add_stream(pStream);

The text_ostream_backend supports adding several streams. In that case its output will be duplicated to all added streams. This may be useful to duplicate output to console and file, since all the filtering, formatting and other overhead the library makes are done only once per record for the sink.

[Note] Note

Please note the difference between registering several sinks with functions init_log_to_* and registering one sink with several target streams. While the former allows to independedly customize output to each stream, the latter would work considerably faster if such customization is not needed.

The last thing worth noting here is that locked_backend member function call to access the sink backend. It is used to get a thread-safe access to the backend and is provided by all sink frontends. This function returns a smart-pointer to the backend and as long as it exists the backend is locked (which means even if another thread tries to log and the log record is passed to the sink, it will not be logged until you release the backend). The only exception is the unlocked_sink frontend which does not synchronize at all and simply returns an unlocked pointer to the backend.


PrevUpHomeNext