![]() |
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 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.
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 |
|---|---|
Please note the difference between registering several sinks with functions
|
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.