Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Sink backends

Boost , Chapter 1. Boost.Log v2 , Detailed features description

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext
#include <boost/log/sinks/text_ostream_backend.hpp>

Бэкэнд потока вывода текста является наиболее общим бэкэндом, предоставляемым библиотекой из коробки. Бэкенд реализован в шаблоне классов basic_text_ostream_backend (text_ostream_backend и wtext_ostream_backend для удобства типизированных файлов, предназначенных для узкой и широкой поддержки символов). Он поддерживает форматирование записей журналов в строки и ввод в один или несколько потоков. Каждый прикрепленный поток получает один и тот же результат форматирования, поэтому, если вам нужно по-разному форматировать записи журнала для разных потоков, вам нужно будет создать несколько раковин - каждая со своим собственным форматером.

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

void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a backend and attach a couple of streams to it
    boost::shared_ptr< sinks::text_ostream_backend > backend =
        boost::make_shared< sinks::text_ostream_backend >();
    backend->add_stream(
        boost::shared_ptr< std::ostream >(&std::clog, boost::null_deleter()));
    backend->add_stream(
        boost::shared_ptr< std::ostream >(new std::ofstream("sample.log")));
    // Enable auto-flushing after each log record written
    backend->auto_flush(true);
    // Wrap it into the frontend and register in the core.
    // The backend requires synchronization in the frontend.
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink_t;
    boost::shared_ptr< sink_t > sink(new sink_t(backend));
    core->add_sink(sink);
}

#include <boost/log/sinks/text_file_backend.hpp>

Хотя можно записывать журналы в файлы с text stream backend Библиотека также предлагает специальный фон раковины с расширенным набором функций, подходящих для регистрации на основе файлов. К особенностям относятся:

  • Вращение файла журнала в зависимости от размера и/или времени файла
  • Гибкие имена файлов log
  • Размещение вращаемых файлов в специальное место в файловой системе
  • Удаление самых старых файлов, чтобы освободить больше места в файловой системе

Бэкэнд называется text_file_backend.

[Warning]Warning

Эта раковина использует Boost.Filesystem внутри, что может вызвать проблемы при прекращении процесса. См. здесь для более подробной информации.

File rotation

Вращение файлов осуществляется самим фоном раковины. Структура имени файла и пороги вращения могут быть указаны при построении text_file_backend backend.

void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    boost::shared_ptr< sinks::text_file_backend > backend =
        boost::make_shared< sinks::text_file_backend >(
            keywords::file_name = "file_%5N.log",                                          1
            keywords::rotation_size = 5 * 1024 * 1024,                                     2
            keywords::time_based_rotation = sinks::file::rotation_at_time_point(12, 0, 0)  3
        );
    // Wrap it into the frontend and register in the core.
    // The backend requires synchronization in the frontend.
    typedef sinks::synchronous_sink< sinks::text_file_backend > sink_t;
    boost::shared_ptr< sink_t > sink(new sink_t(backend));
    core->add_sink(sink);
}

1

имя файла

2

поверните файл, достигнув размера 5 МБ.

3

или каждый день, в полдень, в зависимости от того, что наступит раньше

[Note]Note

Размер файла при ротации может быть неточным. Реализация подсчитывает количество символов, записанных в файл, но базовый API может ввести дополнительные вспомогательные данные, что увеличит фактический размер файла журнала на диске. Например, хорошо известно, что операционные системы Windows и DOS имеют особое отношение к символам новой строки. Каждый символ новой строки записывается как последовательность из двух байтов 0x0D 0x0A вместо одной 0x0A. Также известны другие переводы платформенных символов.

