boost log使用

1.目标

程序日志输出改进目标:

1. 用颜色区分错误和警告

2. 日志文件中用"warning"文本代替数字表示severity,便于日志中搜索

3. 合并log_setting.ini配置文件(到程序json配置文件,不使用init_from_stream)

4. 可同时输出到console和文件

 

资料未整理完,以后再补充对程序的说明。

 

2.实现

log.h


enum SeverityLevel {
    SL_TRACE = 0,
    SL_DEBUG,
    SL_INFO,
    SL_WARNING,
    SL_ERROR,
    SL_CRITICAL
};


class Log {
public:
    static void init_log();
private:
    static void init_logfile();
    static void init_console();
};

boost::log::sources::severity_logger<SeverityLevel>* get_glog();

 

log.cpp

#include "log.h"

#include<ios>
#include <boost/ref.hpp>
#include <boost/bind.hpp>
#include <boost/log/core.hpp>
#include <boost/log/common.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/core/record.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/core/null_deleter.hpp>

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


/* Define place holder attributes */
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
//BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
//BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity )
#include <boost/phoenix.hpp>
BOOST_LOG_ATTRIBUTE_KEYWORD(process_id, "ProcessID", attrs::current_process_id::value_type )
BOOST_LOG_ATTRIBUTE_KEYWORD(thread_id, "ThreadID", attrs::current_thread_id::value_type )

// Get Process native ID
attrs::current_process_id::value_type::native_type get_native_process_id(
    logging::value_ref<attrs::current_process_id::value_type,
    tag::process_id> const& pid) {
    if (pid)
        return pid->native_id();
    return 0;
}

// Get Thread native ID
attrs::current_thread_id::value_type::native_type get_native_thread_id(
    logging::value_ref<attrs::current_thread_id::value_type,
    tag::thread_id> const& tid) {
    if (tid)
        return tid->native_id();
    return 0;
}

typedef SeverityLevel severity_level;
const char *get_severity_tag(severity_level level) {
    static const char* strings[] = {
        "trace",
        "debug",
        "info",
        "waring",
        "error",
        "critical"
    };

    return static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings) ? strings[level] : std::to_string(level).c_str();
}



void coloring_formatter(
    logging::record_view const& rec, logging::formatting_ostream& strm) {
    logging::value_ref< severity_level > level = logging::extract< severity_level >("Severity", rec);
    switch(level.get()) {
    case crush::common::SL_TRACE:
    case crush::common::SL_DEBUG:
    case crush::common::SL_INFO:
        strm << "\033[32m";
        break;
    case  crush::common::SL_WARNING:
        strm << "\033[33m";
        break;
    case crush::common::SL_ERROR:
    case crush::common::SL_CRITICAL:
        strm << "\033[31m";
        break;
    default:
        break;
    }

    strm << logging::extract< unsigned int >("LineID", rec) << "|";
    auto date_time_formatter = expr::stream << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f");
    date_time_formatter(rec, strm);
    strm <<"|";
    strm<< std::dec<< logging::extract< attrs::current_process_id::value_type  >("ProcessID", rec);
    strm << "|";
    strm<< std::dec<< logging::extract< attrs::current_thread_id::value_type  >("ThreadID", rec);
    strm << "|";
    strm<< get_severity_tag(level.get());
    strm<< "|";
    strm<<"--";
    strm << rec[logging::expressions::smessage];

    // Restore the default color
    strm << "\033[0m";
}

struct severity_tag;
logging::formatting_ostream& operator<<
(
    logging::formatting_ostream& strm,
    logging::to_log_manip< severity_level, severity_tag > const& manip
) {
    severity_level level = manip.get();
    strm << get_severity_tag(level);

    return strm;
}

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(glog, boost::log::sources::severity_logger<SeverityLevel>);

void Log::init_console() {
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
    boost::shared_ptr< std::ostream > stream(&std::clog, boost::null_deleter());
    sink->locked_backend()->add_stream(stream);
    sink->set_formatter(&coloring_formatter);
    logging::core::get()->add_sink(sink);
}

void Log::init_logfile() {
    logging::add_file_log(
        keywords::file_name = "%Y-%m-%d_%5N.log",
        keywords::rotation_size = 10 * 1024 * 1024,
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
        keywords::auto_flush = true,
        keywords::open_mode =  std::ios::out | std::ios::app,
        keywords::format =
            (
                expr::stream
                << expr::attr< unsigned int >("LineID")<<"|"
                << expr::format_date_time(expr::attr< boost::posix_time::ptime >("TimeStamp"), "%Y-%m-%d, %H:%M:%S.%f") << "|"
                << boost::phoenix::bind(&get_native_process_id, process_id.or_none()) << ":"
                << boost::phoenix::bind(&get_native_thread_id, thread_id.or_none()) << "|"
                << "[" << expr::attr< severity_level, severity_tag >("Severity")
                << "] " << expr::smessage
            )
    );
}
void Log::init_log() {
    logging::core::get()->set_filter([](const logging::attribute_value_set& attr_set) {
        return attr_set["Severity"].extract<severity_level>() >= SL_WARNING;
    });
    init_logfile();
    init_console();
    logging::add_common_attributes();
}

