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

Extending the library

Boost , Chapter 1. Boost.Log v2 , Chapter 1. Boost.Log v2

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/basic_sink_backend.hpp>

Как было описано в разделеОбзор дизайна, раковины состоят из двух частей: frontend и backend. Фронтенды предоставляются библиотекой и, как правило, не требуют повторного использования. Благодаря frontends реализация backends намного проще, чем могла бы быть: там делается вся фильтрация, форматирование и синхронизация потоков.

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

Frontend requirements
#include <boost/log/sinks/frontend_requirements.hpp>

Для того, чтобы работать с раковинными бэкэндами, frontends используютfrontend_requirementsтипа, определенного всеми бэкэндами. Тип сочетает в себе один или несколько обязательных тегов:

  • синхронизированное питание. Если бэкэнд имеет это требование, он ожидает, что записи журнала будут передаваться с фронтенда синхронно (т.е. только один поток должен подавать запись за раз). Обратите внимание, что разные потоки могут подавать разные записи, требование просто гласит, что не будет параллельных каналов.
  • одновременное кормление. Это требование расширяетсинхронизированное_питание, позволяя различным потокам одновременно подавать записи. Бэкэнд реализует всю необходимую синхронизацию потоков в этом случае.
  • отформатированные_записи. Backend ожидает отформатированных записей журнала. Фронтенд реализует форматирование в строку с типом символа, определеннымchar_typetypedef в бэкэнде. Отформатированная строка будет передана вместе с записью журнала на бэкэнд. Базовый классbasic_formatted_sink_backendавтоматически добавляет это требование к типуfrontend_requirements.
  • смыв. Бэкэнд поддерживает промывку внутренних буферов. Если бэкэнд указывает на это требование, он должен реализовать методфлешбез каких-либо аргументов; этот метод будет называться фронтендом при флешировании.
[Tip]Tip

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

Несколько требований могут быть объединены вfrontend_requirementstype сCombin_requirementsmetafunction:

typedef sinks::combine_requirements<
    sinks::synchronized_feeding,
    sinks::formatted_records,
    sinks::flushing
>::type frontend_requirements;

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

Метафункцияимеет_требованиеможет быть использована для тестирования для конкретного требования вfrontend_требованияtypedef.

Minimalistic sink backend

В качестве примера использования классаbasic_sink_backendдавайте реализуем простой сборщик статистической информации backend. Предположим, что у нас есть сетевой сервер, и мы хотим отслеживать, сколько входящих соединений активны и сколько данных было отправлено или получено. Собранная информация должна быть записана в CSV-файл каждую минуту. Определение backend может выглядеть примерно так:

// The backend collects statistical information about network activity of the application
class stat_collector :
    public sinks::basic_sink_backend<
        sinks::combine_requirements<
            sinks::synchronized_feeding,                                        1
            sinks::flushing                                                     2
        >::type
    >
{
private:
    // The file to write the collected information to
    std::ofstream m_csv_file;
    // Here goes the data collected so far:
    // Active connections
    unsigned int m_active_connections;
    // Sent bytes
    unsigned int m_sent_bytes;
    // Received bytes
    unsigned int m_received_bytes;
    // The number of collected records since the last write to the file
    unsigned int m_collected_count;
    // The time when the collected data has been written to the file last time
    boost::posix_time::ptime m_last_store_time;
public:
    // The constructor initializes the internal data
    explicit stat_collector(const char* file_name);
    // The function consumes the log records that come from the frontend
    void consume(logging::record_view const& rec);
    // The function flushes the file
    void flush();
private:
    // The function resets statistical accumulators to initial values
    void reset_accumulators();
    // The function writes the collected data to the file
    void write_data();
};

1

Нам придется хранить внутренние данные, поэтому давайте потребуем frontend для синхронизации звонков в backend.

2

Кроме того, обеспечивается поддержка промывки

