Search code examples
c++loggingboost

C++ Boost Logging - How to throttle sinks? (+Compiler errors)


I have two sinks. Essentially a sinks::asynchronous_sink<text_file_backend> sink and a sinks::asynchronouse_sink<basic_sinks_backend> sink.

Already existing code sinks::basic_sinks_backend inherited into a custom class that redefines consume() into connecting to a website display of the log. Whenever there's a ridiculous amount of errors being fed into the log, the website freezes. I'd rather throw out repeated log error entries to be able to debug than have the entire thing freeze.

I have implemented log throttling on individual components elsewhere in the code but being able to throttle the amount of log messages when it's being flooded at a single point (the sinks) would be ideal. I've thought of using the set_filter() function but that would involve having to modify the log messages to have an attribute of some log counter that I don't want to print out.

How can this be done? Would I create a new class for the text_file_backend to inherit the sinks and implement the log output counter in consume() and start throttling there by preventing the record from being enqueud?

Assuming logCounter is reset to 0 at regular intervals, and throttleRateLimit is some number of log entries in a specified amount of time...

unsigned int logCounter = 0;

void consume(record_view const& rec)
{
   logCounter++;
   if(logCounter < throttleRateLimit){
      text_file_backend::consume();
   }
}
   

Something like this? Or am I looking entirely in the wrong place? The main problem is I'm running into compiler errors before I even get to implementing my solution.

I have the following class:

class log_throttler:
   public boost::log::sinks::text_file_backend
{
public:
   log_throttler(){};
   void consume(boost::log::record_view const& rec, string_type const& formatted_message){
      // I want to try and provide my solution above in this block
   }
};

There's a typedef to make the code in the main block more readable:

typedef boost::log::sinks::asynchronous_sink<log_throttler, boost::log::sinks::bounded_fifo_queue<100,boost::log::sinks::block_on_overflow>> sink_type;

The application builds when sink is created without parameters:

boost::shared_ptr< sink_type > globalSink;

... 

globalSink.reset( new sink_type());

However when adding parameters:

boost::shared_ptr< sink_type > globalSink;

... 

globalSink.reset( new sink_type(boost::log::keywords::file_name = boost::filesystem::path(dir) / boost::filesystem::path("%Y%m%d_%N.log"),
boost::log::keywords::rotation_size = rotation_size,
boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0,0,0)) );

I get an error "no matching function to call to `log_throttler(const boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::file_name..."

"candidate expects 0 arguments, 3 provided"

Is this a fundamental misunderstanding on my part in regards to inheritance? Am I able to use the overloaded constructor from the base class in order to specify the file name of the sink?

I'm not super familiar with Boost. I am referencing this page of the boost doc: https://beta.boost.org/doc/libs/1_82_0/libs/log/doc/html/log/extension.html


Solution

  • I will not comment on how you should implement log record throttling as the details you provide are rather fuzzy. It looks like the implementation you have is rather specific to your code base. If you want an answer regarding this part, please create a new question that is more focused and has more specifics, including minimal code samples.

    I will comment on this one:

    However when adding parameters [...] I get an error "no matching function [...]"

    Boost.Log uses Boost.Parameter to pass named function parameters, including in sink constructors. When you create asynchronous_sink<log_throttler, ...>, the asynchronous_sink frontend forwards the constructor parameters to the backend (i.e. log_throttler) constructor. The compiler error is telling you that your class doesn't have a constructor that accepts the named parameters that were passed by the caller.

    Since your log_throttler backend derives from text_file_backend, it should have a constructor from named parameters, even if only to forward these parameters to the text_file_backend base class' constructor. It can be as simple as this:

    class log_throttler :
        public text_file_backend
    {
    public:
        // ...
    
        template< typename... Args >
        explicit log_throttler(Args&&... args) :
            text_file_backend(std::forward< Args >(args)...)
        {
        }
    
        // ...
    };
    

    Alternatively, you can simply inherit all constructors from the base class like this:

    class log_throttler :
        public text_file_backend
    {
    public:
        // ...
    
        using text_file_backend::text_file_backend;
    
        // ...
    };
    

    Defining your own forwarding constructor may still be beneficial for two reasons though:

    • You are in control which constructors get defined in your log_throttler class. In the latter case, you are inheriting all constructors from the base class (except copy/move, see here for more details), which may change as you update to newer Boost versions.
    • You may want to add your own named parameters to the constructor in the future.