Фильтр Boost.Log для каждого канала с неизвестными названиями - PullRequest
0 голосов
/ 11 апреля 2020

в Boost.Log Я хочу установить фильтры на основе каналов. Используя этот пример я реализовал с помощью text_file_backend. Но в моей программе имена каналов задаются пользователем в качестве входного аргумента. Поэтому я решил реализовать метод, который устанавливает фильтр серьезности для канала.

commons.h

#ifndef COMMONS_H_
#define COMMONS_H_

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sinks/syslog_backend.hpp>

#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/utility/setup/file.hpp>

#include <boost/thread/thread.hpp>


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

enum severity_levels
{
    normal,
    notification,
    warning,
    error,
    critical
};

// Define the attribute keywords
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_levels)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)

typedef expr::channel_severity_filter_actor< std::string, severity_levels > 
min_severity_filter;
typedef src::severity_channel_logger_mt< severity_levels, std::string > 
logger_type_mt;

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, logger_type_mt)

typedef sinks::synchronous_sink< sinks::text_file_backend > File_sink;

#define ADD_LOG(severity, channel, msg, args...) add_log_message(__FILE__, __LINE__, severity, channel, boost::this_thread::get_id(), msg, args)

#define MY_GLOBAL_LOGGER(log_, channel, sv, file, line, thread) BOOST_LOG_CHANNEL_SEV( log_, channel, sv) \
<< boost::log::add_value("Line", line)      \
<< boost::log::add_value("File", file)       \
<< boost::log::add_value("Thread_id", thread)    


std::ostream& operator<< (std::ostream& strm, severity_levels level)
{
    static const char* strings[] =
    {
        "normal",
        "notification",
        "warning",
        "error",
        "critical"
    };

    if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
        strm << strings[level];
    else
        strm << static_cast< int >(level);

    return strm;
}

#endif

Logger.h

#ifndef LOGGER_H_
#define LOGGER_H_

#define BOOST_LOG_DYN_LINK 1

#include <string.h>
#include <ctime>
#include <chrono>

#include "commons.h"

class Logger
{
public:
    static min_severity_filter min_severity;
    static void init_logging()
    {
        logging::add_common_attributes();

        // Create a text file sink
        boost::shared_ptr< sinks::text_file_backend> backend(new sinks::text_file_backend());
        backend->set_file_name_pattern<std::string>("sample_%N.log");
        backend->set_rotation_size(2 * 1024 * 1024);

        boost::shared_ptr< File_sink > sink(new File_sink(backend));

        // Set up where the rotated files will be stored
        init_file_collecting <File_sink>(sink);
        sink->set_formatter(&file_log_formatter);
        sink->locked_backend()->scan_for_files();
        logging::core::get()->add_sink(sink);   
        logging::core::get()->set_filter(min_severity || severity >= normal);
    }

    template <typename T>
    static void init_file_collecting(boost::shared_ptr< T > sink)
    {
        sink->locked_backend()->set_file_collector(sinks::file::make_collector(
            keywords::target = "logs",                      /*< the target directory >*/
            keywords::max_size = 64 * 1024 * 1024,          /*< maximum total size of the stored files, in bytes >*/
            keywords::min_free_space = 100 * 1024 * 1024    /*< minimum free space on the drive, in bytes >*/
        ));
    }

    static void file_log_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
    {
        // Get the LineID attribute value and put it into the stream
        strm << logging::extract< unsigned int >("LineID", rec) << ": ";

        // TimeStamp
        strm << "[";

        strm << logging::extract<boost::posix_time::ptime>("TimeStamp", rec);
        strm << "]";

        // thread id
        strm << "[" << logging::extract< boost::thread::id >("Thread_id", rec) << "] ";
        strm << "[" << rec[channel] << "] ";
        strm << "[";
        strm << logging::extract< int >("Line", rec) << ", ";
        logging::value_ref< std::string > fullpath = logging::extract< std::string >("File", rec);
        strm << boost::filesystem::path(fullpath.get()).filename().string() << "] ";

        // The same for the severity level.
        // The simplified syntax is possible if attribute keywords are used.
        strm << "<" << rec[severity] << "> ";

        // Finally, put the record message to the stream
        strm << rec[expr::smessage];
    }

