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

Logging sources

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/sources/basic_logger.hpp>

Самыми простыми источниками журналирования, предоставляемыми библиотекой, являются журналы<logger>и их безвредная версия<logger_mt>(<wlogger>и<wlogger_mt>для широкохарактерных журналов, соответственно). Эти регистраторы только обеспечивают возможность хранить атрибуты, специфичные для источника, внутри себя и, конечно же, формировать записи журналов. Этот тип регистратора, вероятно, следует использовать, когда нет необходимости в расширенных функциях, таких как проверки уровня тяжести. Он может использоваться в качестве инструмента для сбора статистики приложений и регистрации событий приложений, таких как уведомления и тревоги. В таких случаях регистратор обычно используется в сочетании сатрибутамидля прикрепления необходимых данных к событию уведомления. Ниже приведен пример использования:

class network_connection
{
    src::logger m_logger;
    logging::attribute_set::iterator m_remote_addr;
public:
    void on_connected(std::string const& remote_addr)
    {
        // Put the remote address into the logger to automatically attach it
        // to every log record written through the logger
        m_remote_addr = m_logger.add_attribute("RemoteAddress",
            attrs::constant< std::string >(remote_addr)).first;
        // The straightforward way of logging
        if (logging::record rec = m_logger.open_record())
        {
            rec.attribute_values().insert("Message",
                attrs::make_attribute_value(std::string("Connection established")));
            m_logger.push_record(boost::move(rec));
        }
    }
    void on_disconnected()
    {
        // The simpler way of logging: the above "if" condition is wrapped into a neat macro
        BOOST_LOG(m_logger) << "Connection shut down";
        // Remove the attribute with the remote address
        m_logger.remove_attribute(m_remote_addr);
    }
    void on_data_received(std::size_t size)
    {
        // Put the size as an additional attribute
        // so it can be collected and accumulated later if needed.
        // The attribute will be attached only to this log record.
        BOOST_LOG(m_logger) << logging::add_value("ReceivedSize", size) << "Some data received";
    }
    void on_data_sent(std::size_t size)
    {
        BOOST_LOG(m_logger) << logging::add_value("SentSize", size) << "Some data sent";
    }
};

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

#include <boost/log/sources/severity_feature.hpp>
#include <boost/log/sources/severity_logger.hpp>

Возможность отличить некоторые записи журнала от других на основе определенного уровня серьезности или важности является одной из наиболее часто запрашиваемых функций. Шаблоны классов<severity_logger>и<severity_logger_mt>(наряду с их<wseverity_logger>и<wseverity_logger_mt>широкохарактерными аналогами) обеспечивают эту функциональность.

Лесозаготовители автоматически регистрируют специальный источник-специфический атрибут «Severity», который может быть установлен для каждой записи компактным и эффективным способом, с именованным аргументом<severity>, который может быть передан конструктору и/или<open_record>методу. В случае передачи конструктору регистратора аргумент<severity>устанавливает значение по умолчанию уровня серьезности, которое будет использоваться, если в аргументах<open_record>не предусмотрено.<severity>аргумент, переданный<open_record>методу, устанавливает уровень конкретной записи журнала. Тип уровня тяжести может быть предоставлен в качестве аргумента шаблона для шаблона класса logger. Тип по умолчанию<int>.

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

// We define our own severity levels
enum severity_level
{
    normal,
    notification,
    warning,
    error,
    critical
};
void logging_function()
{
    // The logger implicitly adds a source-specific attribute 'Severity'
    // of type 'severity_level' on construction
    src::severity_logger< severity_level > slg;
    BOOST_LOG_SEV(slg, normal) << "A regular message";
    BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
    BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
}

void default_severity()
{
    // The default severity can be specified in constructor.
    src::severity_logger< severity_level > error_lg(keywords::severity = error);
    BOOST_LOG(error_lg) << "An error level log record (by default)";
    // The explicitly specified level overrides the default
    BOOST_LOG_SEV(error_lg, warning) << "A warning level log record (overrode the default)";
}

Или, если вы предпочитаете рубить без макросов:

void manual_logging()
{
    src::severity_logger< severity_level > slg;
    logging::record rec = slg.open_record(keywords::severity = normal);
    if (rec)
    {
        logging::record_ostream strm(rec);
        strm << "A regular message";
        strm.flush();
        slg.push_record(boost::move(rec));
    }
}

И, конечно, суровые лесозаготовители также обеспечивают ту же функциональность, что иосновных лесозаготовителей.

