Использование boost.log с макросами в стиле printf

Я работаю над приложением, которое использует настраиваемый регистратор, зависящий от платформы. Приложение определяет некоторые макросы в стиле printf:

#define LOG_DEBUG(format, ...) \
  logger.log(DEBUG, __FUNCTION__, format, ##__VA_ARGS__)

...

Последние несколько дней я работал над переводом приложения на использование boost.log. Самая большая проблема, с которой я столкнулся, - это попытка сохранить этот формат макроса, чтобы нужно было изменить только внутренние компоненты регистратора, поскольку API-интерфейс ведения журнала Boost реализован в стиле iostream, т.е.

BOOST_LOG(logger) << "something";
  1. Есть ли простой способ предоставить макрос, который принимает аргументы в стиле printf и печатает их в регистраторе повышения без использования строковых буферов? (Я бы подумал, что необходимость форматирования в строку повлияет на производительность)
  2. Если нет, есть ли способ отформатировать строку с помощью va_args без необходимости #ifdef для разных платформ реализации функций форматирования? (в этом и был весь смысл перехода на boost.log в первую очередь)

person jwalk    schedule 16.04.2013    source источник
comment
Вы пробовали ответ, который я опубликовал?   -  person Vite Falcon    schedule 18.04.2013
comment
Да. Последний пример, который вы привели с использованием vsnprintf, был очень хорош. Предыдущие определения макросов были немного чрезмерными, но дали мне несколько идей. Спасибо!   -  person jwalk    schedule 18.04.2013


Ответы (2)


К сожалению, нет чистых реализаций без оператора #ifdef. Я знаю, что вы хотите перенести существующее ведение журнала, чтобы увеличить ведение журнала как есть. Это было бы неправильным способом ведения дел. printf был разработан для C, а журнал boost — для C++. Итак, мое предложение будет состоять в том, чтобы использовать его правильно. Вы можете сделать свои макросы ведения журнала безболезненными, чем то, что вы показали в своем примере кода.

Вот моя реализация журнала повышения, где вместо BOOST_LOG(logger) << "something"; у меня есть roLOG_INFO << "something";. Я считаю, что этот пример взят из одного из образцов журнала повышения.

РоЛог.ч

#include <boost/logging/format_fwd.hpp>

#include <boost/logging/writer/on_dedicated_thread.hpp>

// Optimize : use a cache string, to make formatting the message faster
BOOST_LOG_FORMAT_MSG( optimize::cache_string_one_str<> ) 

#ifndef BOOST_LOG_COMPILE_FAST
#include <boost/logging/format.hpp>
#include <boost/logging/writer/ts_write.hpp>
#endif

// Specify your logging class(es)
typedef boost::logging::logger_format_write< > log_type;

// Declare which filters and loggers you'll use
BOOST_DECLARE_LOG_FILTER(roLogFilter, boost::logging::level::holder)
BOOST_DECLARE_LOG(roLog, log_type)

#define roLOG_WHERE_EACH_LINE 0
#if defined(roLOG_WHERE_EACH_LINE) && roLOG_WHERE_EACH_LINE
#   define _roLOG_WHERE << roWHERE
#else
#   define _roLOG_WHERE
#endif

// Define the macros through which you'll log
#define roLOG_DBG BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), debug ) << "[  DEBUG  ]:" _roLOG_WHERE
#define roLOG_WARN BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), warning) << "[ WARNING ]:" _roLOG_WHERE
#define roLOG_ERR BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), error ) << "[  ERROR  ]:" _roLOG_WHERE
#define roLOG_CRIT BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), fatal ) << "[ CRITICAL]:" _roLOG_WHERE
#define roLOG_INFO BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), info )
struct RoLogOptions;

void roInitLogs(const RoLogOptions& options);

RoLog.cpp

#include <Core/RoLog.h>
#include <Core/RoLogOptions.h>

#include <boost/logging/format.hpp>
#include <boost/logging/writer/ts_write.hpp>

using namespace boost::logging;

BOOST_DEFINE_LOG(roLog, log_type)
BOOST_DEFINE_LOG_FILTER(roLogFilter, level::holder)
#define BLOCK(stmts) do{stmts}while(false)
#define CHECK_AND_DO(var,stmt) if (var) stmt
//------------------------------------------------------------------------------
void roInitLogs(const RoLogOptions& options)
{
    static bool initialize = true;
    if (initialize)
    {
        // Add formatters and destinations
        // That is, how the message is to be formatted...
        CHECK_AND_DO(options.printIndex, roLog()->writer().add_formatter(formatter::idx()));
        CHECK_AND_DO(options.printTimestamp, roLog()->writer().add_formatter(formatter::time(options.timestampFormat)));
        CHECK_AND_DO(options.autoAppendLine, roLog()->writer().add_formatter(formatter::append_newline()));

        //        ... and where should it be written to
        roLog()->writer().add_destination(destination::dbg_window());
        CHECK_AND_DO(options.printToStdOut, roLog()->writer().add_destination(destination::cout()));
        if (!options.logFile.empty())
        {
            destination::file_settings settings;
            settings.do_append(options.logFileAppendExisting);
            roLog()->writer().add_destination(destination::file(options.logFile, settings));
        }
        CHECK_AND_DO(options.turnOffCache, roLog()->turn_cache_off());

        //       ... and set the log-level
        level::type boost_log_level;
        switch(options.logLevel)
        {
        case eLogLevel_None:
            boost_log_level = level::disable_all;
            break;
        case eLogLevel_All:
            boost_log_level = level::enable_all;
            break;
        case eLogLevel_Debug:
            boost_log_level = level::debug;
            break;
        case eLogLevel_Info:
            boost_log_level = level::info;
            break;
        case eLogLevel_Warning:
            boost_log_level = level::warning;
        case eLogLevel_Error:
            boost_log_level = level::error;
            break;
        case eLogLevel_Critical:
            boost_log_level = level::fatal;
            break;
        case eLogLevel_Default:
        default:
#        ifdef _DEBUG
            boost_log_level = level::debug;
#        else
            boost_log_level = level::info;
#        endif // _DEBUG
            break;
        };
        roLogFilter()->set_enabled(boost_log_level);
        initialize = false;
    }
}

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я не утверждаю, что это идеальный фрагмент кода. Это работает для меня, и я доволен этим.

Допустим, вам все еще нужна возможность логирования стиля printf, тогда рассмотрите следующий код:

class LogUtil
{
    static void VLogError(const char* format, va_list argList)
    {
#ifdef _WIN32
        int32 size = _vscprintf(format, argList) + 1;
#else
        int32 size = vsnprintf(0, 0, format, argList) + 1;
#endif
        if (size > 0)
        {
            boost::scoped_array<char> formattedString(new char[size]);
            vsnprintf(formattedString.get(), size, format, argList);
            roLOG_ERR << formattedString.get();
        }
    }
}
#define roLOGF_ERR(format, ...) LogUtil::VLogError(format, ##__VA_ARGS)

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: приведенный выше код не тестировался. Возможно, вам придется настроить его, чтобы он работал на вас.

person Vite Falcon    schedule 16.04.2013

Или просто используйте Boost Format для обработки строка формата printf. Например,

template<typename Level, typename T1>
inline void log_format(
    const Level level,
    const char* const fmt,
    const T1& t1)
{
    BOOST_LOG_SEV(logger::get(), level) << boost::format(fmt) % t1;
}

Создание logger и его расширение для обработки нескольких аргументов (скорее всего, с вариативными аргументами шаблона) остается читателю в качестве упражнения.

person Damien Kick    schedule 22.05.2014