Как видите, публичный интерфейс бэкэнда довольно прост. Толькопотребляютиметоды смываназываются фронтендами. Функция«потребление»называется каждый раз, когда запись журнала проходит фильтрацию в интерфейсе. Запись, как было сказано ранее, содержит набор значений атрибутов и строку сообщения. Поскольку у нас нет необходимости в записи сообщения, мы пока будем игнорировать его. Но из других атрибутов мы можем извлечь статистические данные для накопления и записи в файл. Для этого мы можем использоватьатрибут ключевые словаизначение посещения.

BOOST_LOG_ATTRIBUTE_KEYWORD(sent, "Sent", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(received, "Received", unsigned int)
// The function consumes the log records that come from the frontend
void stat_collector::consume(logging::record_view const& rec)
{
    // Accumulate statistical readings
    if (rec.attribute_values().count("Connected"))
        ++m_active_connections;
    else if (rec.attribute_values().count("Disconnected"))
        --m_active_connections;
    else
    {
        namespace phoenix = boost::phoenix;
        logging::visit(sent, rec, phoenix::ref(m_sent_bytes) += phoenix::placeholders::_1);
        logging::visit(received, rec, phoenix::ref(m_received_bytes) += phoenix::placeholders::_1);
    }
    ++m_collected_count;
    // Check if it's time to write the accumulated data to the file
    boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
    if (now - m_last_store_time >= boost::posix_time::minutes(1))
    {
        write_data();
        m_last_store_time = now;
    }
}
// The function writes the collected data to the file
void stat_collector::write_data()
{
    m_csv_file << m_active_connections
        << ',' << m_sent_bytes
        << ',' << m_received_bytes
        << std::endl;
    reset_accumulators();
}
// The function resets statistical accumulators to initial values
void stat_collector::reset_accumulators()
{
    m_sent_bytes = m_received_bytes = 0;
    m_collected_count = 0;
}

Обратите внимание, что мы использовалиBoost.Phoenixдля автоматического генерирования объектов функции посетителя для значений атрибутов.

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

// The function flushes the file
void stat_collector::flush()
{
    // Store any data that may have been collected since the list write to the file
    if (m_collected_count > 0)
    {
        write_data();
        m_last_store_time = boost::posix_time::microsec_clock::universal_time();
    }
    m_csv_file.flush();
}

Вы можете найти полный код этого примераздесь.

Formatting sink backend

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

[Tip]Tip

Приложения реального мира, вероятно, будут использовать некоторые API-инструменты GUI для отображения уведомлений, но программирование GUI выходит за рамки этой документации. Для отображения уведомлений мы используем внешнюю программу, которая делает именно это. В этом примере мы используемуведомить-отправитьпрограмму, которая доступна на Linux (пользователи Ubuntu/Debian могут установить ее с).libnotify-binупаковка; другие дистрибутивы также должны иметь ее в своих упаковочных хранилищах. Программа принимает параметры уведомлений в командной строке, отображает уведомление в текущей среде рабочего стола, а затем выходит. Другие платформы также могут иметь аналогичные инструменты.

Определение бэкэнда очень похоже на то, что мы видели в предыдущем разделе:

// The backend starts an external application to display notifications
class app_launcher :
    public sinks::basic_formatted_sink_backend<
        char,                                                                   1
        sinks::synchronized_feeding                                             2
    >
{
public:
    // The function consumes the log records that come from the frontend
    void consume(logging::record_view const& rec, string_type const& command_line);
};

1

Тип целевого персонажа

2

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

Первое, что следует отметить, это то, чтоapp_launcherbackend происходит отbasic_formatted_sink_backend, а неbasic_sink_backend. Этот базовый класс принимает тип символа в дополнение к требованиям. Указанный тип символа определяет тип целевой строки, которую форматировщик будет составлять в интерфейсе, и он обычно соответствует базовому API, используемому бэкэндом для обработки записей. Следует отметить, что тип символа, который требуется бэкэнду, не связан с типами символов значений атрибутов строки, включая текст сообщения. Форматтер позаботится о преобразовании кода символов, когда это необходимо.

Вторым заметным отличием от предыдущих примеров является то, чтопотребительметод принимает дополнительный параметр строки помимо записи журнала. Это результат форматирования. Типstring_typeопределяется базовым классомbasic_formatted_sink_backendи соответствует запрашиваемому типу символов.

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

Теперьпотребляют.Реализация тривиальна:

// The function consumes the log records that come from the frontend
void app_launcher::consume(logging::record_view const& rec, string_type const& command_line)
{
    std::system(command_line.c_str());
}

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

Раковина может быть сконфигурирована со следующим кодом:

BOOST_LOG_ATTRIBUTE_KEYWORD(process_name, "ProcessName", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(caption, "Caption", std::string)
// Custom severity level formatting function
std::string severity_level_as_urgency(
    logging::value_ref< logging::trivial::severity_level, logging::trivial::tag::severity > const& level)
{
    if (!level || level.get() == logging::trivial::info)
        return "normal";
    logging::trivial::severity_level lvl = level.get();
    if (lvl < logging::trivial::info)
        return "low";
    else
        return "critical";
}
// The function initializes the logging library
void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    typedef sinks::synchronous_sink< app_launcher > sink_t;
    boost::shared_ptr< sink_t > sink(new sink_t());
    const std::pair< const char*, const char* > shell_decorations[] =
    {
        std::pair< const char*, const char* >("\"", "\\\""),
        std::pair< const char*, const char* >("$", "\\$"),
        std::pair< const char*, const char* >("!", "\\!")
    };
    // Make the formatter generate the command line for notify-send
    sink->set_formatter
    (
        expr::stream << "notify-send -t 2000 -u "
            << boost::phoenix::bind(&severity_level_as_urgency, logging::trivial::severity.or_none())
            << expr::if_(expr::has_attr(process_name))
               [
                    expr::stream << " -a '" << process_name << "'"
               ]
            << expr::if_(expr::has_attr(caption))
               [
                    expr::stream << " \"" << expr::char_decor(shell_decorations)[ expr::stream << caption ] << "\""
               ]
            << " \"" << expr::char_decor(shell_decorations)[ expr::stream << expr::message ] << "\""
    );
    core->add_sink(sink);
    // Add attributes that we will use
    core->add_global_attribute("ProcessName", attrs::current_process_name());
}

Самая интересная часть - установка раковины.синхронный_sinkfrontend (как и любой другой frontend) обнаружит, чтоapp_launcherbackend требует форматирования и включения соответствующей функциональности. Способset_formatterстановится доступным и может быть использован для установки выражения форматирования, которое составляет командную строку для запускауведомления-отправкипрограммы. Мы использовалиатрибутные ключевые словадля идентификации конкретных значений атрибутов в форматировщике. Обратите внимание, что значения атрибутов строки должны быть предварительно обработаны так, чтобы специальные символы, интерпретируемые оболочкой, ускользали в командной строке. Мы достигаем этого с помощьюchar_decorдекоратора с нашей собственной картой замены. После настройки раковины мы также добавляем к ядру атрибуттекущего процесса, чтобы нам не приходилось добавлять его к каждой записи.

После всего этого мы можем, наконец, отобразить некоторые уведомления:

void test_notifications()
{
    BOOST_LOG_TRIVIAL(debug) << "Hello, it's a simple notification";
    BOOST_LOG_TRIVIAL(info) << logging::add_value(caption, "Caption text") << "And this notification has caption as well";
}

Полный код этого примера доступен здесь.


PrevUpHomeNext

Статья Extending the library раздела Chapter 1. Boost.Log v2 Chapter 1. Boost.Log v2 может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 1. Boost.Log v2 ::


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 19:05:36/0.010914087295532/0