Вращение, основанное на времени, не ограничено только временными точками. Есть следующие варианты, доступные из коробки:

  1. Time point rotations: rotation_at_time_point class. This kind of rotation takes place whenever the specified time point is reached. The following variants are available:
    • Every day rotation, at the specified time. This is what was presented in the code snippet above:
      sinks::file::rotation_at_time_point(12, 0, 0)
      
    • Вращение в указанный день каждой недели, в указанное время. Например, это приведет к тому, что ротация файлов будет происходить каждый вторник, в полночь:
      sinks::rotation_at_time_point(date_time::,,) В случае полуночи время может быть опущено:
      sinks::rotation_at_time_point:Tuesday
    • Вращение в указанный день каждого месяца, в указанное время. Например, вот как повернуть файлы на 1-й день каждого месяца:
      sinks::rotation_at_time_point:gregorian::greg_day,,, ,greg_day1>)
  2. Вращение временного интервала: rotation_at_time_interval class. С этим предикатом вращение не связано с какими-либо временными точками и происходит, как только указанный временной интервал с момента предыдущего вращения проходит. Вот как делать вращения каждый час:
    sinks::file::rotation_at_time_interval(posix_time::hours(1)) 

Если ни одно из вышеперечисленных положений не применимо, можно указать свой собственный предикат для временного вращения. Предикат не должен принимать аргументов и возвращать bool (значение true указывает на то, что вращение должно иметь место). Предикат будет вызываться для каждой записи журнала, написанной в файл.

bool is_it_time_to_rotate();
void init_logging()
{
    // ...
    boost::shared_ptr< sinks::text_file_backend > backend =
        boost::make_shared< sinks::text_file_backend >(
            keywords::file_name = "file_%5N.log",
            keywords::time_based_rotation = &is_it_time_to_rotate
        );
    // ...
}
[Note]Note

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

Структура имени файла может содержать несколько wildcards, как вы можете видеть в примере выше. Поддерживаемыми держателями являются:

  • Текущие компоненты даты и времени. Заполнители соответствуют тем, которые указаны в библиотеке Boost.DateTime.
  • Счетчик файлов (%N) с дополнительной спецификацией ширины в формате printf. Файловый счетчик всегда будет десятичным, ноль заполнен до заданной ширины.
  • Знак процента (%%).

Несколько быстрых примеров:

Шаблон

Расширяется до

file_%N.log

file_1.log, file_2.log...

file_001.log, file_002.log

file_20080705.log, file_20080706.log

file_%Y-%m-%d_%H-%M-%S.%N.log

file_2008-07-05_13-44-23.1.log, file_2008-07-06_16-00-10.2.log...

[Important]Important

Хотя все спецификаторы формата Boost.DateTime будут работать, на некоторых из них есть ограничения, если вы намерены сканировать старые файлы журналов. Эта функциональность обсуждается в следующем разделе.

Бэкэнд раковины позволяет подключаться к процессу ротации файлов, чтобы выполнять действия до и после вращения. Это может быть полезно для поддержания действительности файла журнала путем написания заголовков и нижних колонок. Например, мы можем изменить функцию init_logging для записи журналов в файлы XML:

// Complete file sink type
typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink;
void write_header(sinks::text_file_backend::stream_type& file)
{
    file << "<?xml version=\"1.0\"?>\n<log>\n";
}
void write_footer(sinks::text_file_backend::stream_type& file)
{
    file << "</log>\n";
}
void init_logging()
{
    // Create a text file sink
    boost::shared_ptr< file_sink > sink(new file_sink(
        keywords::file_name = "%Y%m%d_%H%M%S_%5N.xml",  1
        keywords::rotation_size = 16384                 2
    ));
    sink->set_formatter
    (
        expr::format("\t<record id=\"%1%\" timestamp=\"%2%\">%3%</record>")
            % expr::attr< unsigned int >("RecordID")
            % expr::attr< boost::posix_time::ptime >("TimeStamp")
            % expr::xml_decor[ expr::stream << expr::smessage ]            3
    );
    // Set header and footer writing functors
    sink->locked_backend()->set_open_handler(&write_header);
    sink->locked_backend()->set_close_handler(&write_footer);
    // Add the sink to the core
    logging::core::get()->add_sink(sink);
}

1

полученный шаблон имени файла

2

размер вращения, в символах

3

лог-сообщение должно быть оформлено, если оно содержит специальные символы

См. полный код .

