Boost.Log:支持文件名和行号

Boost.Log: Support file name and line number

我正在尝试让我的团队远离 log4cxx 并尝试使用 Boost.Log v2。我们目前的 log4cxx pattern 相当简单:

log4cxx::helpers::Properties prop;
prop.setProperty("log4j.rootLogger","DEBUG, A1");
prop.setProperty("log4j.appender.A1","org.apache.log4j.ConsoleAppender");
prop.setProperty("log4j.appender.A1.layout","org.apache.log4j.PatternLayout");
prop.setProperty("log4j.appender.A1.layout.ConversionPattern","%d{ABSOLUTE} %-5p [%c] %m%n");
log4cxx::PropertyConfigurator::configure(prop);

但是我没有找到支持文件名和行号打印的解决方案。到目前为止,我已经找到 old post,但没有明确的解决方案(没有公认的解决方案)。我已经研究过使用那些 BOOST_LOG_NAMED_SCOPE 但它们感觉非常难看,因为当在同一函数中使用多个这些时,它们无法打印正确的行号。

我还找到了一个更简单的直接解决方案,here。但这也让人感觉很丑陋,因为它会打印完整路径,而不仅仅是基本名称(它是不可配置的)。所以完整路径和行号总是打印在固定位置(在expr::smessage之前)。

我也找到了这个 post,它看起来像一个旧的解决方案。

最后,我发现最有希望的解决方案是 here。但是它甚至不为我编译。

所以我的问题很简单:如何使用 Boost.Log v2 以最小的 formatter flexibility (no solution with MACRO and __FILE__ / __LINE__ accepted, please). I'd appreciate a solution which does not involve BOOST_LOG_NAMED_SCOPE, as described here:

打印文件名(不是完整路径)和行号

If instead you want to see line numbers of particular log records then the best way is to define a custom macro which you will use to write logs. In that macro, you can add the file name and line number as attributes to the record (you can use manipulators for that). Note that in this case you won't be able to use these attributes in filters, but you probably don't need that.

为了输出文件名和行你需要注册Scopes:

logging::core::get()->add_global_attribute("Scopes", attributes::named_scope());

并添加自定义格式化程序:

void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
    const auto cont = logging::extract< attributes::named_scope::value_type >("Scopes", rec);
    if(cont.empty())
        return;

    auto it = cont->begin();
    boost::filesystem::path path(it->file_name.c_str());
    strm << path.filename().string() << ":" << it->line << "\t" << rec[expr::smessage];
}

注意:为了使其正常工作,需要在每次记录之前清除范围容器: if(!attributes::named_scope::get_scopes().empty()) attributes::named_scope::pop_scope();

我终于在add_value的基础上找到了一个简单的解决方案。这是完整的源代码:

#include <ostream>
#include <fstream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/filesystem.hpp>

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

void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
    // Get the LineID attribute value and put it into the stream
    strm << logging::extract< unsigned int >("LineID", rec) << ": ";
    strm << logging::extract< int >("Line", rec) << ": ";
    logging::value_ref< std::string > fullpath = logging::extract< std::string >("File", rec);
    strm << boost::filesystem::path(fullpath.get()).filename().string() << ": ";

    // The same for the severity level.
    // The simplified syntax is possible if attribute keywords are used.
    strm << "<" << rec[logging::trivial::severity] << "> ";

    // Finally, put the record message to the stream
    strm << rec[expr::smessage];
}

void init()
{
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    sink->set_formatter(&my_formatter);

    logging::core::get()->add_sink(sink);
}

#define MY_GLOBAL_LOGGER(log_,sv) BOOST_LOG_SEV( log_, sv) \
  << boost::log::add_value("Line", __LINE__)      \
  << boost::log::add_value("File", __FILE__)       \
  << boost::log::add_value("Function", BOOST_CURRENT_FUNCTION)

int main(int, char*[])
{
    init();
    logging::add_common_attributes();

    using namespace logging::trivial;
    src::severity_logger< severity_level > lg;

    MY_GLOBAL_LOGGER(lg,debug) << "Keep";
    MY_GLOBAL_LOGGER(lg,info) << "It";
    MY_GLOBAL_LOGGER(lg,warning) << "Simple";
    MY_GLOBAL_LOGGER(lg,error) << "Stupid";

    return 0;
}

在我的 Linux 盒子上,我使用以下方法编译它:

$ c++ -otutorial_fmt_custom -DBOOST_LOG_DYN_LINK tutorial_fmt_custom.cpp -lboost_log -lboost_log_setup -lboost_thread -lpthread -lboost_filesystem

如果您 运行 并检查此处生成的日志文件,您会得到:

$ ./tutorial_fmt_custom && cat sample.log 
1: 61: tutorial_fmt_custom.cpp: <debug> Keep
2: 62: tutorial_fmt_custom.cpp: <info> It
3: 63: tutorial_fmt_custom.cpp: <warning> Simple
4: 64: tutorial_fmt_custom.cpp: <error> Stupid

我的解决方案基于两个输入(谢谢!):