boost::log::sources::severity_logger<SeverityLevel>* get_glog() {
    return &(glog::get());
}


3.使用

#include "log.h"

void test_log() {
    Log::init();
    BOOST_LOG_SEV(*(crush::common::get_glog()), SL_TRACE) << "A trace severity message";
    BOOST_LOG_SEV(*(crush::common::get_glog()), SL_DEBUG) << "A debug severity message";
    BOOST_LOG_SEV(*(crush::common::get_glog()), SL_INFO) << "An informational severity message";
    BOOST_LOG_SEV(*(crush::common::get_glog()), SL_WARNING) << "A warning severity message";
    BOOST_LOG_SEV(*(crush::common::get_glog()), SL_ERROR) << "An error severity message";
    BOOST_LOG_SEV(*(crush::common::get_glog()), SL_CRITICAL) << "A fatal severity message";


    LOG_TRACE<<"this a trace";
    LOG_ERROR<<"this a error";
    return;
}

4.资料

4.1Howto

How to add color coding to boost::log console output?
https://stackoverflow.com/questions/38309479/how-to-add-color-coding-to-boostlog-console-output

A simple, customized logger, based on Boost.Log v2
http://gernotklingler.com/blog/simple-customized-logger-based-boost-log-v2/

how to print ProcessID and ThreadID in dec-format with boost.log
https://stackoverflow.com/questions/27597196/how-to-print-processid-and-threadid-in-dec-format-with-boost-log

how to customize “TimeStamp” format of Boost.Log
https://stackoverflow.com/questions/5947018/how-to-customize-timestamp-format-of-boost-log

How to use boost::log::expressions::format_date_time in a custom formatting function?
https://stackoverflow.com/questions/24287547/how-to-use-boostlogexpressionsformat-date-time-in-a-custom-formatting-func

boost log, why not print threadid and processid
https://stackoverflow.com/questions/46337337/boost-log-why-not-print-threadid-and-processid

Boost set_filter is not working
https://stackoverflow.com/questions/29707017/boost-set-filter-is-not-working

boost.log : using c++11 lambda expression to filter severity level
https://stackoverflow.com/questions/32399608/boost-log-using-c11-lambda-expression-to-filter-severity-level

Boost log and severity / local attributes

https://stackoverflow.com/questions/35895199/boost-log-and-severity-local-attributes

支持__FILE__,__LINE__

boost.log要点笔记

https://www.cnblogs.com/liaocheng/p/4222885.html

 

4.2官方文档

Setting up sinks
https://www.boost.org/doc/libs/1_67_0/libs/log/doc/html/log/tutorial/sinks.html

Adding more information to log: Attributes
https://www.boost.org/doc/libs/1_67_0/libs/log/doc/html/log/tutorial/attributes.html
https://www.boost.org/doc/libs/1_67_0/libs/log/doc/html/log/detailed/expressions.html#log.detailed.expressions.attr

Sink backends
https://www.boost.org/doc/libs/master/libs/log/doc/html/log/detailed/sink_backends.html
Filters
http://boost-log.sourceforge.net/libs/log1/doc/html/log/detailed/filters.html

Log record formatting

https://www.boost.org/doc/libs/1_67_0/libs/log/doc/html/log/tutorial/formatters.html

logging::init_from_settings,

https://www.boost.org/doc/libs/1_67_0/libs/log/doc/html/log/detailed/utilities.html#log.detailed.utilities.setup.filter_formatter

 

5.经验

5.1使用logging::formatter_factory

#日志输出格式
Format=[%TimeStamp% PID:%ProcessID% ThreadID:%ThreadID%] [%Severity%] %MyScopes% %Message%"

注意%MyScopes%

struct ScopeListFormatter {
    typedef attrs::named_scope::value_type scope_stack;

    explicit ScopeListFormatter(logging::attribute_name const &name) :
        name_(name) {
    }

