|  | 
#include <boost/log/attributes/attribute.hpp> #include <boost/log/attributes/attribute_cast.hpp> #include <boost/log/attributes/attribute_value.hpp>
        All attributes in the library are implemented using the pimpl
        idiom, or more specifically - shared pimpl idiom. Every attribute
        provides an interface class which derives from the attribute
        class and an implementation class that derives from impl.
        The interface class only holds a reference counted pointer to the actual
        implementation of the attribute; this pointer is a member of the attribute class, so derived interface
        classes don't have any data members. When the interface class is default
        constructed, it creates the corresponding implementation object and initializes
        the attribute base class
        with a pointer to the implementation. Therefore the pimpl nature of attributes
        is transparent for users in a typical workflow.
      
        The shared pimpl design comes significant in a few cases though. One such
        case is copying the attribute. The copy operation is shallow, so multiple
        interface objects may refer to a single implementation object. There is no
        way to deep copy an attribute. Another case is default construction of attribute which creates an empty
        object that does not refer to an implementation. Attributes in such empty
        state should not be passed to the library but can be useful in some cases,
        e.g. when a delayed variable initialization is needed.
      
        It is possible to upcast the attribute interface from attribute
        to the actual interface class. To do this one has to apply attribute_cast:
      
logging::attribute attr = ...; attrs::constant< int > const_attr = logging::attribute_cast< attrs::constant< int > >(attr);
        In this example, the cast will succeed (i.e. the const_attr
        will be non-empty) if the attribute attr
        was originally created as attrs::constant< int >. Since all data is stored in the implementation
        object, no data is lost in the casting process.
      
        The main purpose of attributes is to generate attribute values. Values are
        semantically distinct from the attributes. Such separation allows implementing
        attributes that can return different values at different time points (like
        clock-related attributes, for example) and, on the other hand, allows using
        different values of the same attribute independently. The attribute
        interface has a method named get_value
        that returns the actual attribute value. Attribute values are also implemented
        using the shared pimpl approach, the interface class is attribute_value
        and implementation classes derive from impl.
      
The attribute value object is mostly intended to store the actual attribute value and implement type dispatching in order to be able to extract the stored value. One should not confuse the attribute value object type and the stored value type. The former is in most cases not needed by users and provides type erasure, but the latter is needed to be able to extract the value. For brevity we call the stored attribute value type simply the attribute value type in this documentation.
#include <boost/log/attributes/constant.hpp>
          The most simple and frequently used attribute type is a constant value
          of some type. This kind of attribute is implemented with the constant class template.
          The template is parametrized with the attribute value type. The constant
          value should be passed to the attribute constructor. Here is an example:
        
