#include <boost/log/core/record.hpp>
All the information that the logging library processes is packed into a
single object of type record
(or wrecord
, in the case
of wide character logging), which is a typedef of the basic_record
class template. Namely, log records provide access to attribute values
and log message strings.
The record stores all the associated data in a shared object internally, so record copying and assignment does not involve deep copying attribute values and the message string. It is possible to default-construct a log record, but the constructed record will be in an empty state. In this state the record is mostly unusable, except that it can be compared to other records for equality. Non-empty log records can only be created by the logging core as a result of successful filtering. An empty log record will never compare equal to a non-empty one.
In multithreaded environments, after being constructed a non-empty log
record is considered to be tied to the current thread as it may refer to
some thread-specific resources. In order to pass the record to another
thread the user has to make it thread-independent by calling the detach_from_thread
method. Calling this
method more than once is safe, but subsequent calls will have no effect.
The components of the library are aware of this feature and will call this
method as needed.
The library also provides an opaque record_handle
type. This type is mostly used by the library implementation. It does not
provide any operations on the log record itself, however it contains a
reference to the record shared data. A log record can be constructed from
handle, provided that the handle refers to shared data with the same character
type or is empty.
#include <boost/log/core/core.hpp>
The logging core is a central hub that provides the following facilities:
The logging core is an application-wide singleton, thus every logging source
has access to it. The core instance is accessible with the static method
get
.
void foo() { boost::shared_ptr< logging::core > core = logging::core::get(); // ... }
The core can be used during application initialization and/or termination.
However, in the latter case one has to keep the shared_ptr
to the core instance up until the last point of use of the logging library.
In order to add or remove global or thread-specific attributes to the core
there are corresponding methods: add_global_attribute
,
remove_global_attribute
,
add_thread_attribute
and
remove_thread_attribute
.
Attribute sets provide interface similar to std::map
,
so the add_*
methods accept an attribute name string (key) and a pointer to the attribute
(mapped value) and return a pair of iterator and boolean value, like std::map< ... >::insert
does. The remove_*
methods accept an iterator to a previously
added attribute.
void foo() { boost::shared_ptr< logging::core > core = logging::core::get(); // Add a global attribute std::pair< logging::core::attribute_set_type::iterator, bool > res = core->add_global_attribute("LineID", boost::make_shared< attrs::counter< unsigned int > >()); // ... // Remove the added attribute core->remove_global_attribute(res.first); }
Tip | |
---|---|
It must be said that all methods of logging core are thread-safe in multithreaded environments. However, that may not be true for other components, such as iterators or attribute sets. |
It is possible to acquire a copy of the whole attribute set (global or
thread-specific) or install it into the core. Methods get_global_attributes
,
set_global_attributes
,
get_thread_attributes
and
set_thread_attributes
serve
this purpose.
Warning | |
---|---|
After installing a whole attribute set into the core, all iterators that
were previously returned by the corresponding |
Global filtering is handled by the filter function object, which can be
provided with the set_filter
method. More on creating filters appears in this
section. Here it will suffice to say that the filter accepts a set
of attribute values and returns a boolean value that tells whether a log
record with these attribute values passed filtering or not. The global
filter is applied to every log record made throughout the application,
so it can be used to wipe out excessive log records quickly.
The global filter can be removed by the reset_filter
method. When there is no filter set in the core it is assumed that no records
are filtered away. This is the default after initial construction of the
logging core.
enum severity_level { normal, warning, error }; void foo() { boost::shared_ptr< logging::core > core = logging::core::get(); // Set a global filter so that only error messages are logged core->set_filter(flt::attr< severity_level >("Severity") >= error); // ... }
The core also provides another way to disable logging. By calling the
set_logging_enabled
with
a boolean argument one may completely disable or reenable logging, including
applying filtering. Disabling logging with this method may be more benefical
in terms of application performance than setting a global filter that always
fails.
After global filtering is applied, log sinks step into action. In order
to add and remove sinks the core provides add_sink
and remove_sink
methods.
Both these methods accept a pointer to the sink. The add_sink
will add the sink to the core if it's not added already. The remove_sink
method will seek for the
provided sink in an internal list of previously added sinks and remove
the sink if it finds it. The order in which the core processes sinks internally
is unspecified.
void foo() { boost::shared_ptr< logging::core > core = logging::core::get(); // Set a sink that will write log records to the console boost::shared_ptr< sinks::text_ostream_backend > backend = boost::make_shared< sinks::text_ostream_backend >(); backend->add_stream( boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter())); typedef sinks::unlocked_sink< sinks::text_ostream_backend > sink_t; boost::shared_ptr< sink_t > sink = boost::make_shared< sink_t >(backend); core->add_sink(sink); // ... // Remove the sink core->remove_sink(sink); }
You can read more on the design of sinks in the following sections: Sink Frontends and Sink Backends.
The core provides a way to set up centralized exception handling. If an
exception takes place during filtering or processing in one of the added
sinks, the core will invoke an exception handler if one was provided with
the set_exception_handler
method. An exception handler is a nullary function object that is invoked
from within a catch
clause.
The library provides tools
to simplify exception handlers construction.
Tip | |
---|---|
The exception handler in the logging core is global and thus is intended to perform some common actions on errors. Logging sinks and sources also provide exception handling facilities (see here and here), which can be used to do a finer grained error processing. |
One of the most important functions of the logging core is providing an
entry point for all logging sources to feed log records into. This is done
with the open_record
and
push_record
methods.
The first method is used to initiate the record logging process. It accepts the source-specific set of attributes. The method constructs a common view of attribute values of the three sets of attributes (global, thread-specific and source-specific) and applies filtering. If the filtering succeeded, i.e. at least one sink accepts a record with these attribute values, the method returns a non-empty record object, which can be used to fill in the log record message. Note that at this point attribute values are immutable. If the filtering failed, an empty record object is returned.
When the log source is ready to complete the logging procedure, it has
to call the push_record
method with the record returned by the open_record
method. Note that one should not call push_record
with an empty record. During the call the record will be passed on to the
sinks that accepted it during filtering. This may involve record formatting
and further processing, like storing it into a file or sending it over
the network. After that the record object can be destroyed.
void foo(logging::core::attribute_set_type const& attribs) { boost::shared_ptr< logging::core > core = logging::core::get(); // Attempt to open a record logging::core::record_type rec = core->open_record(attribs); if (rec) { // Ok, the record is accepted. Compose the message now. rec.message() = "Hello, world!"; // Deliver the record to the sinks. core->push_record(rec); } }
All this logic is usually hidden in the loggers and macros provided by the library. However, this may be useful for those developing new log sources.