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

Sink frontends

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

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

Фронтенды Sink происходят от шаблона класса<sink>, который используется ядром журналирования для подачи записей журналов. С технической точки зрения, можно вывести свой класс из шаблона<sink>и получить его вновь найденную раковину, но использование фронтендов раковины экономит довольно много рутинной работы. Поскольку каждый фронтенд раковины связан с бэкэндом, соответствующий бэкэнд также будет построен фронтендом после его строительства (если пользователь сам не предоставит экземпляр бэкэнда), что делает раковину полной. Поэтому, когда фронтенд построен, он может быть зарегистрирован в ядре регистрации, чтобы начать обработку записей. СмотретьSink Backendsраздел для более подробной информации о взаимодействии между интерфейсами и бэкэндами.

Ниже приведен более подробный обзор услуг, предоставляемых фронтендами раковины.

Существует ряд основных функций, которые обеспечивают все фронтенды раковины.

Все фронтенды раковины поддерживают фильтрацию. Пользователь может указать пользовательский объект функции фильтрации или фильтр, построенный с помощью инструментов, предоставляемых библиотекой.. Фильтр может быть установлен методом<set_filter>или очищен методом<reset_filter>. Фильтр вызывается во время вызова метода<will_consume>, который выдается ядром регистрации. Если фильтр не установлен, предполагается, что раковина примет любую запись журнала.

[Note]Note

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

Для текстовых панелей раковины frontends реализуют форматирование записей. Как и фильтры, лямбда-выражениямогут использоваться для создания формататоров. Формататор может быть установлен для текстовой раковины, позвонив по<set_formatter>методу или очистить, позвонив<reset_formatter>.

Все фронтенды раковины позволяют настраивать обработчики исключений, чтобы настроить обработку ошибок на основе per-sink. Можно установить функцию обработки исключений с помощью метода<set_exception_handler>, эта функция будет вызываться без аргументов из блока<catch>, если исключение происходит во время обработки записей в бэкэнде или во время фильтрации, характерной для раковины. Обработчик исключений свободен отбросить исключение или подавить его. В первом случае исключение распространяется на ядро, где в действие может вступить другой уровень обработки исключений.

[Tip]Tip

Ядро регистрацииирегистраторытакже поддерживают установку обработчиков исключений.

Библиотека предоставляетудобный инструментдля отправки исключений в унарный полиморфный функциональный объект.

[Note]Note

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

[Note]Note

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

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

Разблокированный интерфейс раковины реализован с шаблоном класса<unlocked_sink>. Этот frontend предоставляет самый базовый сервис для backend.<unlocked_sink>frontend не выполняет синхронизацию потоков при доступе к backend, предполагая, что синхронизация либо не нужна, либо реализована backend. Тем не менее, настройка фильтра по-прежнему безвредна (то есть можно безопасно изменить фильтр в<unlocked_sink>интерфейсе, в то время как другие потоки записывают журналы через эту раковину). Это единственный фронтенд раковины, доступный в одной резьбовой среде. Пример использования заключается в следующем:

enum severity_level
{
    normal,
    warning,
    error
};
// A trivial sink backend that requires no thread synchronization
class my_backend :
    public sinks::basic_sink_backend< sinks::concurrent_feeding >
{
public:
    // The function is called for every log record to be written to log
    void consume(logging::record_view const& rec)
    {
        // We skip the actual synchronization code for brevity
        std::cout << rec[expr::smessage] << std::endl;
    }
};
// Complete sink type
typedef sinks::unlocked_sink< my_backend > sink_t;
void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // The simplest way, the backend is default-constructed
    boost::shared_ptr< sink_t > sink1(new sink_t());
    core->add_sink(sink1);
    // One can construct backend separately and pass it to the frontend
    boost::shared_ptr< my_backend > backend(new my_backend());
    boost::shared_ptr< sink_t > sink2(new sink_t(backend));
    core->add_sink(sink2);
    // You can manage filtering through the sink interface
    sink1->set_filter(expr::attr< severity_level >("Severity") >= warning);
    sink2->set_filter(expr::attr< std::string >("Channel") == "net");
}

См. полный текст.

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

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

Синхронный интерфейс раковины реализован с шаблоном класса<synchronous_sink>. Он похож на<unlocked_sink>, но дополнительно обеспечивает синхронизацию потоков с mutex перед передачей записей журнала на бэкэнд. Все нисходящие бэкэнды, поддерживающие форматирование, в настоящее время требуют синхронизации потоков в интерфейсе.

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