	/// @notes
	///  rec.attribute_values()是attrs::named_scope::value_type吗?
	/// 代码的运行效果是:%MyScopes%处输出有颜色的当前时间. 
	///  ? 无法控制日志输出的其它项,如%Message%
    void operator()(logging::record_view const &rec, logging::formatting_ostream &strm) const {
        // We need to acquire the attribute value from the log record
        logging::value_ref< severity_level > level = logging::extract< severity_level >("Severity", rec);
        if (level==SL_ERROR)
            strm<< "\033[31m";
        logging::visit<scope_stack>(name_,
                                    rec.attribute_values(),
                                    boost::bind(&ScopeListFormatter::format, _1, boost::ref(strm)));
        strm<<"\033[0m";
    }

private:
    //! This is where our custom formatting takes place
    static void format(scope_stack const &scopes, logging::formatting_ostream &strm) {
        using namespace std::chrono;
        system_clock::time_point time_now = system_clock::now();
        microseconds duration_in_mics = duration_cast<microseconds>(time_now.time_since_epoch());
        strm << "[" << duration_in_mics.count() << "]";
        scope_stack::const_iterator it = scopes.begin(), end = scopes.end();
        /// @notes 
		/// .scopes是空的,怎么才能加入元素?
		/// . it->scope_name,it->file_name的含义和使用? 
        for (; it != end; ++it) {
            strm << "\t" << it->scope_name << " [" << it->file_name << ":" << it->line << "]\n";
        }
    }

private:
    logging::attribute_name name_;
};

class MyScopesFormatterFactory :
    public logging::formatter_factory<char> {
public:
//
//     * This function creates a formatter for the MyScopes attribute.
//     * It effectively associates the attribute with the scope_list_formatter class
//
    formatter_type create_formatter(
        logging::attribute_name const &attr_name, args_map const &args) {
        return formatter_type(ScopeListFormatter(attr_name));
    }
};

调用代码:

logging::register_formatter_factory("MyScopes", boost::make_shared<MyScopesFormatterFactory>());
logging::core::get()->add_global_attribute("MyScopes", attrs::named_scope());

5.2使用logging::init_from_stream

[Core]
#是否开启Log
DisableLogging=false

[Sinks.TextFileSettings] 
#输出到哪,支持TextFile Console
Destination=Console

#过滤日志等级
#trace = 0, debug = 1, info = 2, warning = 3, error = 4, critical = 5
Filter="%Severity% >= 0"

#输出的文件名
FileName="%Y-%m-%d_%5N.log"

#单个log文件大小
RotationSize=204800000

#产生新的log文件时间点
RotationTimePoint="00:00:00"

#是否开启追加
Append=true

#是否自动刷新
AutoFlush=true

#日志输出格式
Format="[%TimeStamp% PID:%ProcessID% ThreadID:%ThreadID%] [%Severity%] %MyScopes% %Message%"

#是否开启异步
Asynchronous=false

使用代码:

std::ifstream settings(filepath);
logging::init_from_stream(settings);

5.3自定义severity

severity_level类型
不同的severity_level定义,coloring_formatter中level的内容不同.        

typedef int severity_level; ///level有内容,正确
typedef SeverityLevel severity_level; ///< level.get(),断言抛出异常.

