![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Extending the libraryBoost , Chapter 1. Boost.Log v2 , Chapter 1. Boost.Log v2
|
![]() | Tip |
---|---|
Выбирая любой из требований синхронизации потока, вы фактически разрешаете или запрещаете использовать определенныефронтенды раковиныс вашим бэкэндом. |
Несколько требований могут быть объединены вfrontend_requirements
type сCombin_requirements
metafunction:
typedef sinks::combine_requirements< sinks::synchronized_feeding, sinks::formatted_records, sinks::flushing >::type frontend_requirements;
Следует отметить, чтосинхронизированное_кормление
иодновременное_кормление
не следует сочетать вместе, поскольку это сделало бы требование синхронизации двусмысленным.синхронное кормление
является более строгим требованием, чемодновременное кормление
, поэтому всякий раз, когда бэкэнд требует одновременного кормления, он также способен синхронизировать кормление.
Метафункцияимеет_требование
может быть использована для тестирования для конкретного требования вfrontend_требования
typedef.
В качестве примера использования класса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,sinks::flushing
>::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(); };
Нам придется хранить внутренние данные, поэтому давайте потребуем frontend для синхронизации звонков в backend. | |
Кроме того, обеспечивается поддержка промывки |
Как видите, публичный интерфейс бэкэнда довольно прост. Толькопотребляют
иметоды смыва
называются фронтендами. Функция«потребление»
называется каждый раз, когда запись журнала проходит фильтрацию в интерфейсе. Запись, как было сказано ранее, содержит набор значений атрибутов и строку сообщения. Поскольку у нас нет необходимости в записи сообщения, мы пока будем игнорировать его. Но из других атрибутов мы можем извлечь статистические данные для накопления и записи в файл. Для этого мы можем использоватьатрибут ключевые словаизначение посещения.
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(); }
Вы можете найти полный код этого примераздесь.
В качестве примера форматирования раковины бэкэнд, давайте реализуем раковину, которая будет отображать текстовые уведомления для каждой записи журнала, переданной ему.
![]() | Tip |
---|---|
Приложения реального мира, вероятно, будут использовать некоторые API-инструменты GUI для отображения уведомлений, но программирование GUI выходит за рамки этой документации. Для отображения уведомлений мы используем внешнюю программу, которая делает именно это. В этом примере мы используем |
Определение бэкэнда очень похоже на то, что мы видели в предыдущем разделе:
// The backend starts an external application to display notifications class app_launcher : public sinks::basic_formatted_sink_backend< char,sinks::synchronized_feeding
> { public: // The function consumes the log records that come from the frontend void consume(logging::record_view const& rec, string_type const& command_line); };
Тип целевого персонажа | |
Чтобы не создавать слишком много экземпляров приложений, мы требуем, чтобы записи обрабатывались последовательно. |
Первое, что следует отметить, это то, чтоapp_launcher
backend происходит от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()); }
Самая интересная часть - установка раковины.синхронный_sink
frontend (как и любой другой frontend) обнаружит, чтоapp_launcher
backend требует форматирования и включения соответствующей функциональности. Способ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"; }
Статья Extending the library раздела Chapter 1. Boost.Log v2 Chapter 1. Boost.Log v2 может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 1. Boost.Log v2 ::
реклама |