#include <boost/log/sources/channel_feature.hpp>
#include <boost/log/sources/channel_logger.hpp>

Иногда важно связать записи журнала с некоторым компонентом приложения, таким как модуль или имя класса, отношение регистрируемой информации к некоторому определенному домену функциональности приложения (например, сообщения, связанные с сетью или файловой системой) или некоторый произвольный тег, который может быть использован позже для маршрутизации этих записей к конкретному поглотителю. Это свойство выполняется лесорубами<channel_logger>,<channel_logger_mt>и их широкоугольными аналогами<wchannel_logger>,<wchannel_logger_mt>. Эти регистраторы автоматически регистрируют атрибут под названием «канал». Имя канала по умолчанию может быть установлено в конструкторе регистратора с именованным аргументом<channel>. Тип значения атрибута канала может быть определен как шаблонный аргумент для регистратора, с<std::string><std::wstring>в случае регистраторов с широкими символами по умолчанию. Кроме того, использование аналогичноосновным лесозаготовителям:

class network_connection
{
    src::channel_logger< > m_net, m_stat;
    logging::attribute_set::iterator m_net_remote_addr, m_stat_remote_addr;
public:
    network_connection() :
        // We can dump network-related messages through this logger
        // and be able to filter them later
        m_net(keywords::channel = "net"),
        // We also can separate statistic records in a different channel
        // in order to route them to a different sink
        m_stat(keywords::channel = "stat")
    {
    }
    void on_connected(std::string const& remote_addr)
    {
        // Add the remote address to both channels
        attrs::constant< std::string > addr(remote_addr);
        m_net_remote_addr = m_net.add_attribute("RemoteAddress", addr).first;
        m_stat_remote_addr = m_stat.add_attribute("RemoteAddress", addr).first;
        // Put message to the "net" channel
        BOOST_LOG(m_net) << "Connection established";
    }
    void on_disconnected()
    {
        // Put message to the "net" channel
        BOOST_LOG(m_net) << "Connection shut down";
        // Remove the attribute with the remote address
        m_net.remove_attribute(m_net_remote_addr);
        m_stat.remove_attribute(m_stat_remote_addr);
    }
    void on_data_received(std::size_t size)
    {
        BOOST_LOG(m_stat) << logging::add_value("ReceivedSize", size) << "Some data received";
    }
    void on_data_sent(std::size_t size)
    {
        BOOST_LOG(m_stat) << logging::add_value("SentSize", size) << "Some data sent";
    }
};

Также возможно установить название канала отдельных лог-записей. Это может быть полезно, когда вместо объектно-специфического используетсяглобальный регистратор. Название канала может быть установлено путем вызова модификатора<channel>на регистраторе или с помощью специального макроса для регистрации. Например:

// Define a global logger
BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS(my_logger, src::channel_logger_mt< >, (keywords::channel = "general"))
class network_connection
{
    std::string m_remote_addr;
public:
    void on_connected(std::string const& remote_addr)
    {
        m_remote_addr = remote_addr;
        // Put message to the "net" channel
        BOOST_LOG_CHANNEL(my_logger::get(), "net")
            << logging::add_value("RemoteAddress", m_remote_addr)
            << "Connection established";
    }
    void on_disconnected()
    {
        // Put message to the "net" channel
        BOOST_LOG_CHANNEL(my_logger::get(), "net")
            << logging::add_value("RemoteAddress", m_remote_addr)
            << "Connection shut down";
        m_remote_addr.clear();
    }
    void on_data_received(std::size_t size)
    {
        BOOST_LOG_CHANNEL(my_logger::get(), "stat")
            << logging::add_value("RemoteAddress", m_remote_addr)
            << logging::add_value("ReceivedSize", size)
            << "Some data received";
    }
    void on_data_sent(std::size_t size)
    {
        BOOST_LOG_CHANNEL(my_logger::get(), "stat")
            << logging::add_value("RemoteAddress", m_remote_addr)
            << logging::add_value("SentSize", size)
            << "Some data sent";
    }
};

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

[Tip]Tip

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

#include <boost/log/sources/exception_handler_feature.hpp>

Библиотека предоставляет функцию регистратора, которая позволяет пользователю обрабатывать и/или подавлять исключения на уровне регистратора. Функция<exception_handler>добавляет метод<set_exception_handler>к регистратору, который позволяет задать объект функции, который будет вызываться, если исключение выбрасывается из ядра регистрации во время фильтрации или обработки записей журнала. Можно использоватьбиблиотечные адаптерыдля упрощения реализации обработчиков исключений. Пример использования выглядит следующим образом:

