c++boostboost-log

Boost.Log, using custom attributes in filename or target value of configuration file


I use Boost.Log in C++ to log, and use configuration files. I added some custom attributes and use them in the configuration file. Here is the registration of the attributes in C++:

namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;

attr1 myProcessID("");
attr2 myThreadID("");
attr3 myThreadIndex("")
attr4 theCorrelationId("");
attr5 theMiscInfos("");

slg_mt::get().add_attribute("Process_ID", myProcessID);
slg_mt::get().add_attribute("Thread_ID", myThreadID);
slg_mt::get().add_attribute("Thread_Index", myThreadIndex);
slg_mt::get().add_attribute("Correlation_ID",  theCorrelationId);
slg_mt::get().add_attribute("MiscInfos",  theMiscInfos);

I define a configuration file, and initialize the configuration file the normal way using boost::log::init_from_stream.

If I use the following configuration, it works well :

[Sinks.TRACE]
Destination="TextFile"

Asynchronous="true"
AutoFlush="true"
Format="[TimeStamp %TimeStamp(format=\"%Y-%m-%d %H:%M:%S.%f\")%][UpTime 
%Uptime(format=\"%O:%M:%S.%f\")%][ProcessID: %Process_ID%][ThreadID: %Thread_ID% %Thread_Index%] %Message%"
Target="C:\BoostLogTrace"
FileName="C:\BoostLogTrace\REST_%N.log"
RotationSize="10485760"
ScanForFiles="Matching"
Filter="%Severity% = trace" 

Everything works well. Except that I would like to use my custom-defined attributes not only in the log entries, but also in the log target and filename.

When I try with the following FileName, it does not work:

FileName="C:\BoostLogTrace\REST_%THREAD_ID%.log"

Is there a way to change the target and filename using custom attributes, with attributes values changing from one log entry to another?

From a higher perspective, I need to log each individual user session in a different log file. How can I do this with Boost.log ?

Thanks!


Solution

  • You can use the multi-file sink backend for logging into separate files. That backend supports generating the filename from the attributes attached to the log record.

    However, it is not supported for initialization from a config file by default. You will have to register a factory for this sink backend and implement configuring this sink from parsed settings. You can reuse the filter and formatter parsers, and also use the formatter parser to construct the filename generator.

    class multifile_factory :
        public logging::sink_factory< char >
    {
    public:
        // Creates the sink with the provided parameters
        boost::shared_ptr< sinks::sink > create_sink(settings_section const& settings)
        {
            boost::shared_ptr< sinks::text_multifile_backend > backend =
                boost::make_shared< sinks::text_multifile_backend >();
    
            // Read sink parameters
            if (boost::optional< std::string > param = settings["FileName"])
            {
                backend->set_file_name_composer(sinks::file::as_file_name_composer(
                    logging::parse_formatter(*param)));
            }
            else
                throw std::runtime_error("No target file name specified in settings");
    
            typedef sinks::synchronous_sink< sinks::text_multifile_backend > sink_t;
            boost::shared_ptr< sink_t > sink = boost::make_shared< sink_t >(backend);
    
            if (boost::optional< std::string > param = settings["Filter"])
                sink->set_filter(logging::parse_filter(*param));
    
            if (boost::optional< std::string > param = settings["Format"])
                sink->set_formatter(logging::parse_formatter(*param));
    
            return sink;
        }
    };
    
    logging::register_sink_factory("TextMultifile",
        boost::make_shared< multifile_factory >());
    

    Then you can use "TextMultifile" as the "Destination" parameter value in the config file. You can parse the config file the same way as before, by calling init_from_stream or init_from_settings, the initialization routine will invoke the factory to create and configure the sink.

    Note that text_multifile_backend does not support log file rotation as it does not track individual files it produces. If you still need file rotation and managing the rotated files then you will have to implement your own sink backend as well.