Наконец, встроенный вентилятор также поддерживает функцию автоматического смыва, как это делает text stream backend.

Managing rotated files

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

void init_file_collecting(boost::shared_ptr< file_sink > sink)
{
    sink->locked_backend()->set_file_collector(sinks::file::make_collector(
        keywords::target = "logs",                      1
        keywords::max_size = 16 * 1024 * 1024,          2
        keywords::min_free_space = 100 * 1024 * 1024,   3
        keywords::max_files = 512                       4
    ));
}

1

целевой каталог

2

максимальный общий размер хранимых файлов в байтах

3

минимальное свободное пространство на диске, в байтах

4

максимальное количество хранимых файлов

Параметры max_size, min_free_space и max_files необязательны, соответствующий порог не будет учитываться, если параметр не указан.

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

[Warning]Warning

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

Коллектор файлов предоставляет еще одну полезную функцию. Предположим, вы запускали приложение 5 раз, и у вас есть 5 файлов журнала в каталоге «логи». Бэкэнд и коллектор файлов обеспечивают метод scan_for_files, который ищет целевой каталог для этих файлов и учитывает их. Поэтому, если речь идет об удалении файлов, эти файлы не забываются. Более того, если шаблон имени файла в бэкэнде включает в себя счетчик файлов, сканирование старых файлов позволяет обновить счетчик до последнего значения. Вот окончательная версия функции init_logging:

void init_logging()
{
    // Create a text file sink
    boost::shared_ptr< file_sink > sink(new file_sink(
        keywords::file_name = "%Y%m%d_%H%M%S_%5N.xml",
        keywords::rotation_size = 16384
    ));
    // Set up where the rotated files will be stored
    init_file_collecting(sink);
    // Upon restart, scan the directory for files matching the file_name pattern
    sink->locked_backend()->scan_for_files();
    sink->set_formatter
    (
        expr::format("\t<record id=\"%1%\" timestamp=\"%2%\">%3%</record>")
            % expr::attr< unsigned int >("RecordID")
            % expr::attr< boost::posix_time::ptime >("TimeStamp")
            % expr::xml_decor[ expr::stream << expr::smessage ]
    );
    // Set header and footer writing functors
    namespace bll = boost::lambda;
    sink->locked_backend()->set_open_handler
    (
        bll::_1 << "<?xml version=\"1.0\"?>\n<log>\n"
    );
    sink->locked_backend()->set_close_handler
    (
        bll::_1 << "</log>\n"
    );
    // Add the sink to the core
    logging::core::get()->add_sink(sink);
}

Существует два метода сканирования файлов: сканирование, которое включает в себя сопоставление имени файла с шаблоном имени файла (по умолчанию) и сканирование, которое предполагает, что все файлы в целевом каталоге являются файлами журнала. В первом случае применяются определенные ограничения для заполнителей, которые могут быть использованы в шаблоне имени файла, в частности, только заполнитель счетчика файлов и эти заполнители заполнителей Boost.DateTime поддерживаются: %, %Y, %, %, %, %, %, %, , %, , %, , %, , %, , , , Последний метод сканирования, в свою очередь, имеет свой недостаток: он не позволяет обновлять счетчик файлов в бэкэнде. Он также считается более опасным, так как может привести к непреднамеренному удалению файла, поэтому будьте осторожны. Способ сканирования всех файлов можно включить, передав его в качестве дополнительного параметра вызову scan_for_files:

// Look for all files in the target directory
backend->scan_for_files(sinks::file::scan_all);
#include <boost/log/sinks/text_multifile_backend.hpp>

Хотя текстовый поток и бэкэнды файлов предназначены для хранения всех записей журнала в одном файле / потоке, этот бэкэнд служит другой цели. Предположим, у нас есть приложение для обработки банковских запросов, и мы хотим, чтобы журналы, связанные с каждым отдельным запросом, были помещены в отдельный файл. Если мы можем связать некоторые атрибуты с идентификатором запроса, то text_multifile_backend backend - это путь.

