Boost.Log: как перенаправить источник логов

Я пытаюсь выяснить, как лучше всего использовать источник ведения журнала в классе. Прямое решение, просто определить его как переменную члена (стека), имеет большой недостаток, заключающийся в том, что я должен включать заголовочные файлы Boost.Log, что действительно замедляет компиляцию.

Я хотел бы использовать регистратор канала серьезности. Итак, мой первый подход состоял в том, чтобы определить новый тип, например:

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

typedef boost::log::sources::severity_channel_logger_mt<SeverityLogLevel, std::string> DefaultLogger;

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

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

// MyLogger.h
class MyLogger : public boost::log::sources::severity_channel_logger_mt<SeverityLogLevel, std::string>
{
  public:
    MyLogger(const std::string& name);
};

// MyLoggingClient.h
include <memory>

class MyLogger;

class MyLoggingClient
{
  // Actual implementation

  private:
    std::unique_ptr<MyLogger> lg;

};

Но при этом компилятор жалуется, что MyLogger не определяет бинарный оператор "[" или преобразование в тип, приемлемый для предопределенного оператора.

Итак, мой вопрос в том, что является хорошим способом просто переслать определение регистратора? (Я занимаюсь рефакторингом регистрации существующего кода, поэтому использование PIMPL или аналогичного шаблона потребует много дополнительных усилий.)

Спасибо, - Ларс


person Lars    schedule 07.12.2015    source источник


Ответы (1)


Я думаю, вы уже показали лучший способ для прямого объявления регистратора, хотя я бы предложил хранить регистратор Boost.Log как член MyLogger, а не производный от него. Регистраторы Boost.Log разработаны с использованием CRTP, поэтому его производные могут привести к неожиданному поведению.

Ошибка компилятора, которую вы получаете, вероятно, вызвана какой-то частью вашего кода, которая не была обновлена ​​​​для учета внесенных вами изменений - что lg больше не регистратор, а указатель.

PIMPL — это еще один способ добиться того, чего вы хотите, и я думаю, что на самом деле это может быть проще, чем использование указателя. Что вам нужно, так это реализовать оболочку для регистратора, которая реализует интерфейс, аналогичный используемым вами регистраторам Boost.Log. Например, для severity_channel_logger_mt этого может быть достаточно:

// MyLogger.h
#include <string>
#include <boost/log/core/record.hpp>
#include <boost/log/keywords/severity.hpp>
#include <boost/log/keywords/channel.hpp>

enum SeverityLogLevel { ... };

class MyLogger
{
private:
    struct Impl;
    Impl* impl;

public:
    MyLogger();
    MyLogger(MyLogger const&);
    MyLogger(MyLogger&& that) noexcept : impl(that.impl) { that.impl = nullptr; }
    ~MyLogger();

    MyLogger& operator= (MyLogger const&);
    MyLogger& operator= (MyLogger&& that) noexcept
    {
        MyLogger copy(static_cast< MyLogger&& >(that)); // you can use use std::move here and include <utility>
        this->swap(copy);
        return *this;
    }

    boost::log::record open_record();
    boost::log::record open_record(SeverityLogLevel sev, std::string const& chan);
    template< typename Args >
    boost::log::record open_record(Args const& args)
    {
        return open_record(args[boost::log::keywords::severity], args[boost::log::keywords::channel]);
    }

    void push_record(boost::log::record&& rec);

    void swap(MyLogger& that) noexcept
    {
        Impl* p = impl;
        impl = that.impl;
        that.impl = p;
    }
};

// MyLogger.cpp
#include "MyLogger.h"
#include <utility>
#include <boost/log/sources/severity_channel_logger.hpp>

typedef boost::log::sources::severity_channel_logger_mt<
    SeverityLogLevel,
    std::string
> DefaultLogger;

struct MyLogger::Impl
{
    DefaultLogger lg;
};

MyLogger::MyLogger() : impl(new Impl())
{
}

MyLogger::MyLogger(MyLogger const& that) : impl(new Impl(*that.impl))
{
}

MyLogger::~MyLogger()
{
   delete impl;
}

MyLogger& MyLogger::operator= (MyLogger const& that)
{
    MyLogger(that).swap(*this);
    return *this;
}

boost::log::record MyLogger::open_record()
{
    return impl->lg.open_record();
}

boost::log::record MyLogger::open_record(SeverityLogLevel sev, std::string const& chan)
{
    return impl->lg.open_record((boost::log::keywords::severity = sev, boost::log::keywords::channel = chan));
}

void MyLogger::push_record(boost::log::record&& rec)
{
    impl->lg.push_record(std::move(rec));
}

Вы должны иметь возможность использовать макросы Boost.Log с этой оболочкой как есть. Вам все еще нужно включить несколько заголовков в MyLogger.h, но, надеюсь, это будет достаточно значительным улучшением для вас. Его можно улучшить, если вы удалите поддержку ключевых слов (включая Boost.Log и шаблонную перегрузку open_record), но тогда вам придется определять и использовать свои собственные макросы ведения журнала во всем коде.

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

person Andrey Semashev    schedule 08.12.2015