void foo() { src::logger lg; // Register a constant attribute that always yields value -5 lg.add_attribute("MyInteger", attrs::constant< int >(-5)); // Register another constant attribute. Make it a string this time. lg.add_attribute("MyString", attrs::constant< std::string >("Hello world!")); // There is also a convenience generator function. "MyInteger2" is constant< int > here. lg.add_attribute("MyInteger2", attrs::make_constant(10)); }
That's it, there's nothing much you can do with a constant attribute. Constants are very useful when one wants to highlight some log records or just pass some data to a sink backend (e.g. pass statistical parameters to the collector).
#include <boost/log/attributes/mutable_constant.hpp>
          This kind of attribute is an extension for the constant
          attribute. In addition to being able to store some value, the mutable_constant
          class template has two distinctions:
        
          In order to change the stored value of the attribute, one must call the
          set method:
        
void foo() { src::logger lg; // Register a mutable constant attribute that always yields value -5 attrs::mutable_constant< int > attr(-5); lg.add_attribute("MyInteger", attr); BOOST_LOG(lg) << "This record has MyInteger == -5"; // Change the attribute value attr.set(100); BOOST_LOG(lg) << "This record has MyInteger == 100"; }
          In multithreaded applications the set
          method calls must be serialized with the get_value
          calls (which, generally speaking, happen on every log record being made).
          By default mutable_constant
          does not serialize calls in any way, assuming that the user will do so
          externally. However, the mutable_constant
          template provides three additional template arguments: synchronization
          primitive type, scoped exclusive lock type and scoped shareable lock type.
          If a synchronization primitive type is specified, the scoped exclusive
          lock type is a mandatory parameter. If the scoped shareable lock type is
          not specified, the attribute will fall back to the exclusive lock instead
          of shared locks. For example:
        
// This mutable constant will always lock exclusively // either for reading or storing the value typedef attrs::mutable_constant< int, // attribute value type boost::mutex, // synchronization primitive boost::lock_guard< boost::mutex > // exclusive lock type > exclusive_mc; exclusive_mc my_int1(10); // This mutable constant will use shared clocking for reading the value // and exclusive locking for storing typedef attrs::mutable_constant< int, // attribute value type boost::shared_mutex, // synchronization primitive boost::unique_lock< boost::shared_mutex >, // exclusive lock type boost::shared_lock< boost::shared_mutex > // shared lock type > shared_mc; shared_mc my_int2(20); BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, src::logger_mt) { src::logger_mt lg; lg.add_attribute("MyInteger1", my_int1); lg.add_attribute("MyInteger2", my_int2); return lg; } void foo() { src::logger_mt& lg = get_my_logger(); // This is safe, even if executed in multiple threads my_int1.set(200); BOOST_LOG(lg) << "This record has MyInteger1 == 200"; my_int2.set(300); BOOST_LOG(lg) << "This record has MyInteger2 == 300"; }
Mutable constants are often used as auxiliary attributes inside loggers to store attributes that may change on some events. As opposed to regular constants, which would require re-registering in case of value modification, mutable constants allow modifying the value in-place.
#include <boost/log/attributes/counter.hpp>
          Counters are one of the simplest attributes that generate a new value each
          time requested. Counters are often used to identify log records or to count
          some events, e.g. accepted network connections. The class template counter provides such
          functionality. This template is parametrized with the counter value type,
          which should support arithmetic operations, such as operator
          + and operator
          -. The counter attribute allows
          specification of the initial value and step (which can be negative) on
          construction.
        
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, src::logger_mt) { src::logger_mt lg; // This counter will count lines, starting from 0 lg.add_attribute("LineCounter", attrs::counter< unsigned int >()); // This counter will count backwards, starting from 100 with step -5 lg.add_attribute("CountDown", attrs::counter< int >(100, -5)); return lg; } void foo() { src::logger_mt& lg = get_my_logger(); BOOST_LOG(lg) << "This record has LineCounter == 0, CountDown == 100"; BOOST_LOG(lg) << "This record has LineCounter == 1, CountDown == 95"; BOOST_LOG(lg) << "This record has LineCounter == 2, CountDown == 90"; }
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| 
            Don't expect that the log records with the  | 
#include <boost/log/attributes/clock.hpp>
          One of the "must-have" features of any logging library is support
          for attaching a time stamp to every log record. The library provides two
          attributes for this purpose: utc_clock
          and local_clock. The former
          returns the current UTC time and the latter returns the current local time.
          In either case the returned time stamp is acquired with the maximum precision
          for the target platform. The attribute value is boost::posix_time::ptime
          (see Boost.DateTime).
          The usage is quite straightforward:
        
BOOST_LOG_DECLARE_GLOBAL_LOGGER(my_logger, src::logger_mt) void foo() { logging::core::get()->add_global_attribute( "TimeStamp", attrs::local_clock()); // Now every log record ever made will have a time stamp attached src::logger_mt& lg = get_my_logger(); BOOST_LOG(lg) << "This record has a time stamp"; }
#include <boost/log/attributes/timer.hpp>
          The timer attribute
          is very useful when there is a need to estimate the duration of some prolonged
          process. The attribute returns the time elapsed since the attribute construction.
          The attribute value type is boost::posix_time::ptime::time_duration_type
          (see Boost.DateTime).
        
// The class represents a single peer-to-peer connection class network_connection { src::logger m_logger; public: network_connection() { m_logger.add_attribute("Duration", attrs::timer()); BOOST_LOG(m_logger) << "Connection established"; } ~network_connection() { // This log record will show the whole life time duration of the connection BOOST_LOG(m_logger) << "Connection closed"; } };
The attribute provides high resolution of the time estimation and can even be used as a simple in-place performance profiling tool.
| ![[Tip]](../../../../../../doc/src/images/tip.png) | Tip | 
|---|---|
| 
            The  | 
#include <boost/log/attributes/named_scope.hpp> // Supporting headers #include <boost/log/support/exception.hpp>
          The logging library supports maintaining scope stack tracking during the
          application's execution. This stack may either be written to log or be
          used for other needs (for example, to save the exact call sequence that
          led to an exception when throwing one). Each stack element contains the
          following information (see the named_scope_entry
          structure template definition):
        
__FILE__
              macro expansion. Like the scope name, the file name must
              be a constant string literal.
            __LINE__ macro expansion.
            
          The scope stack is implemented as a thread-specific global storage internally.
          There is the named_scope
          attribute that allows hooking this stack into the logging pipeline. This
          attribute generates value of the nested type named_scope::scope_stack
          which is the instance of the scope stack. The attribute can be registered
          in the following way:
        
logging::core::get()->add_global_attribute("Scope", attrs::named_scope());
Note that it is perfectly valid to register the attribute globally because the scope stack is thread-local anyway. This will also implicitly add scope tracking to all threads of the application, which is often exactly what is needed.
          Now we can mark execution scopes with the macros BOOST_LOG_FUNCTION
          and BOOST_LOG_NAMED_SCOPE
          (the latter accepts the scope name as its argument). These macros automatically
          add source position information to each scope entry. An example follows:
        
void foo(int n) { // Mark the scope of the function foo BOOST_LOG_FUNCTION(); switch (n) { case 0: { // Mark the current scope BOOST_LOG_NAMED_SCOPE("case 0"); BOOST_LOG(lg) << "Some log record"; bar(); // call some function } break; case 1: { // Mark the current scope BOOST_LOG_NAMED_SCOPE("case 1"); BOOST_LOG(lg) << "Some log record"; bar(); // call some function } break; default: { // Mark the current scope BOOST_LOG_NAMED_SCOPE("default"); BOOST_LOG(lg) << "Some log record"; bar(); // call some function } break; } }
          After executing foo we
          will be able to see in the log that the bar
          function was called from foo
          and, more precisely, from the case statement that corresponds to the value
          of n. This may be very
          useful when tracking down subtle bugs that show up only when bar is called from a specific location
          (e.g. if bar is being passed
          invalid arguments in that particular location).
        
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| 
            The  | 
Another good use case is attaching the scope stack information to an exception. With the help of Boost.Exception, this is possible:
void bar(int x) { BOOST_LOG_FUNCTION(); if (x < 0) { // Attach a copy of the current scope stack to the exception throw boost::enable_error_info(std::range_error("x must not be negative")) << logging::current_scope(); } } void foo() { BOOST_LOG_FUNCTION(); try { bar(-1); } catch (std::range_error& e) { // Acquire the scope stack from the exception object BOOST_LOG(lg) << "bar call failed: " << e.what() << ", scopes stack:\n" << *boost::get_error_info< logging::current_scope_info >(e); } }
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| In order this code to compile, the Boost.Exception support header has to be included. | 
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| 
            We do not inject the  | 
#include <boost/log/attributes/current_process_id.hpp>
          It is often useful to know the process identifier that produces the log,
          especially if the log can eventually combine the output of different processes.
          The current_process_id
          attribute is a constant that formats into the current process identifier.
          The value type of the attribute can be determined by the current_process_id::value_type typedef.
        
void foo() { logging::core::get()->add_global_attribute( "ProcessID", attrs::current_process_id()); }
#include <boost/log/attributes/current_process_name.hpp>
          The current_process_name
          produces std::string values with the executable name
          of the current process.
        
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| This attribute is not universally portable, although Windows, Linux and OS X are supported. The attribute may work on other POSIX systems as well, but it was not tested. If the process name cannot be obtained the attribute will generate a string with the process id. | 
void foo() { logging::core::get()->add_global_attribute( "Process", attrs::current_process_name()); }
#include <boost/log/attributes/current_thread_id.hpp>
          Multithreaded builds of the library also support the current_thread_id
          attribute with value type current_thread_id::value_type.
          The attribute will generate values specific to the calling thread. The
          usage is similar to the process id.
        
void foo() { logging::core::get()->add_global_attribute( "ThreadID", attrs::current_thread_id()); }
| ![[Tip]](../../../../../../doc/src/images/tip.png) | Tip | 
|---|---|
| You may have noticed that the attribute is registered globally. This will not result in all threads having the same ThreadID in log records as the attribute will always return a thread-specific value. The additional benefit is that you don't have to do a thing in the thread initialization routines to have the thread-specific attribute value in log records. | 
#include <boost/log/attributes/function.hpp>
          This attribute is a simple wrapper around a user-defined function object.
          Each attempt to acquire the attribute value results in the function object
          call. The result of the call is returned as the attribute value (this implies
          that the function must not return void).
          The function object attribute can be constructed with the make_function helper function, like this:
        
void foo() { logging::core::get()->add_global_attribute("MyRandomAttr", attrs::make_function(&std::rand)); }
Auto-generated function objects, like the ones defined in Boost.Bind or STL, are also supported.
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| 
            Some deficient compilers may not support  | 
#include <boost/log/attributes/attribute_name.hpp>
            Attribute names are represented with attribute_name
            objects which are used as keys in associative containers of attributes
            used by the library. The attribute_name
            object can be created from a string, so most of the time its use is transparent.
          
            The name is not stored as a string within the attribute_name
            object. Instead, a process-wide unique identifier is generated and associated
            with the particular name. This association is preserved until the process
            termination, so every time the attribute_name
            object is created for the same name it obtains the same identifier. The
            association is not stable across the different runs of the application
            though.
          
| ![[Warning]](../../../../../../doc/src/images/warning.png) | Warning | 
|---|---|
| 
              Since the association between string names and identifiers involves
              some state allocation, it is not advised to use externally provided
              or known to be changing strings for attribute names. Even if the name
              is not used in any log records, the association is preserved anyway.
              Continuously constructing  | 
Working with identifiers is much more efficient than with strings. For example, copying does not involve dynamic memory allocation and comparison operators are very lightweight. On the other hand, it is easy to get a human-readable attribute name for presentation, if needed.
            The attribute_name
            class supports an empty (uninitialized) state when default constructed.
            In this state the name object is not equal to any other initialized name
            object. Uninitialized attribute names should not be passed to the library
            but can be useful in some contexts (e.g. when a delayed initialization
            is desired).
          
#include <boost/log/attributes/attribute_set.hpp>
            Attribute set is an unordered associative container that maps attribute
            names to attributes.
            It is used in loggers and
            the logging core to store
            source-specific, thread-specific and global attributes. The interface
            is very similar to STL associative containers and is described in the
            attribute_set
            class reference.
          
#include <boost/log/attributes/attribute_value_set.hpp>
            Attribute value set is an unordered associative container that maps
            attribute
            names to attribute values.
            This container is used in log records
            to represent attribute values. Unlike conventional containers, attribute_value_set
            does not support removing or modifying elements after being inserted.
            This warrants that the attribute values that participated filtering will
            not disappear from the log record in the middle of the processing.
          
            Additionally, the set can be constructed from three attribute
            sets, which are interpreted as the sets of source-specific, thread-specific
            and global attributes. The constructor adopts attribute values from the
            three attribute sets into a single set of attribute values. After construction,
            attribute_value_set
            is considered to be in an unfrozen state. This means that the container
            may keep references to the elements of the attribute sets used as the
            source for the value set construction. While in this state, neither the
            attribute sets nor the value set must not be modified in any way as this
            may make the value set corrupted. The value set can be used for reading
            in this state, its lookup operations will perform as usual. The value
            set can be frozen by calling the freeze
            method; the set will no longer be attached to the original attribute
            sets and will be available for further insertions after this call. The
            library will ensure that the value set is always frozen when a log record
            is returned from the logging core; the set is not
            frozen during filtering though.
          
| ![[Tip]](../../../../../../doc/src/images/tip.png) | Tip | 
|---|---|
| In the unfrozen state the value set may not have all attribute values acquired from the attributes. It will only acquire the values as requested by filters. After freezing the container has all attribute values. This transition allows to optimize the library so that attribute values are only acquired when needed. | 
            For further details on the container interface please consult the attribute_value_set
            reference.
          
Since attribute values do not expose the stored value in the interface, an API is needed to acquire the stored value. The library provides two APIs for this purpose: value visitation and extraction.
#include <boost/log/attributes/value_visitation_fwd.hpp> #include <boost/log/attributes/value_visitation.hpp>
Attribute value visitation implements the visitor design pattern, hence the naming. The user has to provide a unary function object (a visitor) which will be invoked on the stored attribute value. The caller also has to provide the expected type or set of possible types of the stored value. Obviously, the visitor must be capable of receiving an argument of the expected type. Visitation will only succeed if the stored type matches the expectation.
              In order to apply the visitor, one should call the visit function on
              the attribute value. Let's see an example:
            
// Our attribute value visitor struct print_visitor { typedef void result_type; result_type operator() (int val) const { std::cout << "Visited value is int: " << val << std::endl; } result_type operator() (std::string const& val) const { std::cout << "Visited value is string: " << val << std::endl; } }; void print_value(logging::attribute_value const& attr) { // Define the set of expected types of the stored value typedef boost::mpl::vector< int, std::string > types; // Apply our visitor logging::visitation_result result = logging::visit< types >(attr, print_visitor()); // Check the result if (result) std::cout << "Visitation succeeded" << std::endl; else std::cout << "Visitation failed" << std::endl; }
              In this example we print the stored attribute value in our print_visitor. We expect the attribute
              value to have either int
              or std::string stored type; only in this
              case the visitor will be invoked and the visitation result will be
              positive. In case of failure the visitation_result
              class provides additional information on the failure reason. The class
              has the method named code
              which returns visitation error code. The following error codes are
              possible:
            
ok - visitation
                  succeeded, the visitor has been invoked; visitation result is positive
                  when this code is used
                value_not_found
                  - visitation failed because the requested value was not found;
                  this code is used when visitation is applied to a log record or
                  a set of attribute values rather than a single value
                value_has_invalid_type
                  - visitation failed because the value has type differing from any
                  of the expected types
                
              By default the visitor function result is ignored but it is possible
              to obtain it. To do this one should use a special save_result wrapper
              for the visitor; the wrapper will save the visitor resulting value
              into an external variable captured by reference. The visitor result
              is initialized when the returned visitation_result
              is positive. See the following example where we compute the hash value
              on the stored value.
            
struct hash_visitor { typedef std::size_t result_type; result_type operator() (int val) const { std::size_t h = val; h = (h << 15) + h; h ^= (h >> 6) + (h << 7); return h; } result_type operator() (std::string const& val) const { std::size_t h = 0; for (std::string::const_iterator it = val.begin(), end = val.end(); it != end; ++it) h += *it; h = (h << 15) + h; h ^= (h >> 6) + (h << 7); return h; } }; void hash_value(logging::attribute_value const& attr) { // Define the set of expected types of the stored value typedef boost::mpl::vector< int, std::string > types; // Apply our visitor std::size_t h = 0; logging::visitation_result result = logging::visit< types >(attr, logging::save_result(hash_visitor(), h)); // Check the result if (result) std::cout << "Visitation succeeded, hash value: " << h << std::endl; else std::cout << "Visitation failed" << std::endl; }
| ![[Tip]](../../../../../../doc/src/images/tip.png) | Tip | 
|---|---|
| 
                When there is no default state for the visitor result it is convenient
                to use Boost.Optional
                to wrap the returned value. The  | 
              As it has been mentioned, visitation can also be applied to log records
              and attribute value sets. The syntax is the same, except that the attribute
              name also has to be specified. The visit algorithm will
              try to find the attribute value by name and then apply the visitor
              to the found element.
            
void hash_value(logging::record_view const& rec, logging::attribute_name name) { // Define the set of expected types of the stored value typedef boost::mpl::vector< int, std::string > types; // Apply our visitor std::size_t h = 0; logging::visitation_result result = logging::visit< types >(name, rec, logging::save_result(hash_visitor(), h)); // Check the result if (result) std::cout << "Visitation succeeded, hash value: " << h << std::endl; else std::cout << "Visitation failed" << std::endl; }
              Also, for convenience attribute_value
              has the method named visit
              with the same meaning as the free function applied to the attribute
              value.
            
#include <boost/log/attributes/value_extraction_fwd.hpp> #include <boost/log/attributes/value_extraction.hpp>
Attribute value extraction API allows to acquire a reference to the stored value. It does not require a visitor function object, but the user still has to provide the expected type or a set of types the stored value may have.
void print_value(logging::attribute_value const& attr) { // Extract a reference to the stored value logging::value_ref< int > val = logging::extract< int >(attr); // Check the result if (val) std::cout << "Extraction succeeded: " << val.get() << std::endl; else std::cout << "Extraction failed" << std::endl; }
              In this example we expect the attribute value to have the stored type
              int. The extract
              function attempts to extract a reference to the stored value and returns
              the filled value_ref object if succeeded.
            
Value extraction can also be used with a set of expected stored types. The following code snippet demonstrates this:
void print_value_multiple_types(logging::attribute_value const& attr) { // Define the set of expected types of the stored value typedef boost::mpl::vector< int, std::string > types; // Extract a reference to the stored value logging::value_ref< types > val = logging::extract< types >(attr); // Check the result if (val) { std::cout << "Extraction succeeded" << std::endl; switch (val.which()) { case 0: std::cout << "int: " << val.get< int >() << std::endl; break; case 1: std::cout << "string: " << val.get< std::string >() << std::endl; break; } } else std::cout << "Extraction failed" << std::endl; }
              Notice that we used which
              method of the returned reference to dispatch between possible types.
              The method returns the index of the type in the types
              sequence. Also note that the get
              method now accepts an explicit template parameter to select the reference
              type to acquire; naturally, this type must correspond to the actual
              referred type, which is warranted by the switch/case statement in our
              case.
            
              Value visitation is also supported by the value_ref object. Here is
              how we compute a hash value from the extracted value:
            
struct hash_visitor { typedef std::size_t result_type; result_type operator() (int val) const { std::size_t h = val; h = (h << 15) + h; h ^= (h >> 6) + (h << 7); return h; } result_type operator() (std::string const& val) const { std::size_t h = 0; for (std::string::const_iterator it = val.begin(), end = val.end(); it != end; ++it) h += *it; h = (h << 15) + h; h ^= (h >> 6) + (h << 7); return h; } }; void hash_value(logging::attribute_value const& attr) { // Define the set of expected types of the stored value typedef boost::mpl::vector< int, std::string > types; // Extract the stored value logging::value_ref< types > val = logging::extract< types >(attr); // Check the result if (val) std::cout << "Extraction succeeded, hash value: " << val.apply_visitor(hash_visitor()) << std::endl; else std::cout << "Extraction failed" << std::endl; }
Lastly, like with value visitation, value extraction can also be applied to log records and attribute value sets.
void hash_value(logging::record_view const& rec, logging::attribute_name name) { // Define the set of expected types of the stored value typedef boost::mpl::vector< int, std::string > types; // Extract the stored value logging::value_ref< types > val = logging::extract< types >(name, rec); // Check the result if (val) std::cout << "Extraction succeeded, hash value: " << val.apply_visitor(hash_visitor()) << std::endl; else std::cout << "Extraction failed" << std::endl; }
              In addition the library provides two special variants of the extract
              function: extract_or_throw and
              extract_or_default.
              As the naming implies, the functions provide different behavior in
              case if the attribute value cannot be extracted. The former one throws
              an exception if the value cannot be extracted and the latter one returns
              the default value.
            
| ![[Warning]](../../../../../../doc/src/images/warning.png) | Warning | 
|---|---|
| 
                Care must be taken with the  | 
              Similarly to visit,
              the attribute_value
              class has methods named extract,
              extract_or_throw and
              extract_or_default
              with the same meaning as the corresponding free functions applied to
              the attribute value.
            
#include <boost/log/attributes/scoped_attribute.hpp>
Scoped attributes are a powerful mechanism of tagging log records that can be used for different purposes. As the naming implies, scoped attributes are registered in the beginning of a scope and unregistered on the end of the scope. The mechanism includes the following macros:
BOOST_LOG_SCOPED_LOGGER_ATTR(logger, attr_name, attr);BOOST_LOG_SCOPED_THREAD_ATTR(attr_name, attr);
            The first macro registers a source-specific attribute in the logger logger object. The attribute
            name and the attribute itself are given in the attr_name
            and attr arguments. The
            second macro does exactly the same but the attribute is registered for
            the current thread in the logging core (which does not require a logger).
          
| ![[Note]](../../../../../../doc/src/images/note.png) | Note | 
|---|---|
| If an attribute with the same name is already registered in the logger/logging core, the macros won't override the existing attribute and will eventually have no effect. See Rationale for a more detailed explanation of the reasons for such behavior. | 
Usage example follows:
BOOST_LOG_DECLARE_GLOBAL_LOGGER(my_logger, src::logger_mt) void foo() { // This log record will also be marked with the "Tag" attribute, // whenever it is called from the A::bar function. // It will not be marked when called from other places. BOOST_LOG(get_my_logger()) << "A log message from foo"; } struct A { src::logger m_Logger; void bar() { // Set a thread-wide markup tag. // Note the additional parentheses to form a Boost.PP sequence. BOOST_LOG_SCOPED_THREAD_ATTR("Tag", attrs::constant< std::string >("Called from A::bar")); // This log record will be marked BOOST_LOG(m_Logger) << "A log message from A::bar"; foo(); } }; int main(int, char*[]) { src::logger lg; // Let's measure our application run time BOOST_LOG_SCOPED_LOGGER_ATTR(lg, "RunTime", attrs::timer()); // Mark application start. // The "RunTime" attribute should be nearly 0 at this point. BOOST_LOG(lg) << "Application started"; // Note that no other log records are affected by the "RunTime" attribute. foo(); A a; a.bar(); // Mark application ending. // The "RunTime" attribute will show the execution time elapsed. BOOST_LOG(lg) << "Application ended"; return 0; }
It is quite often convenient to mark a group of log records with a constant value in order to be able to filter the records later. The library provides two convenience macros just for this purpose:
BOOST_LOG_SCOPED_LOGGER_TAG(logger, tag_name, tag_value);BOOST_LOG_SCOPED_THREAD_TAG(tag_name, tag_value);
            The macros are effectively wrappers around BOOST_LOG_SCOPED_LOGGER_ATTR
            and BOOST_LOG_SCOPED_THREAD_ATTR,
            respectively. For example, the "Tag" scoped attribute from
            the example above can be registered like this:
          
BOOST_LOG_SCOPED_THREAD_TAG("Tag", "Called from A::bar");
| ![[Warning]](../../../../../../doc/src/images/warning.png) | Warning | 
|---|---|
| When using scoped attributes, make sure that the scoped attribute is not altered in the attribute set in which it was registered. For example, one should not clear or reinstall the attribute set of the logger if there are logger-specific scoped attributes registered in it. Otherwise the program will likely crash. This issue is especially critical in multithreaded application, when one thread may not know whether there are scoped attributes in the logger or there are not. Future releases may solve this limitation but currently the scoped attribute must remain intact until unregistered on leaving the scope. | 
Although the described macros are intended to be the primary interface for the functionality, there is also a C++ interface available. It may be useful if the user decides to develop his own macros that cannot be based on the existing ones.
            Any scoped attribute is attached to a generic sentry object of type
            scoped_attribute. As
            long as the sentry exists, the attribute is registered. There are several
            functions that create sentries for source or thread-specific attributes:
          
// Source-specific scoped attribute registration template< typename LoggerT > [unspecified] add_scoped_logger_attribute( LoggerT& l, attribute_name const& name, attribute const& attr); // Thread-specific scoped attribute registration template< typename CharT > [unspecified] add_scoped_thread_attribute( attribute_name const& name, attribute const& attr);
            An object of the scoped_attribute
            type is able to attach results of each of these functions on its construction.
            For example, BOOST_LOG_SCOPED_LOGGER_ATTR(lg, "RunTime", attrs::timer()) can roughly be expanded to this:
          
attrs::scoped_attribute sentry = attrs::add_scoped_logger_attribute(lg, "RunTime", attrs::timer());