    static void set_channel_filter(std::string channel, severity_levels min_level)
    {
        min_severity[channel] = min_level;
        logging::core::get()->set_filter(min_severity);
    }

    static void add_log_message(const char* file, int line, severity_levels severity,
                                    std::string channel, boost::thread::id thread_id,
                                    const char* message, ...)
    {
        char buffer[256];
        va_list ap;
        va_start(ap, message);
        vsnprintf(buffer, 256, message, ap);
        MY_GLOBAL_LOGGER(test_lg::get(), channel, severity, file, line, thread_id) << buffer;
        va_end(ap);
    }
};

#endif
min_severity_filter Logger::min_severity = expr::channel_severity_filter(channel, severity);

На первом этапе программы вызов фильтра init_logging() для всех каналов установлен на нормальное значение.

проблема в том, что когда я вызываю set_channel_filter() с некоторым входом (например, "CHANNEL_1", предупреждение), я ожидаю установки фильтра только для "CHANNEL_1", Но фильтрация установлена ​​для всех возможных каналов. (например, "CHANNEL_2, et c). Когда я добавляю, например," OTHER_CHANNEL "вручную в set_channel_filter (), это работает для него. Я хочу иметь карту c ++, подобную структуре данных, сохраняющую все фильтры серьезности для канала. и каждый раз, когда пользователь вызывает добавьте новый или существующий канал с фильтром, просто измените фильтрацию для этого конкретного канала, а не для всех.

main. cpp

int main()
{
    Logger::init_logging();

    Logger::ADD_LOG(severity_levels::normal, "NETWORK", "a warning message with id %d", 34);
    Logger::ADD_LOG(severity_levels::notification, "NETWORK", "a warning message with id %d", 65);

    Logger::ADD_LOG(severity_levels::notification, "GENERAL", "a notification message with id %d", 12);
    Logger::ADD_LOG(severity_levels::warning, "GENERAL", "a warning message with id %d", 13);

    // Logs in GENERAL category must have severity level of warning or higher in-order to record.
    Logger::set_channel_filter("GENERAL", severity_levels::warning);

    Logger::ADD_LOG(severity_levels::notification, "GENERAL", "a notification message with id %d", 14);
    for (int i = 0; i < 2; i++)
    {
        Logger::ADD_LOG(severity_levels::warning, "GENERAL", "a warning message with id %d", 15);
    }

    Logger::ADD_LOG(severity_levels::normal, "NETWORK", "a warning message with id %d", 34); // Expected to sent to file. but filtered!!
    Logger::ADD_LOG(severity_levels::notification, "NETWORK", "a warning message with id %d", 65); //Also filtered !!
}

1 Ответ

1 голос
/ 21 апреля 2020

Проблема в том, что изначально, в init_logging, вы устанавливаете фильтр следующим образом:

logging::core::get()->set_filter(min_severity || severity >= normal);

На данный момент, min_severity пусто, так как вы не добавили никаких каналов / серьезности пороги. По умолчанию min_severity вернет false, когда канал не найден в отображении канала / серьезности, что означает, что фильтр, который вы здесь установили, действительно severity >= normal.

Позже, когда вы вызываете set_channel_filter, вы добавляете первую запись в отображение min_severity, чтобы она работала для канала «ОБЩИЕ», но не для других. Однако на этот раз вы установили другой фильтр:

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

На этот раз, если min_severity вернет false, запись журнала отбрасывается, что и происходит с двумя последними записями в «СЕТИ» канал. Вам нужно каждый раз устанавливать одно и то же выражение фильтра, чтобы иметь согласованное поведение.

...