Схожая ситуация с<unlocked_sink>.

enum severity_level
{
    normal,
    warning,
    error
};
// Complete sink type
typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink_t;
void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a backend and initialize it with a stream
    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()));
    // Wrap it into the frontend and register in the core
    boost::shared_ptr< sink_t > sink(new sink_t(backend));
    core->add_sink(sink);
    // You can manage filtering and formatting through the sink interface
    sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
    sink->set_formatter
    (
        expr::stream
            << "Level: " << expr::attr< severity_level >("Severity")
            << " Message: " << expr::smessage
    );
    // You can also manage backend in a thread-safe manner
    {
        sink_t::locked_backend_ptr p = sink->locked_backend();
        p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
    } // the backend gets released here
}

См. полный текст.

#include <boost/log/sinks/async_frontend.hpp>
// Related headers
#include <boost/log/sinks/unbounded_fifo_queue.hpp>
#include <boost/log/sinks/unbounded_ordering_queue.hpp>
#include <boost/log/sinks/bounded_fifo_queue.hpp>
#include <boost/log/sinks/bounded_ordering_queue.hpp>
#include <boost/log/sinks/drop_on_overflow.hpp>
#include <boost/log/sinks/block_on_overflow.hpp>

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

[Note]Note

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

enum severity_level
{
    normal,
    warning,
    error
};
// Complete sink type
typedef sinks::asynchronous_sink< sinks::text_ostream_backend > sink_t;
boost::shared_ptr< sink_t > init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a backend and initialize it with a stream
    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()));
    // Wrap it into the frontend and register in the core
    boost::shared_ptr< sink_t > sink(new sink_t(backend));
    core->add_sink(sink);
    // You can manage filtering and formatting through the sink interface
    sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
    sink->set_formatter
    (
        expr::stream
            << "Level: " << expr::attr< severity_level >("Severity")
            << " Message: " << expr::message
    );
    // You can also manage backend in a thread-safe manner
    {
        sink_t::locked_backend_ptr p = sink->locked_backend();
        p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
    } // the backend gets released here
    return sink;
}

[Important]Important

Если в мультимодульном приложении используется асинхронная регистрация, следует тщательно решить, когда выгружать динамически загруженные модули, которые записывают журналы. Библиотека имеет много мест, где она может в конечном итоге использовать ресурсы, которые находятся в динамически загружаемом модуле. Примерами таких ресурсов являются виртуальные таблицы, литералы строк и функции. Если какой-либо из этих ресурсов все еще используется библиотекой, когда модуль, в котором они находятся, разгружается, приложение, скорее всего, выйдет из строя. Строго говоря, эта проблема существует с любым типом раковины (и не ограничивается раковинами в первую очередь), но асинхронные раковины вводят дополнительную проблему. Нельзя сказать, какие ресурсы используются асинхронной мойкой, потому что она работает в выделенной нити и использует буферизованные записи журнала. Общего решения этого вопроса нет. Пользователям рекомендуется либо избегать разгрузки динамических модулей во время работы приложения, либо избегать асинхронной регистрации. В качестве дополнительного способа справиться с проблемой можно попытаться отключить все асинхронные раковины перед разгрузкой любых модулей, а после разгрузки воссоздать их. Однако избежать динамической разгрузки — единственный способ решить проблему полностью.

Для того, чтобы остановить выделенные потоки подачи записей журнала на бэкэнд можно назвать<stop>метод фронтенда. Этот метод будет называться автоматически в интерфейсном деструкторе. Метод<stop>, в отличие от прерывания потока, завершает цикл подачи только тогда, когда подаваемая запись журнала обрабатывается бэкэндом (т.е. он не прерывает уже начатую обработку записи). Однако может случиться так, что некоторые записи все еще остаются в очереди после возвращения из метода<stop>. Для того, чтобы смыть их в бэкэнд, требуется дополнительный вызов<feed_records>метода. Это полезно на этапе прекращения подачи заявки.

void stop_logging(boost::shared_ptr< sink_t >& sink)
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Remove the sink from the core, so that no records are passed to it
    core->remove_sink(sink);
    // Break the feeding loop
    sink->stop();
    // Flush all log records that may have left buffered
    sink->flush();
    sink.reset();
}

См. полный текст.

Нерест выделенной нити для подачи записи журнала можно подавить с помощью необязательного булевого<start_thread>названного параметра фронтенда. В этом случае пользователь может выбрать любой способ обработки записей:

  • Назовите<run>метод фронтенда. Этот звонок заблокирует цикл кормления. Этот цикл можно прервать с помощью вызова<stop>.
  • Периодически звоните<feed_records>. Этот метод будет обрабатывать все записи журнала, которые были в очереди фронтенда, когда звонок был выпущен, а затем вернуться.