void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    boost::shared_ptr< sinks::text_multifile_backend > backend =
        boost::make_shared< sinks::text_multifile_backend >();
    // Set up the file naming pattern
    backend->set_file_name_composer
    (
        sinks::file::as_file_name_composer(expr::stream << "logs/" << expr::attr< std::string >("RequestID") << ".log")
    );
    // Wrap it into the frontend and register in the core.
    // The backend requires synchronization in the frontend.
    typedef sinks::synchronous_sink< sinks::text_multifile_backend > sink_t;
    boost::shared_ptr< sink_t > sink(new sink_t(backend));
    // Set the formatter
    sink->set_formatter
    (
        expr::stream
            << "[RequestID: " << expr::attr< std::string >("RequestID")
            << "] " << expr::smessage
    );
    core->add_sink(sink);
}

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

Если по какой-то причине использование форматировщиков не подходит, вы можете предоставить собственное имя файла композитора. Композитор является простым функциональным объектом, который принимает запись журнала в качестве единственного аргумента и возвращает значение text_multifile_backend::path_type type.

[Note]Note

Мультифайловый бэкэнд не знает, будет ли использоваться тот или иной файл. То есть, если запись журнала была записана в файл A, библиотека не может сказать, будет ли больше записей, которые вписываются в файл A или нет. Это делает невозможным вращение файлов и удаление неиспользуемых файлов, чтобы освободить место в файловой системе. Пользователю придется самостоятельно реализовывать такую функциональность.

#include <boost/log/sinks/text_ipc_message_queue_backend.hpp>

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

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

BOOST_LOG_ATTRIBUTE_KEYWORD(a_timestamp, "TimeStamp", attrs::local_clock::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_process_id, "ProcessID", attrs::current_process_id::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_thread_id, "ThreadID", attrs::current_thread_id::value_type)
int main()
{
    try
    {
        typedef logging::ipc::reliable_message_queue queue_t;
        typedef sinks::text_ipc_message_queue_backend< queue_t > backend_t;
        typedef sinks::synchronous_sink< backend_t > sink_t;
        // Create a sink that is associated with the interprocess message queue
        // named "ipc_message_queue".
        boost::shared_ptr< sink_t > sink = boost::make_shared< sink_t >
        (
            keywords::name = logging::ipc::object_name(logging::ipc::object_name::user, "ipc_message_queue"),
            keywords::open_mode = logging::open_mode::open_or_create,
            keywords::capacity = 256,
            keywords::block_size = 1024,
            keywords::overflow_policy = queue_t::block_on_overflow
        );
        // Set the formatter
        sink->set_formatter
        (
            expr::stream << "[" << a_timestamp << "] [" << a_process_id << ":" << a_thread_id << "] " << expr::smessage
        );
        logging::core::get()->add_sink(sink);
        // Add the commonly used attributes, including TimeStamp, ProcessID and ThreadID
        logging::add_common_attributes();
        // Do some logging
        src::logger logger;
        for (unsigned int i = 1; i <= 10; ++i)
        {
            BOOST_LOG(logger) << "Message #" << i;
        }
    }
    catch (std::exception& e)
    {
        std::cout << "Failure: " << e.what() << std::endl;
    }
    return 0;
}

См. полный код .

Такая же очередь между процессами может быть использована для реализации принимающей стороны. Следующий код отображает полученные сообщения журнала на консоли.

int main()
{
    try
    {
        typedef logging::ipc::reliable_message_queue queue_t;
        // Create a message_queue_type object that is associated with the interprocess
        // message queue named "ipc_message_queue".
        queue_t queue
        (
            keywords::name = logging::ipc::object_name(logging::ipc::object_name::user, "ipc_message_queue"),
            keywords::open_mode = logging::open_mode::open_or_create,
            keywords::capacity = 256,
            keywords::block_size = 1024,
            keywords::overflow_policy = queue_t::block_on_overflow
        );
        std::cout << "Viewer process running..." << std::endl;
        // Keep reading log messages from the associated message queue and print them on the console.
        // queue.receive() will block if the queue is empty.
        std::string message;
        while (queue.receive(message) == queue_t::succeeded)
        {
            std::cout << message << std::endl;
            // Clear the buffer for the next message
            message.clear();
        }
    }
    catch (std::exception& e)
    {
        std::cout << "Failure: " << e.what() << std::endl;
    }
    return 0;
}

