Question

I'm working on an application that uses an custom, platform-dependent logger. The application defines some printf-style macros:

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

...

The past few days I've been working on moving the application to use boost.log. The biggest problem I'm having is trying to retain this macro format so that only the logger internals need to be changed, since boost's logging API is implemented in iostream-style, i.e.

BOOST_LOG(logger) << "something";
  1. Is there an easy way to provide a macro that takes printf-style args and prints them to the boost logger without having to use string buffers? (I would think that having to format to a string would be a performance impact)
  2. If not, is there a way to format a string using va_args without having to #ifdef for different platform implementations of formatting functions? (this was the whole point of moving to boost.log in the first place)
Was it helpful?

Solution 2

Unfortunately, there are no clean implementations without the #ifdef statement. I know you want to port your existing logging to boost log as-is. That would be an incorrect way of doing things. printf was designed to be used for C, while boost log was designed for C++. So, my suggestion would be to use it the right way. You can make your logging macros painless than what you've shown in your code sample.

Here's my implementation of boost log where instead of having BOOST_LOG(logger) << "something";, I have it as roLOG_INFO << "something";. I believe this example comes from one of boost log's samples.

RoLog.h

#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;
    }
}

DISCLAIMER: I'm not claiming this to be a perfect piece of code. It works for me and I'm happy with it.

Let's say you would still like the option of logging the printf style, then consider the following code:

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)

DISCLAIMER: I haven't tested the above code. You might need to tweak it to make it work for you.

OTHER TIPS

Or just use Boost Format to deal with the printf format string. For example,

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;
}

Creating logger and extending this to handle multiple arguments (most probably with variadic template arguments) is left as an exercise for the reader.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top