Boost.Log模块学习
该笔记用来记录学习boost库的日志模块,方便在后面的使用过程中翻阅查找。参考视频Boost日志库快速入门。
首先拿出log的框架设计图(祭天),后面会反复参考这个东西。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aD9oMVbj-1625902858707)(https://www.boost.org/doc/libs/1_63_0/libs/log/doc/html/images/log/Design.png)]
1. 快速示例
示例源码
#include <boost/log/trivial.hpp>
int main(int, char*[])
{
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
示例输出
[2021-07-08 15:39:05.226965] [0x000080ac] [trace] A trace severity message
[2021-07-08 15:39:05.227964] [0x000080ac] [debug] A debug severity message
[2021-07-08 15:39:05.227964] [0x000080ac] [info] An informational severity message
[2021-07-08 15:39:05.227964] [0x000080ac] [warning] A warning severity message
[2021-07-08 15:39:05.227964] [0x000080ac] [error] An error severity message
[2021-07-08 15:39:05.227964] [0x000080ac] [fatal] A fatal severity message
格式含义
[时间戳] [线程id] [日志级别] 打印信息
这里的日志级别(递增)使用规范如下
- trace 最低级,用来打印一些执行信息,比如执行某些函数
- debug 用来做一些调试输出
- info 打印一些比较重要的阶段性信息
- warning 打印一些可能不符合程序执行的潜在隐患
- error 打印一些不影响程序继续执行的错误
- fatal 通常是程序崩溃时打印的提示语句
这里的BOOST_LOG_TRIVIAL是一个宏定义,该宏会根据输入的日志级别,生成指定的类流对象,该对象支持插入操作符,将输入的信息输出到控制台。
2. 简单日志过滤
示例源码
#include <boost/log/trivial.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
namespace logging = boost::log;
void init() {
// 设置过滤器
logging::core::get()->set_filter
(
// 这里是一个lambda表达式 只有大于info级别的日志才会输出
logging::trivial::severity >= logging::trivial::info
);
}
int main(int, char*[])
{
init();
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
示例输出
[2021-07-08 15:57:58.849685] [0x00004e00] [info] An informational severity message
[2021-07-08 15:57:58.851685] [0x00004e00] [warning] A warning severity message
[2021-07-08 15:57:58.851685] [0x00004e00] [error] An error severity message
[2021-07-08 15:57:58.851685] [0x00004e00] [fatal] A fatal severity message
这里的**logging::core::get()**获取到了全局的核心单例日志对象,因此该过滤是对全局日志对象的设置。
3. 设置输出槽
基本步骤
- 创建输出槽
- 在日志核心中注册已经创建的输出槽
设置输出槽的注意事项
- 一般在程序的启动阶段设置输出槽
- 快速日志输出使用库内建的输出槽作为默认输出
- 一旦用户向日志核心添加了其他的输出槽,将不再使用默认的输出槽
示例源码
#include <boost/log/trivial.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
namespace logging = boost::log;
void init() {
// 设置日志输出文件
logging::add_file_log("E:\\temp\\sample.log");
// 设置过滤器
logging::core::get()->set_filter
(
// 只有大于info级别的日志才会输出
logging::trivial::severity >= logging::trivial::info
);
}
int main(int, char*[])
{
init();
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
示例输出
可以看到,虽然有输出日志信息,但是格式跟默认的格式不符,没有了时间戳、线程id以及日志级别。要想设置地更为复杂,可以继续往下看。
示例源码
#include <boost/log/trivial.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
namespace logging = boost::log;
namespace keywords = boost::log::keywords;
namespace sinks = boost::log::sinks;
void init() {
// 设置日志输出文件
logging::add_file_log(
keywords::file_name = "E:\\temp\\sample_%N.log", // 以smaple_N 为模板 N从0开始递增,配合下面的rotation使用
keywords::rotation_size = 1 * 1024 * 1024 , // 每1M归一次档,生成一个日志文件
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), // 每次 00:00:00(时间点) 生成一个归档
keywords::format = "[%TimeStamp%]: %Message%" // 输出日志的格式 [时间戳]: 打印信息
);
logging::add_console_log( // 输出到控制台
std::clog,
keywords::format = "[%TimeStamp%]: %Message%"
);
// 设置过滤器
logging::core::get()->set_filter
(
// 只有大于info级别的日志才会输出
logging::trivial::severity >= logging::trivial::info
);
// 添加一些默认属性,否则上面设置的东西可能不生效,如时间戳
logging::add_common_attributes();
}
int main(int, char*[])
{
init();
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
示例输出
这里就不展示自动归档了,很简单,用一个循环或者跳时间试一下就好了。
如果说想要再添加一个文件用来保存日志的话,就使用logging::add_file_log函数再设置一个日志文件就可以了。
手动设置输出槽也是比较简单的,参考视频中的项目工程
示例源码
#include <fstream>
#include <iostream>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#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/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/core/null_deleter.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
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 >("E:\\temp\\sample.log"));
// 同时添加输出到屏幕
sink->locked_backend()->add_stream(boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// 把输出槽注册到日志库核心
logging::core::get()->add_sink(sink);
}
int main()
{
init();
src::logger lg;
BOOST_LOG(lg) << "Hello world!";
return 0;
}
输出的话就比较简单了,这里把输出结果既输出到了控制台,又输出到了文件。
4. 创建日志源
日志源,照我的理解就是创建一个用来向日志处理层发送数据的一个对象。
以创建全局日志源为例
#include <boost/log/trivial.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
namespace logging = boost::log;
namespace keywords = boost::log::keywords;
namespace sinks = boost::log::sinks;
namespace src = boost::log::sources;
// 定义一个全局的logger标签 my_logger
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt)
void init() {
// 这里的初始化都是对于log处理层而言,并不牵扯到日志源
// 设置日志输出文件
logging::add_file_log(
keywords::file_name = "E:\\temp\\sample_%N.log", // 以smaple_N 为模板 N从0开始递增,配合下面的rotation使用
keywords::rotation_size = 1 * 1024 * 1024 , // 每1M归一次档,生成一个日志文件
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), // 每次 00:00:00(时间点) 生成一个归档
keywords::format = "[%TimeStamp%]: %Message%"// 输出日志的格式 [时间戳]: 打印信息
);
设置过滤器
//logging::core::get()->set_filter
//(
// // 只有大于info级别的日志才会输出
// logging::trivial::severity >= logging::trivial::info
//);
// 添加一些默认属性,否则上面设置的东西可能不生效,如时间戳
logging::add_common_attributes();
}
int main(int, char*[])
{
init();
// BOOST_LOG_TRIVIAL 这个宏在输出日志时会使用默认的日志源
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
// =================================================================================
// 获取全局的logger
src::logger_mt& lg_global = my_logger::get();
// BOOST_LOG_SEV(lg, logging::trivial::severity_level::info) << "hello world";
BOOST_LOG(lg_global) << "hello world";
// =================================================================================
// 定义局部的logger
src::logger lg_local;
// 手动logger输出
logging::record rec = lg_local.open_record();
if (rec)
{
logging::record_ostream strm(rec);
strm << "Hello, World!";
strm.flush();
lg_local.push_record(boost::move(rec));
}
// =================================================================================
return 0;
}
这里注意要将log过滤器那一部分注释掉,不然可能打印不出来hello world
示例结果
5. 设置属性
属性可以是与日志产生相关的信息,如当前的时间、行号、作用域等。属性既可以是固定的值,也可以是一个值生成器。根据作用域进行行划分,属性可以依次分为日志源级属性、线程级属性、全局属性等。这三种属性最终进行汇总,传递到槽。同一作用域内的属性必须不同名,不同作用域的属性可以同名,不同作用域的同名属性按照就近原则进行赋值。
常用属性:行号(日志)、时间戳、线程id、进行id。使用logging::add_common_attributes()进行添加。某些属性会在日志源创建时进行添加,如severity_logger。
示例源码
#include <ostream>
#include <fstream>
#include <iomanip>
#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/attributes.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.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/date_time/posix_time/posix_time.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;
// 手动定义日志级别
enum severity_level
{
normal,
notification,
warning,
error,
critical
};
// 这里是提供占位符 方便后面在设置格式时使用
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(scope, "Scope", attrs::named_scope::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, "Timeline", attrs::timer::value_type)
// 因为手动定义了日志级别,为了使得ostream函数能够支持自定义的日志级别,需要提供一个friend函数
std::ostream& operator<< (std::ostream& strm, severity_level level)
{
static const char* strings[] =
{
"normal",
"notification",
"warning",
"error",
"critical"
};
if (static_cast<std::size_t>(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast<int>(level);
return strm;
}
void logging_function()
{
//BOOST_LOG_NAMED_SCOPE("named_scope_logging");
// 这里直接获取到该函数的定义
BOOST_LOG_FUNCTION();
// 定义局部的logger
src::severity_logger< severity_level > slg;
BOOST_LOG_SEV(slg, normal) << "A regular message";
BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
}
void tagged_logging()
{
BOOST_LOG_FUNCTION();
src::severity_logger< severity_level > slg;
BOOST_LOG_SEV(slg, normal) << "normal log";
// 日志源属性 添加tag
slg.add_attribute("Tag", attrs::constant< std::string >("Important"));
BOOST_LOG_SEV(slg, normal) << "normal log with tag";
}
void nested_logging(src::logger & lg)
{
Sleep(100);
BOOST_LOG(lg) << "睡眠100ms";
Sleep(200);
BOOST_LOG(lg) << "睡眠200ms";
}
void timed_logging()
{
// 开始计时
BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", attrs::timer());
src::logger lg;
BOOST_LOG(lg) << "开始对嵌套函数计时";
nested_logging(lg);
BOOST_LOG(lg) << "结束对嵌套函数计时";
}
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 >("E:\\temp\\sample.log"));
sink->set_formatter
(
expr::stream
<< std::hex << std::setw(8) << std::setfill('0') << line_id << std::dec << std::setfill(' ')
<< expr::if_(expr::has_attr(timeline)) // 添加时间部分的格式化
[
expr::stream << "[" << timeline << "] "
]
<< ": <" << severity << ">\t"
<< "(" << scope << ")\t"
<< expr::if_(expr::has_attr(tag_attr)) //添加标签部分的格式化
[
expr::stream << "[" << tag_attr << "] "
]
<< expr::smessage
);
logging::core::get()->add_sink(sink);
// 添加全局属性
logging::add_common_attributes();
logging::core::get()->add_global_attribute("Scope", attrs::named_scope());
}
int main()
{
init();
logging_function();
tagged_logging();
timed_logging();
}
示例输出
这里基本上就是教学视频中的项目,添加了一些注释,并把三个项目合到了一起,展示了timeline、tag、severity三个属性的手动添加。只是格式稍微有点儿丑,后面可以自己再格式化亿下。
6. 日志格式化
存在着很多的格式化方式
Lambda格式化器
示例
expr::stream
<< std::hex << std::setw(8) << std::setfill('0') << expr::attr<unsigned int>("LineID") << std::dec << std::setfill(' ')
<< expr::if_(expr::has_attr(timeline)) // 添加时间部分的格式化
[
expr::stream << "[" << timeline << "] "
]
<< ": <" << severity << ">\t"
<< "(" << scope << ")\t"
<< expr::if_(expr::has_attr(tag_attr)) //添加标签部分的格式化
[
expr::stream << "[" << tag_attr << "] "
]
<< expr::smessage
expr = boost::log::expressions。这里的stream是一个流的占位符, expr::attr(“LineID”)、severity、timeline、tag_attr、smessage都是将要输出到流里面的内容,timeline在前面介绍过是通过BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, “Timeline”, attrs::timer::value_type)定义的一个替代符号。
Boost.Format格式化器
示例
expr::format("%1%: <%2%> %3%")
%expr::attr<unsigned int>("LineID")
%logging::trivial::severity
%expr::smessage
一看就明白的,不需要太多的介绍,相当于就是使用占位符进行格式化
专业格式化器
示例
expr::stream
<< expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%M-%d %H:%M:%S")
<< ": <" << logging::trivial::severity << ">"
<< expr::smessage
这里的expr::format_date_timeboost::posix_time::ptime(“TimeStamp”, “%Y-%M-%d %H:%M:%S”)就是一个专业格式化器,可以对时间进行特殊的处理。
模板字符串格式化器
示例
logging::add_file_log(
keywords::file_name = "E:\\temp\\sample.log",
keywords::format = "[%TimeStamp%]: %Message%"
);
就简单的那个使用例子,用%%嵌入属性的名称。sink->set_formatter不能接受模板字符串作为参数,需要使用parse_formatter函数进行解析。
自定义格式化器
示例
// 定义
void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
strm << logging::extract< unsigned int >("LineID", rec) << ": ";
strm << "<" << rec[logging::trivial::severity] << "> ";
strm << rec[expr::smessage];
}
// 使用
sink->set_formatter(&my_formatter);
感觉应该用不到这么高级的定义,就了解下算了。
所有的示例源码
#include <fstream>
#include <iostream>
#include <iomanip>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/core/null_deleter.hpp>
#include <boost/log/support/date_time.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void initLambdaFormat() {
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 >("E:\\temp\\sample.log"));
// 这里同时把日志输出到控制台
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter())
);
sink->set_formatter
(
expr::stream
// 行号将以前导为0的8位十六进制显示
<< std::hex << std::setw(8) << std::setfill('0') << expr::attr< unsigned int >("LineID")
// 注意这里因为使用的是logging::trivial::severity 因此才可以直接使用BOOST_LOG_TRIVIAL
<< ": <" << logging::trivial::severity
<< "> " << expr::smessage
);
logging::core::get()->add_sink(sink);
logging::add_common_attributes();
}
void lambdaFormatTest() {
initLambdaFormat();
BOOST_LOG_TRIVIAL(info) << "in lambdaFormatTest";
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}
void initBoostFormat() {
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 >("E:\\temp\\sample.log"));
// 这里同时把日志输出到控制台
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter())
);
sink->set_formatter
(
expr::format("%1%: <%2%> %3%")
% expr::attr<unsigned int>("LineID")
% logging::trivial::severity
%expr::smessage
);
logging::core::get()->add_sink(sink);
logging::add_common_attributes();
}
void boostFormatTest() {
initBoostFormat();
BOOST_LOG_TRIVIAL(info) << "in boostFormatTest";
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}
void initSpecializedFormat() {
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 >("E:\\temp\\sample.log"));
// 这里同时把日志输出到控制台
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter())
);
sink->set_formatter
(
expr::stream
<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< ": <" << logging::trivial::severity
<< "> " << expr::smessage
);
logging::core::get()->add_sink(sink);
logging::add_common_attributes();
}
void specializedFormatTest() {
initSpecializedFormat();
BOOST_LOG_TRIVIAL(info) << "in specializedFormatTest";
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}
void initStringFormat() {
logging::add_file_log(
keywords::file_name = "E:\\temp\\sample.log",
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::add_console_log(
std::clog,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::add_common_attributes();
}
void stringFormatTest() {
initStringFormat();
BOOST_LOG_TRIVIAL(info) << "in stringFormatTest";
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}
void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
strm << logging::extract< unsigned int >("LineID", rec) << ": ";
strm << "<" << rec[logging::trivial::severity] << "> ";
strm << rec[expr::smessage];
}
void initCustomFormat() {
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 >("E:\\temp\\sample.log"));
// 这里同时把日志输出到控制台
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter())
);
sink->set_formatter(&my_formatter);
logging::core::get()->add_sink(sink);
logging::add_common_attributes();
}
void customFormatTest() {
initCustomFormat();
BOOST_LOG_TRIVIAL(info) << "in customFormatTest";
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}
int main(int, char*[])
{
lambda foramt 测试
//lambdaFormatTest();
boost format 测试
//boostFormatTest();
specialized format 测试
//specializedFormatTest();
string format 测试
//stringFormatTest();
// custom format 测试
customFormatTest();
return 0;
}
7. 复杂日志过滤
日志过滤这部分比较简单,就直接上源码了
#include <cstddef>
#include <string>
#include <ostream>
#include <fstream>
#include <iomanip>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/phoenix/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/basic_logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/utility/value_ref.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;
//自定义严重等级
enum severity_level
{
normal,
notification,
warning,
error,
critical
};
//重载<<运算符将严重等级插入流中
std::ostream& operator<< (std::ostream& strm, severity_level level)
{
static const char* strings[] =
{
"normal",
"notification",
"warning",
"error",
"critical"
};
if (static_cast<std::size_t>(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast<int>(level);
return strm;
}
//定义属性关键字
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
//自定义过滤函数
bool my_filter(logging::value_ref< severity_level, tag::severity > const& level,
logging::value_ref< std::string, tag::tag_attr > const& tag)
{
return level >= warning || tag == "IMPORTANT_MESSAGE";
}
void init()
{
//为所有槽设置公共的格式化器
logging::formatter fmt1 = expr::stream
<< "fmt1: "
<< std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
<< ": <" << severity << ">\t"
<< expr::if_(expr::has_attr(tag_attr))
[
expr::stream << "[" << tag_attr << "] "
]
<< expr::smessage;
logging::formatter fmt2 = expr::stream
<< "fmt2: "
<< std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
<< ": <" << severity << ">\t"
<< expr::if_(expr::has_attr(tag_attr))
[
expr::stream << "[" << tag_attr << "] "
]
<< expr::smessage;
//初始化槽
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 >("E:\\temp\\full.log"));
sink->set_formatter(fmt1);
logging::core::get()->add_sink(sink);
sink = boost::make_shared< text_sink >();
sink->locked_backend()->add_stream(
boost::make_shared< std::ofstream >("E:\\temp\\important.log"));
//sink->locked_backend()->auto_flush(true); // 加上这一句之后 每出现一条重要信息后 都会flush一次
sink->set_formatter(fmt2);
//sink->set_filter(severity >= warning || (expr::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));
namespace phoenix = boost::phoenix;
sink->set_filter(phoenix::bind(&my_filter, severity.or_none(), tag_attr.or_none()));
logging::core::get()->add_sink(sink);
//添加常用属性
logging::add_common_attributes();
}
void logging_function()
{
src::severity_logger< severity_level > slg;
BOOST_LOG_SEV(slg, normal) << "常规消息";
BOOST_LOG_SEV(slg, warning) << "警告消息";
BOOST_LOG_SEV(slg, critical) << "致命错误消息";
{
BOOST_LOG_SCOPED_THREAD_TAG("Tag", "IMPORTANT_MESSAGE");
BOOST_LOG_SEV(slg, normal) << "重要消息";
}
}
int main(int, char*[])
{
init();
logging_function();
return 0;
}
有几个点还是需要注意下的
- 可以对不同的日志文件使用不同的输出格式
- 可以对不同的日志文件使用不同的过滤器
- 可以通过打标签的方式控制不同的日志流向
过滤的存在使得日志可以有更多灵活的处理方式
8. 宽字节日志
主要就是牵扯到不同的操作系统、软件打开日志时使用的阅读格式可能时不同的,为了应对这一点,sink在后端加了一个字符转换的功能
这里直接贴视频里的代码吧~
示例源码
#include <iostream>
#include <boost/locale/generator.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/support/date_time.hpp>
namespace logging = boost::log;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
enum severity_level
{
normal,
notification,
warning,
error,
critical
};
template< typename CharT, typename TraitsT >
inline std::basic_ostream< CharT, TraitsT >& operator<< (
std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl)
{
static const char* const str[] =
{
"normal",
"notification",
"warning",
"error",
"critical"
};
if (static_cast<std::size_t>(lvl) < (sizeof(str) / sizeof(*str)))
strm << str[lvl];
else
strm << static_cast<int>(lvl);
return strm;
}
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
void init_logging()
{
boost::shared_ptr< sinks::synchronous_sink< sinks::text_file_backend > > sink = logging::add_file_log
(
"sample.log",
keywords::format = expr::stream
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f")
<< " <" << severity.or_default(normal)
<< "> " << expr::message
);
std::locale loc = boost::locale::generator()("en_US.UTF-8");
sink->imbue(loc);
logging::add_common_attributes();
}
void test_narrow_char_logging()
{
src::logger lg;
BOOST_LOG(lg) << "Narrow character message";
}
void test_wide_char_logging()
{
src::wlogger lg;
BOOST_LOG(lg) << L"Wide character message";
const wchar_t chinese_chars[] = { 0x5BBD, 0x5B57, 0x7B26, 0 };
BOOST_LOG(lg) << chinese_chars;
src::wseverity_logger< severity_level > slg;
BOOST_LOG_SEV(slg, normal) << L"A normal severity message";
BOOST_LOG_SEV(slg, warning) << L"A warning severity message";
BOOST_LOG_SEV(slg, error) << L"An error severity message";
}
int main(int argc, char* argv[])
{
init_logging();
test_narrow_char_logging();
test_wide_char_logging();
return 0;
}
9. 通过网络发送日志
这一部分可能有些人会用到~~(你说的有些人不会是你自己吧)~~
这里的话主要是用到了一个syslog_backend,其实使用的话也比较简单,官方的已经给出了使用样例(<boost源码目录>/libs/log/example/syslog)
因此这里的话就贴一下我自己的一个代码
#include <fstream>
#include <iostream>
#include <iomanip>
#include <boost/core/null_deleter.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sinks/syslog_backend.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.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 keywords = boost::log::keywords;
namespace attrs = boost::log::attributes;
void init()
{
// ======================================================
// 创建文本输出槽
typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;
boost::shared_ptr<text_sink> local_sink = boost::make_shared<text_sink>();
// 添加写文件的流
local_sink->locked_backend()->add_stream(
boost::make_shared< std::ofstream >("E:\\temp\\sample.log"));
local_sink->set_formatter
(
expr::stream
<< "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") << "]"
<< "(" << logging::trivial::severity << ") : "
<< expr::message
);
// 同时添加输出到屏幕
local_sink->locked_backend()->add_stream(boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// 把输出槽注册到日志库核心
logging::core::get()->add_sink(local_sink);
// ======================================================
// 创建网络输出槽
typedef sinks::synchronous_sink<sinks::syslog_backend> net_sink;
boost::shared_ptr<net_sink> remote_sink = boost::make_shared<net_sink>();
remote_sink->set_formatter
(
expr::stream
<< "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") << "]"
<< "(" << logging::trivial::severity << ") : "
<< expr::message
);
remote_sink->locked_backend()->set_target_address("localhost", 1024); // 这个地方的端口的话 可以参数化定义
// 只把trace级别的信息进行发送
remote_sink->set_filter(logging::trivial::severity == logging::trivial::severity_level::trace);
// 把输出槽注册到日志库核心
logging::core::get()->add_sink(remote_sink);
// ======================================================
// 添加常用属性
logging::add_common_attributes();
}
int main()
{
init();
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}
除此之外贴一个服务器端代码,方便做测试,这部分有参考文章
#include <boost/asio.hpp>
#include <iostream>
using namespace boost::asio;
using namespace boost::asio::ip;
int main() {
using namespace boost::asio;
using namespace boost::asio::ip;
try
{
boost::asio::io_service io; //构造IO服务,由于非异步,无需run
udp::socket socket(io, udp::endpoint(udp::v4(), 1024));//构造socket并绑定到1024端口
std::cout << "bind to port 1024, begin receive message...\n";
for (;;)
{
std::array<char, 1024> recv_buf;//接收缓冲
udp::endpoint remote_endpoint; //发送端信息
boost::system::error_code error;
//阻塞读取
auto size = socket.receive_from(boost::asio::buffer(recv_buf), remote_endpoint, 0, error);
if (error && error != boost::asio::error::message_size)
{
throw boost::system::system_error(error);
}
std::cout.write(recv_buf.data(), size);//输出结果
std::cout << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
至此笔记全部完成,总的来说,Boost.log这个库是真的强大,基本上所有你能想到的功能都可以轻松地实现,入门的话感觉并不是很复杂,但是想要玩儿地花的话,还得多看api了,最后十分感谢“编程小强”的讲解视频
此外,根据文章的测试可以看到,spdlog的效率好像更高一些,spdlog我也有尝试着用了一下,感觉也还是挺不错的,后面可能会再学习下这个东西。