【问题标题】:Enable Boost.Log only on debug仅在调试时启用 Boost.Log
【发布时间】:2013-07-10 00:09:28
【问题描述】:

我需要一个用于调试目的的记录器,并且我正在使用 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

【问题讨论】:

    标签: c++ logging boost-log


    【解决方案1】:

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

    如果您试图根本不发送消息,那么您需要将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();
    

    即使你使用上面的NullLoggerSomeSuperExpensiveFunction() 仍然会被调用。

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

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 这种方法几乎没有什么好处。它可能会阻止调用 NullLogger 方法,但请查看它们:它们是在类中定义的,所以inline。它们也是空的。调用它们只是理论上的,实际上优化器将删除NullLogger 的每一条痕迹。
      【解决方案3】:

      可以在不定义NullLogger 或类似名称的情况下实现此目的:

      #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下面的所有日志消息。

      另请注意,使用正确实现的宏(如TEST_LOG,但也如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
      }
      

      【讨论】:

        【解决方案4】:

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

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

        class NullLogger
        {
            public:
                template <typename Val> NullLogger& operator<< (const Val&) { return *this; };
        };
        #define BOOST_LOG_TRIVIAL(lvl) NullLogger()
        

        【讨论】:

          猜你喜欢
          • 2012-12-28
          • 2018-08-07
          • 2012-03-06
          • 1970-01-01
          • 2015-07-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多