void coloring_formatter(
    logging::record_view const& rec, logging::formatting_ostream& strm) {
    logging::value_ref< severity_level > level = logging::extract< severity_level >("Severity", rec);
    switch(level.get()) {

  异常的原因是未指定 SeverityLevel参数

修改

boost::log::sources::severity_logger<>* get_glog();
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(glog, boost::log::sources::severity_logger<>);   

 为:

boost::log::sources::severity_logger<SeverityLevel>* get_glog();
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(glog, boost::log::sources::severity_logger<SeverityLevel>);      

5.4 logging::formatter

console的格式化输出的另外一种方式.         

void Log::init_console() {
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
    boost::shared_ptr< std::ostream > stream(&std::clog, boost::null_deleter());
    sink->locked_backend()->add_stream(stream);
 //   sink->set_formatter(&coloring_formatter);
    /// @note 是phoneix函数对象?
    logging::formatter formatter = expr::stream
                                   << std::setw(7) << std::setfill('0') << line_id << std::setfill(' ') << " | "
                                   << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
                                   << "[" << logging::trivial::severity << "]"
                                   << " - " << expr::smessage;
    sink->set_formatter(formatter)
    
    logging::core::get()->add_sink(sink);
}

boost 的函数式编程库 Phoenix入门学习
https://blog.csdn.net/doon/article/details/9119601
        

5.5 BOOST_LOG_ATTRIBUTE_KEYWORD

查看宏展开代码
创建test2.cpp,内容如下:

#include <boost/log/attributes.hpp>
#include <boost/log/expressions.hpp>

BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)

编译:

  g++ -std=c++11 -g -E -P  -c ./test2.cpp > b.txt

查看b.txt内容:
line_id,timestamp展开后的定义:

namespace tag {
struct line_id : public ::boost::log::expressions::keyword_descriptor {
    typedef unsigned int value_type;
    static ::boost::log::attribute_name get_name() {
        return ::boost::log::attribute_name("LineID");
    }
};
} 
typedef ::boost::log::expressions::attribute_keyword< tag::line_id > line_id_type;
 const line_id_type line_id = {};
 
namespace tag {
struct timestamp : public ::boost::log::expressions::keyword_descriptor {
    typedef boost::posix_time::ptime value_type;
    static ::boost::log::attribute_name get_name() {
        return ::boost::log::attribute_name("TimeStamp");
    }
};
} 

typedef ::boost::log::expressions::attribute_keyword< tag::timestamp > timestamp_type; 
const timestamp_type timestamp = {};

相关宏:
文件:/usr/local/include/boost/log/expressions/keyword.hpp

#define BOOST_LOG_ATTRIBUTE_KEYWORD(keyword_, name_, value_type_)\
    BOOST_LOG_ATTRIBUTE_KEYWORD_IMPL(keyword_, name_, value_type_, tag)
    
#define BOOST_LOG_ATTRIBUTE_KEYWORD_IMPL(keyword_, name_, value_type_, tag_ns_)\
    BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE_IMPL(keyword_, name_, value_type_, tag_ns_)\
    const BOOST_PP_CAT(keyword_, _type) keyword_ = {};    

默认属性:    

namespace boost {
namespace log { inline namespace v2s_mt_posix {
namespace aux {
namespace default_attribute_names {
 attribute_name severity();
 attribute_name channel();
 attribute_name message();
 attribute_name line_id();
 attribute_name timestamp();
 attribute_name process_id();
 attribute_name thread_id();
}
}
}}
}

5.6 keywords::format指定    

void Log::init_logfile() {
    logging::add_file_log(
        keywords::file_name = "%Y-%m-%d_%5N.log",
        keywords::rotation_size = 10 * 1024 * 1024,
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
        keywords::auto_flush = true,
        keywords::open_mode =  std::ios::out | std::ios::app,
        /// @notes 属性名称方式
 //       keywords::format = "[%TimeStamp% PID:%ProcessID% ThreadID:%ThreadID%] [%Severity%]  %Message%"
         // @question 以下是lambda还是phoneix?
        keywords::format =
            (
                expr::stream
                << expr::attr< unsigned int >("LineID")<<"|"
                << expr::format_date_time(expr::attr< boost::posix_time::ptime >("TimeStamp"), "%Y-%m-%d, %H:%M:%S.%f") << "|"
                << boost::phoenix::bind(&get_native_process_id, process_id.or_none()) << ":"
                << boost::phoenix::bind(&get_native_thread_id, thread_id.or_none()) << "|"
      /// @question 以下2行代码输出的内容怎么是空的?
//         << logging::expressions::attr<logging::attributes::current_thread_id::value_type>("ThreadID") << ":"
//      << logging::expressions::attr<logging::attributes::current_process_id::value_type>("ProcessID") <<  "|"
                << "[" << expr::attr< severity_level, severity_tag >("Severity")
                << "] " << expr::smessage
            )
    );
}    

5.7add_global_attribute

    boost::shared_ptr< logging::core > core = logging::core::get();
    core->add_global_attribute("LineID", attrs::counter< unsigned int >(1));
    core->add_global_attribute("TimeStamp", attrs::local_clock());

5.8set_filter

方法1:

  logging::core::get()->set_filter(
      logging::trivial::severity >= logging::trivial::info
  );

方法2:对于自定义severity级别  

  logging::core::get()->set_filter([](const logging::attribute_value_set& attr_set) {
      return attr_set["Severity"].extract<severity_level>() >= SL_WARNING;
  })  

5.9logging::init_from_settings

以下代码未验证

void init_logging() {
    logging::settings setts;

    setts["Core"]["Filter"] = "%Severity% >= warning";
    setts["Core"]["DisableLogging"] = false;

    // Subsections can be referred to with a single path
    setts["Sinks.Console"]["Destination"] = "Console";
    setts["Sinks.Console"]["Filter"] = "%Severity% >= fatal";
    setts["Sinks.Console"]["AutoFlush"] = true;

    // ...as well as the individual parameters
    setts["Sinks.File.Destination"] = "TextFile";
    setts["Sinks.File.FileName"] = "MyApp_%3N.log";
    setts["Sinks.File.AutoFlush"] = true;
    setts["Sinks.File.RotationSize"] = 10 * 1024 * 1024; // 10 MiB

    logging::init_from_settings(setts);
}

猜你喜欢

转载自blog.csdn.net/wherwh/article/details/84453710