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,
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);
}