【问题标题】:How do I set std::ios_base flags like std::left in boost log 2.0?如何在 boost log 2.0 中设置 std::ios_base 标志,如 std::left?
【发布时间】:2014-05-30 00:33:45
【问题描述】:

我有一个广泛使用 boost log 2.0 的应用程序。现在我想为该应用程序设置一些默认标志,例如std::setprecision(std::numeric_limits<double>::digits10 + 1)std::scientificstd::left。但是我该怎么做呢?一种方法是在我的主要功能的最开始创建一个记录器并创建一个虚拟日志消息。这将永久设置所需的标志。但是没有更好的方法吗?

编辑回复:“OP 应该显示实际代码。”

我有一个全局 Logging 单例,称为 L:

class L{
public:
  enum severity_level
  {
      dddebug,
      ddebug,
      debug,
      control,
      iiinfo,
      iinfo,
      info,
      result,
      warning,
      error,
      critical
  };

  typedef boost::log::sources::severity_channel_logger<
      severity_level, // the type of the severity level
      std::string // the type of the channel name
  > logger_t;
  typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > text_sink;
  boost::shared_ptr< text_sink > sink_;

  static L& get();
  static boost::shared_ptr<text_sink> sink();
  static double t0();
  static double tElapsed();
private:
  L();
  double t0_p;
  static std::string tElapsedFormat();

  L(const L&) = delete;
  void operator=(const L&) = delete;
};

它提供了一个日志接收器、严重性级别,并利用 MPI 方法在 MPI 节点之间进行同步计时。类成员的实现如下:

#include "log.h"

#include <iomanip>
#include <limits>
#include <fstream>
#include <boost/log/attributes/function.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>


namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;

#include "mpiwrap.h"
#include <mpi.h>

BOOST_LOG_ATTRIBUTE_KEYWORD(t, "Time", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(rank, "Rank", int)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", L::severity_level)

L::L():
  sink_(boost::make_shared< text_sink >()),
  t0_p(MPI_Wtime())
{

  sink_->locked_backend()->add_stream(
    boost::make_shared< std::ofstream >("log." + std::to_string(MpiWrap::getRank())));

  sink_->set_formatter
  (
    expr::stream
      << "< "
      << t << " "
      << "[p:" << rank << "] "
      << "[c:" << channel << "] "
      << "[s:" << severity << "] "
      << expr::smessage
  );

  logging::core::get()->add_sink(sink_);

  logging::core::get()->set_filter(

       (channel == "ChannelName1" && severity >= dddebug)
    || (channel == "ChannelName2" && severity >= info)
    || (channel == "ChannelName3" && severity >= result)

  );

  // Add attributes
  logging::core::get()->add_global_attribute("Time", attrs::make_function(&tElapsedFormat));
  logging::core::get()->add_global_attribute("Rank", attrs::constant<int>(MpiWrap::getRank()));
}

L& L::get(){
  static L instance;
  return instance;
}

boost::shared_ptr<L::text_sink> L::sink(){
  return get().sink_;
}

double L::t0(){
  return get().t0_p;
}

double L::tElapsed(){
  return MPI_Wtime() - t0();
}

std::string L::tElapsedFormat(){
  std::stringstream ss;
  const int prec = std::numeric_limits<double>::digits10;
  ss << std::setw(prec + 2 + 6) << std::left << std::setprecision(prec) << tElapsed();
  return ss.str();
}

std::ostream& operator<< (std::ostream& strm, L::severity_level level)
{
    static const char* strings[] =
    {
        "DBG3",
        "DBG2",
        "DBG1",
        "CTRL",
        "INF3",
        "INF2",
        "INF1",
        "RSLT",
        "WARN",
        "ERRR",
        "CRIT"
    };

    if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
        strm << strings[level];
    else
        strm << static_cast< int >(level);

    return strm;
}

现在使用:我的类通常有一个静态logger_tboost::log::sources::severity_channel_logger&lt;severity_level, std::string&gt; 的类型定义)成员

class A {
public:
    logger_t logger;
    //other stuff here
    void function_which_does_logging();
};

L::logger_t A::logger(boost::log::keywords::channel = "ClassA");

void A::function_which_does_logging(){
    //do non logging related stuff
    BOOST_LOG_SEV(logger, L::result) << "the error is: " << 0.1234567890;
    //do non logging related stuff
}

我目前对该问题的解决方案是在我的程序开头放置一个日志记录语句

int main(){
    L::logger_t logger(boost::log::keywords::channel = "init");
    BOOST_LOG_SEV(logger, L::critical) << "setting up logger" << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1);

    //do stuff
}