enum severity_level
{
    normal,
    warning,
    error
};
// A logger class that allows to intercept exceptions and supports severity level
class my_logger_mt :
    public src::basic_composite_logger<
        char,
        my_logger_mt,
        src::multi_thread_model< boost::shared_mutex >,
        src::features<
            src::severity< severity_level >,
            src::exception_handler
        >
    >
{
    BOOST_LOG_FORWARD_LOGGER_MEMBERS(my_logger_mt)
};
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, my_logger_mt)
{
    my_logger_mt lg;
    // Set up exception handler: all exceptions that occur while
    // logging through this logger, will be suppressed
    lg.set_exception_handler(logging::make_exception_suppressor());
    return lg;
}
void logging_function()
{
    // This will not throw
    BOOST_LOG_SEV(my_logger::get(), normal) << "Hello, world";
}

[Tip]Tip

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

#include <boost/log/sources/severity_channel_logger.hpp>

Если вам интересно, можете ли вы использовать смешанный набор из нескольких функций регистратора в одном регистраторе, то да, вы, безусловно, можете. Библиотека предоставляет<severity_channel_logger>и<severity_channel_logger_mt>(с их широкоугольными аналогами<wseverity_channel_logger>и<wseverity_channel_logger_mt>), которые сочетают в себе особенности описанных лесорубов суровнем строгостииканаламиподдержки. Композитные регистраторы также являются шаблонами, что позволяет указывать уровень серьезности и типы каналов. Вы также можете разработать свои собственные функции регистратора и объединить их с теми, которые предоставляются библиотекой, как описано в. Расширение библиотеки.

Использование лесозаготовителей с несколькими функциями концептуально не отличается от использования однофункциональных лесозаготовителей. Вот как можно использовать<severity_channel_logger_mt>:

enum severity_level
{
    normal,
    notification,
    warning,
    error,
    critical
};
typedef src::severity_channel_logger_mt<
    severity_level,     // the type of the severity level
    std::string         // the type of the channel name
> my_logger_mt;
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, my_logger_mt)
{
    // Specify the channel name on construction, similarly as with the channel_logger
    return my_logger_mt(keywords::channel = "my_logger");
}
void logging_function()
{
    // Do logging with the severity level. The record will have both
    // the severity level and the channel name attached.
    BOOST_LOG_SEV(my_logger::get(), normal) << "Hello, world!";
}

#include <boost/log/sources/global_logger_storage.hpp>

Иногда неудобно иметь объект регистратора, чтобы иметь возможность писать журналы. Эта проблема часто присутствует в коде функционального стиля без очевидных мест, где можно хранить регистратор. Другой областью, где проблема сохраняется, являются общие библиотеки, которые выиграют от регистрации. В таких случаях было бы удобнее иметь одного или нескольких глобальных регистраторов, чтобы при необходимости легко получить к ним доступ в любом месте. В этом отношении<std::cout>является хорошим примером такого лесозаготовителя.

Библиотека предоставляет способ декларирования глобальных регистраторов, к которым можно получить доступ почти как<std::cout>. Фактически, эта функция может быть использована с любым регистратором, включая пользовательские. Объявив глобальный регистратор, можно быть уверенным, что у вас есть безопасный доступ к этому регистратору из любого места кода приложения. Библиотека также гарантирует, что глобальный регистратор будет уникальным даже за пределами модулей. Это позволяет использовать журналирование даже в компонентах только для заголовков, которые могут быть скомпилированы в различные модули.

Можно задаться вопросом, зачем нужно что-то особенное для создания глобальных лесозаготовителей. Почему бы просто не объявить переменную регистратора в области пространства имен и использовать ее везде, где вам нужно? Хотя технически это возможно, декларирование и использование глобальных переменных лесозаготовителей затруднено по следующим причинам:

  • Порядок инициализации переменных пространства имен не определяется стандартом C++. Это означает, что обычно вы не можете использовать регистратор на этом этапе инициализации (т.е. до<main>).
  • Инициализация переменных пространства имен не является безвредной. Вы можете в конечном итоге инициализировать один и тот же регистратор дважды или использовать неинициализированный регистратор.
  • Использование переменных пространства имен в библиотеке только для заголовков довольно сложно. Нужно либо объявить переменную с внешней связью и определить ее только в одном блоке перевода (то есть в отдельном файле .cpp, который побеждает тезис «только заголовк»), либо определить переменную с внутренней связью, либо как особый случай в анонимном пространстве имен (это, скорее всего, нарушит ODR и даст неожиданные результаты, когда заголовок используется в разных блоках перевода). Существуют и другие специфические для компилятора и стандартные приемы решения проблемы, но они не совсем тривиальны и портативны.
  • На большинстве платформ переменные пространства имен локальны для модуля, в котором они были скомпилированы. То есть, если переменная<a>имеет внешнюю связь и была скомпилирована в модули X и Y, каждый из этих модулей имеет свою копию переменной<a>. Что еще хуже, на других платформах эта переменная может быть разделена между модулями.