См. полный код .

#include <boost/log/sinks/syslog_backend.hpp>

Бэкэнд syslog, как и его название, обеспечивает поддержку API syslog, доступного практически на любой UNIX-подобной платформе. В Windows существует по меньшей мере one публичной реализации клиентского API syslog. Однако для обеспечения максимальной гибкости и лучшей переносимости библиотека предлагает встроенную поддержку протокола syslog, описанного в RFC 3164. Таким образом, в Windows поддерживается только встроенная реализация, в то время как в UNIX-подобных системах поддерживаются как встроенные, так и системные реализации на основе API.

Бэкэнд реализован в классе syslog_backend. Бэкэнд поддерживает форматирование записей журналов и, следовательно, требует синхронизации потоков в интерфейсе. Бэкэнд также поддерживает перевод уровня строгости от значений, специфичных для приложения, к значениям, определенным в сислоге. Это достигается с помощью дополнительного функционального объекта, картографа уровня, который получает набор значений атрибутов каждой записи журнала и возвращает соответствующее значение уровня сислога. Это значение используется бэкэндом для построения конечного приоритетного значения записи сислога. Другой компонент приоритетного значения syslog, объект, постоянен для каждого бэкэнд-объекта и может быть указан в аргументах бэкэнд-конструктора.

Картографы уровней могут быть написаны пользователями библиотеки, чтобы наилучшим образом перевести уровни журналов приложений на уровни сислогов. Однако библиотека предоставляет два картографа, которые бы соответствовали этой потребности в очевидных случаях. Шаблон класса direct_severity_mapping обеспечивает способ прямого отображения значений некоторых интегральных атрибутов на уровни сислогов без какого-либо преобразования значений. Шаблон класса custom_severity_mapping добавляет некоторую гибкость и позволяет отображать произвольные значения некоторых атрибутов для уровней сислога.

Один пример лучше тысячи слов.

// Complete sink type
typedef sinks::synchronous_sink< sinks::syslog_backend > sink_t;
void init_native_syslog()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a backend
    boost::shared_ptr< sinks::syslog_backend > backend(new sinks::syslog_backend(
        keywords::facility = sinks::syslog::user,               1
        keywords::use_impl = sinks::syslog::native              2
    ));
    // Set the straightforward level translator for the "Severity" attribute of type int
    backend->set_severity_mapper(sinks::syslog::direct_severity_mapping< int >("Severity"));
    // Wrap it into the frontend and register in the core.
    // The backend requires synchronization in the frontend.
    core->add_sink(boost::make_shared< sink_t >(backend));
}
void init_builtin_syslog()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a new backend
    boost::shared_ptr< sinks::syslog_backend > backend(new sinks::syslog_backend(
        keywords::facility = sinks::syslog::local0,             3
        keywords::use_impl = sinks::syslog::udp_socket_based    4
    ));
    // Setup the target address and port to send syslog messages to
    backend->set_target_address("192.164.1.10", 514);
    // Create and fill in another level translator for "MyLevel" attribute of type string
    sinks::syslog::custom_severity_mapping< std::string > mapping("MyLevel");
    mapping["debug"] = sinks::syslog::debug;
    mapping["normal"] = sinks::syslog::info;
    mapping["warning"] = sinks::syslog::warning;
    mapping["failure"] = sinks::syslog::critical;
    backend->set_severity_mapper(mapping);
    // Wrap it into the frontend and register in the core.
    core->add_sink(boost::make_shared< sink_t >(backend));
}

1

лесозаготовительный комплекс

2

нативный syslog API должен использоваться

3

лесозаготовительный комплекс

4

следует использовать встроенную реализацию на основе сокетов