【问题讨论】:

  • 很少需要格式化来记录日志。如果需要格式化,那么您可以使用std::ostringstream 进行格式化,并记录预先格式化的字符串。
  • @JoachimPileborg 对于我的工作来说,使用`std::scientific.任何未启用的双重登录都不会给我任何信息,所以我想将此作为全球标准
  • 您的日志记录现在是什么样的?你有全局可访问的记录器吗?你在使用 BOOST_LOG 宏吗?
  • @WilliamKunkel 我的每个类中都有一个静态记录器成员来设置频道名称的默认值。要记录我使用 BOOST_LOG_SEV。问题是,做一次BOOST_LOG_SEV(…) &lt;&lt; std::scientific 就足以为所有记录器(源)的所有后续消息设置它
  • 窥视。 Boost Log 有很多选项可以实现这一点(我用了几种方法),但是这个问题确实缺乏任何细节来决定什么是合适的。数字将如何记录?它们是属性吗?他们只是使用流式操作进行流式传输吗?使用了哪些记录器实例?换句话说,OP 应该显示实际代码。 /cc @JoachimPileborg

标签: c++ logging boost boost-log


【解决方案1】:

@rhashimoto 很好地说明了您当前的解决方案将如何因多线程/并发日志记录操作而崩溃。我觉得最好的解决方案是定义自己的日志记录宏来替换包含流修饰符的BOOST_LOG_SEV,如下所示:

#define LOG_SCIENTIFIC(logger, sev) (BOOST_LOG_SEV(logger, sev) << std::scientific)

它可以用作BOOST_LOG_SEV 的替代品,它将数字格式化为科学格式。但是,通过您的代码并用新的自定义宏替换每个日志记录操作可能会很痛苦。除了定义自己的宏,您还可以重新定义 BOOST_LOG_SEV 以按照您的意愿行事。 boost/log/sources/severity_feature.hpp 定义BOOST_LOG_SEV 如下:

//! An equivalent to BOOST_LOG_STREAM_SEV(logger, lvl)
#define BOOST_LOG_SEV(logger, lvl) BOOST_LOG_STREAM_SEV(logger, lvl)

因为 BOOST_LOG_STREAM_SEV 仍然是公共 boost API 的一部分,所以您应该能够像这样安全地重新定义 BOOST_LOG_SEV

#define BOOST_LOG_SEV(logger, lvl) (BOOST_LOG_STREAM_SEV(logger, lvl) << std::scientific)

只要在包含 boost 日志标头后定义它,它就应该做你想做的事。但是,我建议使用具有自定义名称的宏,以便其他人清楚您的代码在做什么。

【讨论】:

    【解决方案2】:

    我不相信您当前的方法适用于所有情况,尤其是如果您的代码是线程化的。当你说一个记录格式标志可以修复多个记录器时,我感到很紧张,所以我查看了代码(record_ostream.hpp 和 record_ostream.cpp)。

    Boost Log 使用Object pool design pattern 为带有辅助结构stream_provider 的记录器提供流格式化程序。 stream_provider 的实现使用线程本地存储(当支持线程时)为每个线程使用单独的 ostream 实例池。在池中,ostream 实例会根据需要创建 - 如果一次只需要一个格式化程序,则只会创建一个。因此,如果您从单个线程进行日志记录并且如果您从未在记录其他内容的过程中记录某些内容,那么我认为您当前的解决方法将适用于当前的 Boost Log 实现。

    线程如何失败应该很明显。这是一个简单的例子,说明这如何在单个线程中失败:

    static double f(double x) {
       BOOST_LOG(my_logger::get()) << "called f with " << x;
       return x;
    }
    
    int main() {
       BOOST_LOG(my_logger::get()) << std::scientific << "format flag";
       BOOST_LOG(my_logger::get()) << "top level " << f(0.01);
       return 0;
    }
    

    产生:

    [2014-04-27 14:16:39.832008] [0x000007fff7a1c631] [info]    format flag
    [2014-04-27 14:16:39.832616] [0x000007fff7a1c631] [info]    called f with 0.01
    [2014-04-27 14:16:39.832630] [0x000007fff7a1c631] [info]    top level 1.000000e-02
    

    请注意,顶级日志条目(在第三行)的格式正确,而函数日志条目(在第二行)则不正确。这是因为在调用函数时正在使用配置的流,因此创建并使用了单独的流。

    我认为@WilliamKunkel 建议定义您自己的日志记录宏是我见过的处理此问题的最佳方式。但是,如果你真的想使用 Boost 宏,这个 hack 在 Boost 1.55 上对我有用:

    #include <iomanip>
    
    #define BOOST_LOG_DYN_LINK
    #include <boost/log/sources/global_logger_storage.hpp>
    #include <boost/log/sources/logger.hpp>
    #include <boost/log/sources/record_ostream.hpp>
    
    typedef boost::log::sources::logger logger_t;
    BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, logger_t)
    
    namespace boost {
       namespace log {
          namespace aux {
             template<>
             BOOST_FORCEINLINE record_pump<logger_t> make_record_pump(logger_t& lg, record& rec)
             {
                auto rp = record_pump<logger_t>(lg, rec);
                rp.stream() << std::scientific;
                return rp;
             }
          }
       }
    }
    
    int main() {
       BOOST_LOG(my_logger::get()) << "Logging number " << 0.01;
       return 0;
    }
    

    基本思想是专门化为宏提供流的模板函数。当它专门用于您正在使用的实际记录器类时,传递std::scientific 标志的专门实现将优先于通用实现。

    这是一个 hack,因为它取决于 Boost Log 的实现细节,并且不能保证在不同版本之间都能正常工作。在我看来,定义自己的宏是一种更好的方法。

    我曾希望boost::log::basic_formatting_stream 可以做一些事情,因为它的标题说:

    虽然basic_formatting_ostream不是从std::basic_ostream派生的,但用户不需要添加 operator&lt;&lt; 的特殊重载,因为默认情况下流将重用 std::basic_ostream 的运算符。 但是,如果需要某种类型,可以为 basic_formatting_ostream 定义 operator&lt;&lt; 的特殊重载 输出到日志时的特殊格式。

    不幸的是,这似乎只适用于类类型而不是原始类型,因为所有原始类型都有 operator&lt;&lt; 的成员函数实现。

    【讨论】:

      猜你喜欢
      • 2012-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-12
      • 2017-03-11
      • 2015-09-18
      • 2018-08-20
      相关资源
      最近更新 更多