2

I am new to Boost Log, and having troubles doing some pretty simple stuff. I'm trying to create a logger and assign a level to it (such as Warning, Info, Trace, etc.), and filter out(for performance reasons) any logs sent to this logger with a lower level of that assigned to the logger, at the Logging core level, rather than at the sink level. For example (pseudo code):

logger lg;
lg.setLevel(Warn);
BOOST_LOG_TRIVIAL(trace) << "A trace severity message"; // Will be filtered
BOOST_LOG_TRIVIAL(warn) << "A warning severity message"; // Won't be filtered

I'm pretty sure this can be achieved with Boost Log, but for some reason I was not able to do this.

Thanks,

Omer.

omer
  • 1,242
  • 4
  • 18
  • 45

2 Answers2

2

I suggest you use this link as a reference...

Here below a small snippet of my code. In this small snippet I used a sync backend but you're free to use an async one.

log.hpp

#pragma once

#include <boost/log/common.hpp>
#include <boost/log/sources/severity_logger.hpp>

enum class LogSeverity {
    trace, debug, info, warning, error, fatal
};

extern boost::log::sources::severity_logger<LogSeverity> g_logger;

void log_set_filter(LogSeverity level);
void init_logger();

#define LOG_TRACE   BOOST_LOG_SEV(g_logger, LogSeverity::trace)
#define LOG_DEBUG   BOOST_LOG_SEV(g_logger, LogSeverity::debug)
#define LOG_INFO    BOOST_LOG_SEV(g_logger, LogSeverity::info)
#define LOG_WARNING BOOST_LOG_SEV(g_logger, LogSeverity::warning)
#define LOG_ERROR   BOOST_LOG_SEV(g_logger, LogSeverity::error)
#define LOG_FATAL   BOOST_LOG_SEV(g_logger, LogSeverity::fatal)

log.cpp

#include "bumper-common/log.hpp"
#include <boost/log/common.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/core/null_deleter.hpp>
#include <iomanip>
#include <iostream>

using namespace boost::log;

using LogTextSink = sinks::synchronous_sink<sinks::text_ostream_backend>;

LogSeverity g_logLevel = LogSeverity::info;
sources::severity_logger<LogSeverity> g_logger;

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", LogSeverity)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(log_thread_id, "ThreadID", attributes::current_thread_id::value_type)

std::ostream& operator<< (std::ostream& strm, LogSeverity level)
{
    static const std::array<std::string, 6> strings
    {
        std::string{"trace"},
        std::string{"debug"},
        std::string{"info"},
        std::string{"warn"},
        std::string{"error"},
        std::string{"fatal"}
    };

    strm << strings[static_cast< std::size_t >(level)];

    return strm;
}

void init_logger()
{
    boost::shared_ptr<std::ostream> stream{&std::cout,
        boost::null_deleter{}};

    auto loggerSink = boost::make_shared<LogTextSink>();

    add_common_attributes();

    loggerSink->locked_backend()->add_stream(stream);
    loggerSink->locked_backend()->auto_flush(true);

    loggerSink->set_filter(severity >= g_logLevel);

    loggerSink->set_formatter( expressions::stream
        << "[" << expressions::format_date_time(timestamp, "%H:%M:%S.%f") << "] ["
        << std::setw(5) << std::left << severity << "] ["
        << log_thread_id << "] "
        << expressions::smessage
    );

    boost::log::core::get()->add_sink(loggerSink);
}

void log_set_filter(LogSeverity level)
{
    g_logLevel = level;
}

Hope this can help you. I've troubled a lot with this library. So I strongly suggest you read the documentation I've posted before.

Elvis Dukaj
  • 7,142
  • 12
  • 43
  • 85
  • Thanks for the answer! If I understand your code snippet correctly, the level filtering is done at the sink level (loggerSink->set_filter(severity >= g_logLevel);). Is there a way to do this filter at the logging core level? Performance is very critical to me, and I'd like to filter out logs at the earliest state possible. – omer Aug 24 '17 at 10:16
  • @omer I've asked the same question here: https://stackoverflow.com/questions/16102128/how-to-redirect-boost-log-to-file – Elvis Dukaj Aug 24 '17 at 10:58
  • If speed is critical consider to use other logging libraries: https://github.com/gabime/spdlog or similar – Elvis Dukaj Aug 24 '17 at 12:29
  • it's nit the exact same questions, as I'm aiming for creating different loggers with different levels, and in your case all loggers have the same level. Regarding performance, I'm looking for a library with a good balance between feature set and performance, so it doesn't have to be the fastest library, just one that does not require much processing in order to decide whether a log record should be processed or not. – omer Aug 24 '17 at 13:06
  • If you look the reply of my responce you get the answer to your question: `logging::core::get()->set_filter ( logging::trivial::severity >= logging::trivial::info );` – Elvis Dukaj Aug 24 '17 at 14:09
  • Of course, but doesn't it mean that all loggers (log sources) will have the same level? as you call the "set_filter" function fro, the logging core, and hence it is not related to any particular logger. – omer Aug 24 '17 at 14:15
  • @omer sorry maybe I didn't get you... there are two ways: at global level (like in the link I posted) or at sink level like the snippet I posted... – Elvis Dukaj Aug 24 '17 at 14:49
2

There are no filters on the logger (source) level in Boost.Log. You can set filters either globally (in the logging core) or per sink, and log records from all sources will be processed uniformly.

You can implement the behavior you want with use of channels by having each logger assigned a channel and filtering based on channel and severity level.

BOOST_LOG_ATTRIBUTE_KEYWORD(a_severity, "Severity", LogSeverity)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_channel, "Channel", std::string)

typedef sources::severity_channel_logger< LogSeverity, std::string > logger_type;

logger_type lg_a(keywords::channel = "A");
logger_type lg_b(keywords::channel = "B");

core::get()->set_filter
(
    (a_channel == "A" && a_severity >= LogSeverity::info) ||
    (a_channel == "B" && a_severity >= LogSeverity::warning)
);

The library also provides a specialized filter that can be used to simplify this.

auto min_severity = expressions::channel_severity_filter(a_channel, a_severity);
min_severity["A"] = LogSeverity::info;
min_severity["B"] = LogSeverity::warning;

core::get()->set_filter(min_severity);

Note that you are actually not limited to have only one logger per channel - you can create multiple ones and log records from each will be treated the same way.

Andrey Semashev
  • 10,046
  • 1
  • 17
  • 27
  • Thanks for the answer Andrey! Does each record go under substantial processing in the Logging core before getting to the sinks? And does that mean that if I have multiple sinks per channel, the filter above will be applied for each of them, or can I apply it globally to all sinks related to a channel? – omer Aug 25 '17 at 15:15
  • On the filtering stage, there is no record processing except the filters. All other processing, including log message composition, happens after at least one sink accepts the record (i.e. passed filtering for this sink). The global filter is applied first, sink-specific filters are applied next, sequentially. So in my example the filter is global and is applied exactly once per record regardless of the sinks. If you set sink-specific filters then each such filter will be applied once per each record *that passed the global filter*. – Andrey Semashev Aug 26 '17 at 08:58
  • Thanks for the comment! But Andrey, I am afraid that the rich set of features and the flexibility of Boost.Log might result in heavy performance impact (My project involves high rate packet processing, so performance is a crucial requirement). Were there any performance test made or even better yet, performance comparison against other C++ logging frameworks? Thanks in advance! – omer Aug 27 '17 at 11:13
  • I did not perform any comparisons, but you can search the net. There are a few performance tests in the library that you can play with. – Andrey Semashev Aug 27 '17 at 19:09