Как я могу установить уровень серьезности для каждого источника журнала с помощью журнала Boost?

Я новичок в Boost Log, и у меня проблемы с выполнением некоторых довольно простых вещей. Я пытаюсь создать регистратор и назначить ему уровень (например, предупреждение, информация, трассировка и т. Д.) И отфильтровать (по соображениям производительности) любые журналы, отправленные на этот регистратор, с более низким уровнем, чем назначенный для logger, на уровне ядра ведения журнала, а не на уровне приемника. Например (псевдокод):

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

Я почти уверен, что этого можно добиться с помощью Boost Log, но по какой-то причине мне не удалось этого сделать.

Спасибо,

Омер.


person omer    schedule 24.08.2017    source источник


Ответы (2)


На уровне логгера (источника) в Boost.Log нет фильтров. Вы можете установить фильтры либо глобально (в ядре ведения журнала), либо для каждого приемника, и записи журнала из всех источников будут обрабатываться единообразно.

Вы можете реализовать желаемое поведение с помощью каналы, назначив каждому регистратору канал и выполняя фильтрацию на основе канала и уровня серьезности.

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)
);

Библиотека также предоставляет специализированный фильтр, который можно использовать для упрощения этого.

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);

Обратите внимание, что на самом деле вы не ограничены только одним регистратором на канал - вы можете создать несколько, и записи журнала для каждого будут обрабатываться одинаково.

person Andrey Semashev    schedule 25.08.2017
comment
Спасибо за ответ Андрей! Каждая запись подвергается существенной обработке в ядре ведения журнала перед тем, как попасть в приемники? И означает ли это, что если у меня есть несколько приемников на канал, указанный выше фильтр будет применяться для каждого из них, или я могу применить его глобально ко всем приемникам, связанным с каналом? - person omer; 25.08.2017
comment
На этапе фильтрации кроме фильтров нет обработки записей. Вся остальная обработка, включая составление сообщения журнала, происходит после того, как хотя бы один приемник принимает запись (т.е. проходит фильтрацию для этого приемника). Сначала применяется глобальный фильтр, затем последовательно применяются фильтры, специфичные для приемника. Итак, в моем примере фильтр является глобальным и применяется ровно один раз для каждой записи независимо от приемников. Если вы установите фильтры, специфичные для приемника, то каждый такой фильтр будет применяться один раз для каждой записи , прошедшей глобальный фильтр. - person Andrey Semashev; 26.08.2017
comment
Спасибо за комментарий! Но Андрей, я боюсь, что богатый набор функций и гибкость Boost.Log могут сильно повлиять на производительность (мой проект предполагает высокоскоростную обработку пакетов, поэтому производительность является решающим требованием). Были ли сделаны какие-либо тесты производительности или, что еще лучше, сравнение производительности с другими фреймворками C ++ для ведения журналов? Заранее спасибо! - person omer; 27.08.2017
comment
Я не проводил никаких сравнений, но вы можете поискать в сети. В библиотеке есть несколько тестов производительности, с которыми вы можете поиграть. - person Andrey Semashev; 27.08.2017

Я предлагаю вам использовать эту ссылку в качестве справочного материала ...

Вот небольшой фрагмент моего кода. В этом небольшом фрагменте я использовал серверную часть синхронизации, но вы можете использовать асинхронную.

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;
}

Надеюсь, это поможет тебе. Я много возился с этой библиотекой. Поэтому я настоятельно рекомендую вам прочитать документацию, которую я размещал ранее.

person Elvis Dukaj    schedule 24.08.2017
comment
Спасибо за ответ! Если я правильно понимаю ваш фрагмент кода, фильтрация уровня выполняется на уровне приемника (loggerSink- ›set_filter (severity› = g_logLevel);). Есть ли способ сделать этот фильтр на уровне ядра журналирования? Для меня очень важна производительность, и я хотел бы отфильтровать журналы как можно раньше. - person omer; 24.08.2017
comment
@omer Я задал здесь тот же вопрос: stackoverflow.com/questions/16102128/ - person Elvis Dukaj; 24.08.2017
comment
Если скорость имеет решающее значение, подумайте об использовании других библиотек журналов: github.com/gabime/spdlog или аналогичные - person Elvis Dukaj; 24.08.2017
comment
это одни и те же вопросы, так как я стремлюсь создавать разные регистраторы с разными уровнями, а в вашем случае все регистраторы имеют один и тот же уровень. Что касается производительности, я ищу библиотеку с хорошим балансом между набором функций и производительностью, поэтому она не должна быть самой быстрой библиотекой, просто такой, которая не требует большой обработки, чтобы решить, должна ли запись журнала быть обработано или нет. - person omer; 24.08.2017
comment
Если вы посмотрите ответ на мой ответ, вы получите ответ на свой вопрос: logging::core::get()->set_filter ( logging::trivial::severity >= logging::trivial::info ); - person Elvis Dukaj; 24.08.2017
comment
Конечно, но разве это не значит, что все логгеры (источники логов) будут иметь одинаковый уровень? когда вы вызываете функцию set_filter из ядра регистрации, и, следовательно, она не связана с каким-либо конкретным регистратором. - person omer; 24.08.2017
comment
@omer, извините, может быть, я вас не понял ... есть два пути: на глобальном уровне (как в опубликованной мной ссылке) или на уровне раковины, как в опубликованном мной фрагменте ... - person Elvis Dukaj; 24.08.2017