Обратите внимание, что все константы сислога, а также вытяжки уровней объявлены в вложенном пространстве имен syslog. Библиотека не будет принимать (и не объявлять в бэкэнд-интерфейсе) нативные константы сизлога, которые на самом деле являются макросами.

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

[Tip]Tip

Способ set_target_address также принимает имена DNS, которые он решит для фактического IP-адреса. Эта функция, однако, не доступна в однопоточной сборке.

#include <boost/log/sinks/debug_output_backend.hpp>

У Windows API есть интересная особенность: процесс, запускаемый под отладчиком, способен испускать сообщения, которые будут перехвачены и отображены в окне отладчика. Например, если приложение работает под IDE Visual Studio, оно может писать сообщения отладки в окно IDE. basic_debug_output_backend backend обеспечивает простой способ передачи таких сообщений. Кроме того, для оптимизации производительности приложения доступен специальный фильтр , который проверяет, работает ли приложение под отладчиком. Как и многие другие backends, этот backend также поддерживает настройку форматтера для составления текста сообщения.

Использование довольно простое и простое:

// Complete sink type
typedef sinks::synchronous_sink< sinks::debug_output_backend > sink_t;
void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create the sink. The backend requires synchronization in the frontend.
    boost::shared_ptr< sink_t > sink(new sink_t());
    // Set the special filter to the frontend
    // in order to skip the sink when no debugger is available
    sink->set_filter(expr::is_debugger_present());
    core->add_sink(sink);
}

Обратите внимание, что фон раковины шаблонизирован по типу персонажа. Этот тип определяет версию Windows API, которая используется для отправки сообщений. Кроме того, предоставляются debug_output_backend и wdebug_output_backend.

#include <boost/log/sinks/event_log_backend.hpp>

Операционная система Windows предоставляет специальный API для публикации событий, связанных с выполнением приложений. Широкий спектр приложений, включая компоненты Windows, используют эту возможность для предоставления пользователю всей необходимой информации о здоровье компьютера в одном месте - журнале событий. Может быть более одного журнала событий. Однако, как правило, все приложения пользовательского пространства используют общий журнал приложений. Записи из различных приложений или их частей могут быть выбраны из журнала по имени источника записи. Журналы событий можно читать с помощью стандартной утилиты Event Viewer, которая поставляется с Windows.

Хотя это выглядит очень заманчиво, API довольно сложный и навязчивый, что затрудняет его поддержку. Приложение необходимо для обеспечения динамической библиотеки специальными ресурсами, описывающими все события, которые поддерживает приложение. Эта библиотека должна быть зарегистрирована в реестре Windows, который определяет ее местоположение в файловой системе. Event Viewer использует эту регистрацию для поиска ресурсов, а также для составления и отображения сообщений. Положительная особенность этого подхода заключается в том, что, поскольку ресурсы событий могут описывать события по-разному для разных языков, это позволяет приложению поддерживать интернационализацию событий довольно прозрачным образом: приложение просто предоставляет идентификаторы событий и нелокализуемые параметры событий API, и оно выполняет остальную часть работы.

Чтобы поддержать как упрощенный подход «это просто работает», так и более сложную композицию событий, включая поддержку интернационализации, библиотека предоставляет два фона раковины, которые работают с API журнала событий.

Simple event log backend

basic_simple_event_log_backend Backend предназначен для инкапсуляции как можно большего количества API журнала событий, что делает модель интерфейса и использования очень похожей на другие backendы. Он содержит все ресурсы, необходимые для правильной работы Event Viewer, и регистрирует Boost. Библиотека журналов в реестре Windows для того, чтобы заполнить себя в качестве контейнера этих ресурсов.

[Important]Important

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

Единственное, что пользователь должен сделать, чтобы добавить поддержку журнала событий Windows в свое приложение, это предоставить источник событий и имена журналов (которые являются необязательными и могут быть автоматически предложены библиотекой), настроить соответствующий фильтр, формататор и отображение серьезности событий.