Глобальное хранение лесозаготовителей призвано устранить все эти проблемы.

Самый простой способ объявить глобального регистратора - использовать следующий макрос:

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger_mt< >)

<my_logger>Аргумент дает регистратору имя, которое может быть использовано для приобретения регистратора. Это имя действует как метка объявленного лесоруба. Второй параметр обозначает тип лесозаготовителя. В многопоточных приложениях, когда к регистратору можно получить доступ из разных потоков, пользователи обычно хотят использовать безопасные для потоков версии регистраторов.

Если требуется передача аргументов конструктору регистратора, есть еще один макрос:

BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS(
    my_logger,
    src::severity_channel_logger< >,
    (keywords::severity = error)(keywords::channel = "my_channel"))

Последний макроаргумент — этоBoost.Preprocessorпоследовательность аргументов, переданная конструктору регистратора. Однако будьте осторожны при использовании непостоянных выражений и ссылок на объекты в качестве аргументов конструктора, так как аргументы оцениваются только один раз и часто трудно сказать точный момент, когда это сделано. Лесоруб строится по первому запросу из любой части заявки, которая знает декларацию лесозаготовителя. Пользователь должен убедиться, что все аргументы имеют действительные состояния.

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

BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, src::severity_logger_mt)
{
    // Do something that needs to be done on logger initialization,
    // e.g. add a stop watch attribute.
    src::severity_logger_mt< > lg;
    lg.add_attribute("StopWatch", boost::make_shared< attrs::timer >());
    // The initializing routine must return the logger instance
    return lg;
}

Как и макрос<BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS>, код инициализации называется только один раз, по первому требованию регистратора.

[Important]Important

Остерегайтесь правил одного определения (ODR). Независимо от выбранного вами способа декларирования регистратора, вы должны убедиться, чторегистратор объявлен точно таким же образом во всех случаяхивсе имена символов, участвующие в декларации, разрешают одинаковые объекты. Последнее включает в себя имена, используемые в рутине инициализации макроса<BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT>, такие как ссылки на внешние переменные, функции и типы. Библиотека пытается в определенной степени защитить себя от нарушений ODR, но в целом поведение не определено, если правило нарушено.

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

  • <BOOST_LOG_GLOBAL_LOGGER>Пояснительная записка. Он может использоваться в заголовке, аналогично<BOOST_LOG_INLINE_GLOBAL_LOGGER*>макросам, описанным выше.
  • <BOOST_LOG_GLOBAL_LOGGER_INIT>,<BOOST_LOG_GLOBAL_LOGGER_DEFAULT>и<BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS>определяют порядок инициализации регистратора. Их семантика и использование аналогичны соответствующим<BOOST_LOG_INLINE_GLOBAL_LOGGER*>макросам, за одним исключением: эти макросы должны использоваться в одном файле .cpp.

Например:

// my_logger.h
// ===========
BOOST_LOG_GLOBAL_LOGGER(my_logger, src::severity_logger_mt)
// my_logger.cpp
// ===========
#include "my_logger.h"
BOOST_LOG_GLOBAL_LOGGER_INIT(my_logger, src::severity_logger_mt)
{
    src::severity_logger_mt< > lg;
    lg.add_attribute("StopWatch", boost::make_shared< attrs::timer >());
    return lg;
}

Независимо от макроса, который вы использовали для объявления регистратора, вы можете получить экземпляр регистратора со статической функцией<get>тега регистратора:

src::severity_logger_mt< >& lg = my_logger::get();

Дальнейшее использование регистратора такое же, как если бы он был обычным объектом регистратора соответствующего типа.

[Warning]Warning

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


PrevUpHomeNext

Статья Logging sources раздела 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:20:05/0.015520095825195/1