[Note]Note

Пользователи должны позаботиться о том, чтобы не смешивать эти два подхода одновременно. Кроме того, ни один из этих методов не следует называть, если выделенная нить подачи работает (т.е.<start_thread>не была указана в конструкции или имела значение<true>).

Customizing record queueing strategy

Шаблон класса<asynchronous_sink>можно настроить с помощью стратегии очередей записей. Библиотека предлагает несколько стратегий:

  • <unbounded_fifo_queue>. Эта стратегия является дефолтом. Как следует из названия, очередь не ограничена по глубине и не заказывает регистрационные записи.
  • <unbounded_ordering_queue>Как и<unbounded_fifo_queue>, очередь имеет неограниченную глубину, но она применяет порядок на очерченных записях. Мы вернемся к заказу очередей через мгновение.
  • <bounded_fifo_queue>. Очередь имеет ограниченную глубину, указанную в шаблонном параметре, а также стратегию обработки переполнения. Заказ записей не производится.
  • <bounded_ordering_queue>Как<bounded_fifo_queue>, но также применяется порядок записи журнала.
[Warning]Warning

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

Ограниченные очереди поддерживают следующие стратегии переполнения:

  • <drop_on_overflow>. Когда очередь заполнена, молча сбрасывайте чрезмерные регистрационные записи.
  • <block_on_overflow>. Когда очередь заполнена, блокируйте нить регистрации до тех пор, пока поток подачи бэкэнда не обработает некоторые из стоящих в очереди записей.

Например, мы могли бы изменить предыдущий пример, чтобы ограничить очередь записи до 100 элементов:

// Complete sink type
typedef sinks::asynchronous_sink<
    sinks::text_ostream_backend,
    sinks::bounded_fifo_queue<                                                   1
        100,                                                                     2
        sinks::drop_on_overflow                                                  3
    >
> sink_t;
boost::shared_ptr< sink_t > init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a backend and initialize it with a stream
    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()));
    // Wrap it into the frontend and register in the core
    boost::shared_ptr< sink_t > sink(new sink_t(backend));
    core->add_sink(sink);
    // ...
    return sink;
}

1

стратегия постановки в очередь

2

рекордный объем очередей

3

Политика управления потоками

См. полный текст.

Также см.<bounded_async_log>пример в библиотечном распределении.

Ordering log records

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

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

// Complete sink type
typedef sinks::asynchronous_sink<
    sinks::text_ostream_backend,
    sinks::unbounded_ordering_queue<                                             1
        logging::attribute_value_ordering<                                       2
            unsigned int,                                                        3
            std::less< unsigned int >                                            4
        >
    >
> sink_t;
boost::shared_ptr< sink_t > init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Create a backend and initialize it with a stream
    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()));
    // Wrap it into the frontend and register in the core
    boost::shared_ptr< sink_t > sink(new sink_t(
        backend,                                                                 5
        keywords::order =
            logging::make_attr_ordering("LineID", std::less< unsigned int >()),  6
        keywords::ordering_window = boost::posix_time::seconds(1)                7
    ));
    core->add_sink(sink);
    // You can manage filtering and formatting through the sink interface
    sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
    sink->set_formatter
    (
        expr::stream
            << "Level: " << expr::attr< severity_level >("Severity")
            << " Message: " << expr::smessage
    );
    // You can also manage backend in a thread-safe manner
    {
        sink_t::locked_backend_ptr p = sink->locked_backend();
        p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
    } // the backend gets released here
    return sink;
}

1

стратегия постановки в очередь

2

log record ordering тип предиката

3

Тип значения атрибута

4

необязательный предикат сравнения значений атрибутов;<std::less>эквивалент используется по умолчанию

5

Указатель на прединициализированный бэкэнд

6

лог-регистрация предиката

7

задержка обработки записей журнала

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

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

void stop_logging(boost::shared_ptr< sink_t >& sink)
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    // Remove the sink from the core, so that no records are passed to it
    core->remove_sink(sink);
    // Break the feeding loop
    sink->stop();
    // Flush all log records that may have left buffered
    sink->flush();
    sink.reset();
}

Этот метод также продемонстрирован в.<async_log>пример в библиотечном распределении.


PrevUpHomeNext

Статья Sink frontends раздела 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 19:16:41/0.034981966018677/1