// Complete sink type
typedef sinks::synchronous_sink< sinks::simple_event_log_backend > sink_t;
// Define application-specific severity levels
enum severity_level
{
    normal,
    warning,
    error
};
void init_logging()
{
    // Create an event log sink
    boost::shared_ptr< sink_t > sink(new sink_t());
    sink->set_formatter
    (
        expr::format("%1%: [%2%] - %3%")
            % expr::attr< unsigned int >("LineID")
            % expr::attr< boost::posix_time::ptime >("TimeStamp")
            % expr::smessage
    );
    // We'll have to map our custom levels to the event log event types
    sinks::event_log::custom_event_type_mapping< severity_level > mapping("Severity");
    mapping[normal] = sinks::event_log::info;
    mapping[warning] = sinks::event_log::warning;
    mapping[error] = sinks::event_log::error;
    sink->locked_backend()->set_event_type_mapper(mapping);
    // Add the sink to the core
    logging::core::get()->add_sink(sink);
}

После этого все записи регистрации, которые проходят в раковину, будут отформатированы так же, как и в других раковинах. Отформатированное сообщение будет отображаться в Event Viewer в качестве описания события.

Advanced event log backend

basic_event_log_backend позволяет более детально контролировать API регистрации, но требует значительно большего количества строительных лесов во время инициализации и использования.

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

; /* --------------------------------------------------------
; HEADER SECTION
; */
SeverityNames=(Debug=0x0:MY_SEVERITY_DEBUG
            Info=0x1:MY_SEVERITY_INFO
            Warning=0x2:MY_SEVERITY_WARNING
            Error=0x3:MY_SEVERITY_ERROR
            )
; /* --------------------------------------------------------
; MESSAGE DEFINITION SECTION
; */
MessageIdTypedef=WORD
MessageId=0x1
SymbolicName=MY_CATEGORY_1
Language=English
Category 1
.
MessageId=0x2
SymbolicName=MY_CATEGORY_2
Language=English
Category 2
.
MessageId=0x3
SymbolicName=MY_CATEGORY_3
Language=English
Category 3
.
MessageIdTypedef=DWORD
MessageId=0x100
Severity=Warning
Facility=Application
SymbolicName=LOW_DISK_SPACE_MSG
Language=English
The drive %1 has low free disk space. At least %2 Mb of free space is recommended.
.
MessageId=0x101
Severity=Error
Facility=Application
SymbolicName=DEVICE_INACCESSIBLE_MSG
Language=English
The drive %1 is not accessible.
.
MessageId=0x102
Severity=Info
Facility=Application
SymbolicName=SUCCEEDED_MSG
Language=English
Operation finished successfully in %1 seconds.
.

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

// Create an event log sink
boost::shared_ptr< sinks::event_log_backend > backend(
    new sinks::event_log_backend((
        keywords::message_file = "%SystemDir%\\event_log_messages.dll",
        keywords::log_name = "My Application",
        keywords::log_source = "My Source"
    ))
);

Как и простой бэкэнд, basic_event_log_backend зарегистрируется в реестре Windows, что позволит Event Viewer отображать излучаемые события.

Затем пользователю придется предоставить отображение между атрибутами журналирования приложений и идентификаторами событий. Эти идентификаторы были предоставлены на выходе компилятора сообщений в результате компиляции файла сообщений. Можно использовать basic_event_composer и одно из отображений идентификатора события, как в следующем примере:

// Create an event composer. It is initialized with the event identifier mapping.
sinks::event_log::event_composer composer(
    sinks::event_log::direct_event_id_mapping< int >("EventID"));
// For each event described in the message file, set up the insertion string formatters
composer[LOW_DISK_SPACE_MSG]
    // the first placeholder in the message
    // will be replaced with contents of the "Drive" attribute
    % expr::attr< std::string >("Drive")
    // the second placeholder in the message
    // will be replaced with contents of the "Size" attribute
    % expr::attr< boost::uintmax_t >("Size");
composer[DEVICE_INACCESSIBLE_MSG]
    % expr::attr< std::string >("Drive");
