6

我需要一个用于调试目的的记录器,并且我正在使用 Boost.Log(1.54.0,在 boost.org 主页上有一个补丁)。

一切都很好,我创建了一些这样的宏:

#define LOG_MESSAGE( lvl ) BOOST_LOG_TRIVIAL( lvl )

现在是 LOG_MESSAGE( lvl ) 仅在调试模式下在 BOOST_LOG_TRIVIAL( lvl ) 中扩展并在发布时忽略的一种方式吗?

例如:

LOG_MESSAGE( critical ) << "If I read this message we're in debug mode"

编辑 我的第一次尝试是创建一个空流......我认为在发布模式下编译器会优化它......

#if !defined( NDEBUG )
#include <boost/log/trivial.hpp>
#define LOG_MESSAGE( lvl ) BOOST_LOG_TRIVIAL( lvl )
#else
#if defined( __GNUC__ )
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-value"
#endif


#include <iosfwd>
struct nullstream : public std::ostream {
    nullstream() : std::ios(0), std::ostream(0) {}
};

static nullstream g_nullstream;

#define LOG_MESSAGE( lvl ) g_nullstream

#if defined( __GNUC__ )
#pragma GCC diagnostic pop
#endif

#endif
4

4 回答 4

7

日志条目的严重性级别仅充当接收器的过滤器。接收器将根据严重性级别决定如何处理消息(打印或不打印)。但消息仍会发送。

如果您试图根本不发送消息,那么您需要重新定义LOG_MESSAGE实际上什么都不做的东西。Boost 库中可能为此提供了一些东西,否则,您必须自己编写。也许这将是一个开始:

class NullLogger
{
public:
  template <typename SeverityT> NullLogger (SeverityT) {};
  template <typename Val> NullLog& operator<< (const Val&) { return * this};
};

...进而:

#define LOG_MESSAGE (lvl) NullLogger (lvl)

但是请注意,即使没有对日志消息或组成它的表达式进行任何操作,表达式仍然会被评估。如果其中一些表达式很昂贵,您仍然会受到性能影响。例如:

LOG_MESSAGE (debug) << SomeSuperExpensiveFunction();

即使你使用NullLogger上面的,SomeSuperExpensiveFunction()仍然会被调用。

我建议作为替代添加一个在运行时评估的标志,并在运行时决定是否进行日志记录:

if (mLogStuff)
{ 
  LOG_MESSAGE (debug) << SomeSuperExpensiveFunction();
}

bool比较非常便宜,您可能会发现在未来的某一天,打开和关闭登录的功能可能会非常方便。此外,这样做意味着您不需要再添加另一个#define,这总是一件好事。

于 2013-07-11T16:13:59.497 回答
3

我喜欢约翰的NullLogger课。我要做的唯一改变如下

#define LOG_MESSAGE(lvl) while (0) NullLogger (lvl)

不幸的是,这可能会产生警告,但我希望一个体面的编译器能够消除所有相关的日志记录代码。

于 2013-08-03T07:23:48.143 回答
1

John 的NullLogger类在 MSVC 上无法正确编译,并且仍然需要SeverityT实际上不需要的 Boost 依赖项。

我建议对班级进行以下更改:

class NullLogger
{
    public:
        template <typename Val> NullLogger& operator<< (const Val&) { return *this; };
};
#define BOOST_LOG_TRIVIAL(lvl) NullLogger()
于 2021-03-17T18:49:43.567 回答
1

NullLogger可以在不定义 a或类似的情况下实现这一点:

#define TEST_LOG(lvl) \
    if constexpr(boost::log::trivial::lvl >= boost::log::trivial::MAX_LOG_LEVEL) \
        BOOST_LOG_TRIVIAL(lvl)

然后编译-DMAX_LOG_LEVEL=info以静态停用下面的所有日志消息info

另请注意,使用正确实现的宏(likeTEST_LOG但也 like )不会评估BOOST_LOG_TRIVIAL昂贵的功能:

// We either log with trace or warning severity, so this filter
// does not let any message pass
logging::core::get()->set_filter(
    logging::trivial::severity >= logging::trivial::error);

// Filtered at compile time
{
    auto start = std::chrono::steady_clock::now();
    for (size_t i = 0; i < 1000 * 1000; i++) {
        TEST_LOG(trace) << "Hello world!";
    }
    auto end = std::chrono::steady_clock::now();
    std::cerr << std::chrono::duration<double>(end-start).count() << "s" << std::endl;
    // Prints: 1.64e-07s
}

// Filtered at compile time
{
    auto start = std::chrono::steady_clock::now();
    for (size_t i = 0; i < 1000 * 1000; i++) {
        TEST_LOG(trace) << ComputeExpensiveMessage();
    }
    auto end = std::chrono::steady_clock::now();
    std::cerr << std::chrono::duration<double>(end-start).count() << "s" << std::endl;
    // Prints: 8.5e-08s
}

// Filtered at run time
{
    auto start = std::chrono::steady_clock::now();
    for (size_t i = 0; i < 1000 * 1000; i++) {
        TEST_LOG(warning) << "Hello world!";
    }
    auto end = std::chrono::steady_clock::now();
    std::cerr << std::chrono::duration<double>(end-start).count() << "s" << std::endl;
    // Prints: 0.249306s
}

// Filtered at run time
{
    auto start = std::chrono::steady_clock::now();
    for (size_t i = 0; i < 1000 * 1000; i++) {
        TEST_LOG(warning) << ComputeExpensiveMessage();
    }
    auto end = std::chrono::steady_clock::now();
    std::cerr << std::chrono::duration<double>(end-start).count() << "s" << std::endl;
    // Prints: 0.250101s
}
于 2021-09-08T20:51:13.693 回答