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

Writing your own sources

Boost , Chapter 1. Boost.Log v2 , Extending the library

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/threading_models.hpp>
#include <boost/log/sources/basic_logger.hpp>

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

Creating a new logger feature

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

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

  • Функция регистрации должна быть шаблоном класса. Он должен иметь по крайней мере один тип параметров шаблона (назовем его BaseT).
  • Функция должна публично выводиться из параметра шаблона BaseT.
  • Функция должна быть по умолчанию конструктивной и копируемой.
  • Функция должна быть конструируемой с одним аргументом шаблонного типа. Функция может не использовать сам этот аргумент, но она должна передать этот аргумент конструктору BaseT.

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

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

  1. Обычно нет необходимости вводить в каждую функцию мутекс или другой механизм синхронизации. Кроме того, рекомендуется не делать этого, потому что одна и та же функция может использоваться как в нитей-безопасных, так и не нитей-безопасных лесозаготовителей. Вместо этого функции должны использовать резьбовую модель регистратора как примитивную синхронизацию, аналогичную тому, как они будут использовать mutex. Модель резьбы доступна через метод get_threading_model, определенный в шаблоне класса basic_logger.
  2. Общественные методы, которые в конечном итоге называют эти методы, реализуются basic_composite_logger шаблон класса. Эти реализации делают необходимые блокировки _unlocked метод базовых особенностей. .
  3. The public methods that eventually call these methods are implemented by the basic_composite_logger class template. These implementations do the necessary locking and then pass control to the corresponding _unlocked method of the base features. [ORIG_END] -->
  4. Требования безопасности резьбы для этих методов выражены типами блокировки. Эти типы доступны в виде типдефов в каждой функции и шаблона класса basic_logger. Если функция показывает защищенную функцию foo_unlocked, она также выдаст тип foo_lock, который будет выражать требования к блокировке foo_unlocked. Соответствующий метод foo в шаблоне класса basic_composite_logger будет использовать этот типдеф для блокировки модели резьбы перед вызовом foo_unlocked.
  5. Конструкторы функций не нуждаются в блокировке, и поэтому для них нет необходимости в типах блокировок.
  • Функция может реализовать конструктор копий. Аргумент конструктора уже запирается общим замком, когда конструктор вызывается. Естественно, ожидается, что функция перенаправит вызов конструктора копий в класс BaseT.
  • Функция не должна реализовывать оператора назначения. Задание будет автоматически предоставлено экземпляром класса basic_composite_logger. Однако функция может предоставлять метод swap_unlocked, который будет менять содержимое этой функции и аргумент метода, и вызывать аналогичный метод в классе BaseT. Автоматически генерируемый оператор присваивания будет использовать этот метод вместе с конструктором копий.
  • Чтобы проиллюстрировать все эти длинные рекомендации, давайте реализуем простую функцию регистратора. Предположим, мы хотим, чтобы наш регистратор мог пометить отдельные записи журнала. Другими словами, регистратор должен временно добавить атрибут к своему набору атрибутов, испустить запись регистрации, а затем автоматически удалить атрибут. Несколько похожая функциональность может быть достигнута с помощью расширенных атрибутов, хотя синтаксис может усложнить обертывание его в аккуратный макрос:

    // We want something equivalent to this
    {
        BOOST_LOG_SCOPED_LOGGER_TAG(logger, "Tag", "[GUI]");
        BOOST_LOG(logger) << "The user has confirmed his choice";
    }
    

    Давайте объявим нашу функцию регистратора:

    template< typename BaseT >
    class record_tagger_feature :
        public BaseT                        1
    {
    public:
        // Let's import some types that we will need. These imports should be public,
        // in order to allow other features that may derive from record_tagger to do the same.
        typedef typename BaseT::char_type char_type;
        typedef typename BaseT::threading_model threading_model;
    public:
        // Default constructor. Initializes m_Tag to an invalid value.
        record_tagger_feature();
        // Copy constructor. Initializes m_Tag to a value, equivalent to that.m_Tag.
        record_tagger_feature(record_tagger_feature const& that);
        // Forwarding constructor with named parameters
        template< typename ArgsT >
        record_tagger_feature(ArgsT const& args);
        // The method will require locking, so we have to define locking requirements for it.
        // We use the strictest_lock trait in order to choose the most restricting lock type.
        typedef typename logging::strictest_lock<
            boost::lock_guard< threading_model >,
            typename BaseT::open_record_lock,
            typename BaseT::add_attribute_lock,
            typename BaseT::remove_attribute_lock
        >::type open_record_lock;
    protected:
        // Lock-less implementation of operations
        template< typename ArgsT >
        logging::record open_record_unlocked(ArgsT const& args);
    };
    // A convenience metafunction to specify the feature
    // in the list of features of the final logger later
    struct record_tagger :
        public boost::mpl::quote1< record_tagger_feature >
    {
    };
    

    1

    функция должна быть получена из других функций или класса basic_logger

    Вы можете видеть, что мы используем шаблон strictest_lock, чтобы определить типы блокировок, которые удовлетворяют требованиям безопасности потока базового класса для методов, которые должны быть вызваны из соответствующих методов record_tagger_feature. Определение open_record_lock показывает, что реализация open_record_unlocked для функции record_tagger_feature требует эксклюзивной блокировки (которой является lock_guard) для регистратора, но также учитывает требования к блокировке open_record_unlocked, add_attribute_unlocked способов базового класса, поскольку для этого придется их вызывать. Сгенерированный метод open_record конечного класса регистратора будет использовать этот типдеф для автоматического получения соответствующего типа блокировки перед пересылкой на методы open_record_unlocked.

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

    Реализация публичного интерфейса становится довольно тривиальной:

    template< typename BaseT >
    record_tagger_feature< BaseT >::record_tagger_feature()
    {
    }
    template< typename BaseT >
    record_tagger_feature< BaseT >::record_tagger_feature(record_tagger_feature const& that) :
        BaseT(static_cast< BaseT const& >(that))
    {
    }
    template< typename BaseT >
    template< typename ArgsT >
    record_tagger_feature< BaseT >::record_tagger_feature(ArgsT const& args) : BaseT(args)
    {
    }
    

    Теперь, поскольку вся блокировка извлекается в общедоступный интерфейс, у нас есть большая часть нашей логики функций, которая будет реализована в защищенной части интерфейса. Чтобы установить значение тега в регистраторе, нам нужно будет ввести новое ключевое слово Boost.Parameter. Следуя рекомендациям из этой библиотечной документации, лучше ввести ключевое слово в специальное пространство имен:

    namespace my_keywords {
        BOOST_PARAMETER_KEYWORD(tag_ns, tag)
    }
    

    Открытие нового альбома может выглядеть примерно так:

    template< typename BaseT >
    template< typename ArgsT >
    logging::record record_tagger_feature< BaseT >::open_record_unlocked(ArgsT const& args)
    {
        // Extract the named argument from the parameters pack
        std::string tag_value = args[my_keywords::tag | std::string()];
        logging::attribute_set& attrs = BaseT::attributes();
        logging::attribute_set::iterator tag = attrs.end();
        if (!tag_value.empty())
        {
            // Add the tag as a new attribute
            std::pair<
                logging::attribute_set::iterator,
                bool
            > res = BaseT::add_attribute_unlocked("Tag",
                attrs::constant< std::string >(tag_value));
            if (res.second)
                tag = res.first;
        }
        // In any case, after opening a record remove the tag from the attributes
        BOOST_SCOPE_EXIT_TPL((&tag)(&attrs))
        {
            if (tag != attrs.end())
                attrs.erase(tag);
        }
        BOOST_SCOPE_EXIT_END
        // Forward the call to the base feature
        return BaseT::open_record_unlocked(args);
    }
    

    Здесь мы добавляем новый атрибут со значением тега, если он указан в вызове open_record. Когда запись журнала открывается, все значения атрибутов приобретаются и блокируются после записи, поэтому мы удаляем тег из набора атрибутов с блоком Boost.ScopeExit.

    Хорошо, у нас есть функция, и пришло время ввести ее в лесоруб. Предположим, что мы хотим объединить его со стандартным уровнем жесткости. Нет проблем:

    template< typename LevelT = int >
    class my_logger :
        public src::basic_composite_logger<
            char,                           1
            my_logger< LevelT >,            2
            src::single_thread_model,       3
            src::features<                  4
                src::severity< LevelT >,
                record_tagger
            >
        >
    {
        // The following line will automatically generate forwarding constructors that
        // will call to the corresponding constructors of the base class
        BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE(my_logger)
    };
    

    1

    тип символа для лесоруба

    2

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

    3

    регистратор не выполняет синхронизацию потоков; используйте multi_thread_model для объявления регистратора

    4

    список функций, которые мы хотим объединить

    Как видите, создание лесозаготовителя – довольно простая процедура. Макро BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE, которое вы видите здесь, просто для удобства: оно разворачивается в конструктор по умолчанию, конструктор копий, оператор назначения и ряд конструкторов для поддержки названных аргументов. Для нешаблонных лесорубов существует аналогичная BOOST_LOG_FORWARD_LOGGER_MEMBERS macro.

    Предполагая, что мы определили уровни тяжести, как это:

    enum severity_level
    {
        normal,
        warning,
        error
    };
    

    теперь мы можем использовать наш регистратор следующим образом:

    void manual_logging()
    {
        my_logger< severity_level > logger;
        logging::record rec = logger.open_record((keywords::severity = normal, my_keywords::tag = "GUI"));
        if (rec)
        {
            logging::record_ostream strm(rec);
            strm << "The user has confirmed his choice";
            strm.flush();
            logger.push_record(boost::move(rec));
        }
    }
    

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

    #define LOG_WITH_TAG(lg, sev, tg) \
        BOOST_LOG_WITH_PARAMS((lg), (keywords::severity = (sev))(my_keywords::tag = (tg)))
    void logging_function()
    {
        my_logger< severity_level > logger;
        LOG_WITH_TAG(logger, normal, "GUI") << "The user has confirmed his choice";
    }
    

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

    Guidelines for designers of standalone logging sources

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

    1. Всякий раз, когда источник регистрации готов испустить запись журнала, он должен называть open_record в ядре. В этот призыв должны быть переданы специфические атрибуты источника. Во время этого вызова ядро выделяет ресурсы для записи и выполняет фильтрацию.
    2. Если вызов open_record вернул действительную запись журнала, то запись прошла фильтрацию и считается открытой. Запись может быть позже подтверждена источником путем последующего вызова push_record или удалена путем ее уничтожения.
    3. Если вызов open_record вернул недействительную (пустую) запись журнала, это означает, что запись не была открыта (скорее всего, из-за отказа фильтрации). В этом случае ядро регистрации не содержит никаких ресурсов, связанных с записью, и поэтому источник не должен вызывать push_record для этой конкретной попытки регистрации.
    4. Впоследствии источник может открыть более одной записи. Открытые журнальные записи существуют независимо друг от друга.

    PrevUpHomeNext

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




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



    :: Главная :: Extending the library ::


    реклама


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

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