composer[SUCCEEDED_MSG]
    % expr::attr< unsigned int >("Duration");
// Then put the composer to the backend
backend->set_event_composer(composer);

Как вы можете видеть, можно использовать обычные форматы , чтобы указать, какие атрибуты будут вставлены вместо заполнителей в окончательном сообщении о событии. Кроме того, можно указать отображения значений атрибутов для типов и категорий событий. Предположим, что наше приложение имеет следующие уровни тяжести:

// Define application-specific severity levels
enum severity_level
{
    normal,
    warning,
    error
};

Затем эти уровни могут быть отображены на значения в файле описания сообщения:

// We'll have to map our custom levels to the event log event types
sinks::event_log::custom_event_type_mapping< severity_level > type_mapping("Severity");
type_mapping[normal] = sinks::event_log::make_event_type(MY_SEVERITY_INFO);
type_mapping[warning] = sinks::event_log::make_event_type(MY_SEVERITY_WARNING);
type_mapping[error] = sinks::event_log::make_event_type(MY_SEVERITY_ERROR);
backend->set_event_type_mapper(type_mapping);
// Same for event categories.
// Usually event categories can be restored by the event identifier.
sinks::event_log::custom_event_category_mapping< int > cat_mapping("EventID");
cat_mapping[LOW_DISK_SPACE_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_1);
cat_mapping[DEVICE_INACCESSIBLE_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_2);
cat_mapping[SUCCEEDED_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_3);
backend->set_event_category_mapper(cat_mapping);

[Tip]Tip

В Windows NT 6 (Vista, Server 2008) не требуется указывать отображения типа событий. Эта информация доступна в ресурсах определения сообщений и не должна дублироваться в вызове API.

Теперь, когда инициализация завершена, раковина может быть зарегистрирована в ядре.

// Create the frontend for the sink
boost::shared_ptr< sinks::synchronous_sink< sinks::event_log_backend > > sink(
    new sinks::synchronous_sink< sinks::event_log_backend >(backend));
// Set up filter to pass only records that have the necessary attribute
sink->set_filter(expr::has_attr< int >("EventID"));
logging::core::get()->add_sink(sink);

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

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(event_logger, src::severity_logger_mt< severity_level >)
// The function raises an event of the disk space depletion
void announce_low_disk_space(std::string const& drive, boost::uintmax_t size)
{
    BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)LOW_DISK_SPACE_MSG);
    BOOST_LOG_SCOPED_THREAD_TAG("Drive", drive);
    BOOST_LOG_SCOPED_THREAD_TAG("Size", size);
    // Since this record may get accepted by other sinks,
    // this message is not completely useless
    BOOST_LOG_SEV(event_logger::get(), warning) << "Low disk " << drive
        << " space, " << size << " Mb is recommended";
}
// The function raises an event of inaccessible disk drive
void announce_device_inaccessible(std::string const& drive)
{
    BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)DEVICE_INACCESSIBLE_MSG);
    BOOST_LOG_SCOPED_THREAD_TAG("Drive", drive);
    BOOST_LOG_SEV(event_logger::get(), error) << "Cannot access drive " << drive;
}
// The structure is an activity guard that will emit an event upon the activity completion
struct activity_guard
{
    activity_guard()
    {
        // Add a stop watch attribute to measure the activity duration
        m_it = event_logger::get().add_attribute("Duration", attrs::timer()).first;
    }
    ~activity_guard()
    {
        BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)SUCCEEDED_MSG);
        BOOST_LOG_SEV(event_logger::get(), normal) << "Activity ended";
        event_logger::get().remove_attribute(m_it);
    }
private:
    logging::attribute_set::iterator m_it;
};

Теперь вы можете называть эти вспомогательные функции излучать события. Полный код этого раздела доступен в примере event_log в дистрибутиве библиотеки.


PrevUpHomeNext

Статья Sink backends раздела Chapter 1. Boost.Log v2 Detailed features description может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Detailed features description ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 18:23:21/0.022761106491089/1