Boost.Log模块学习

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. 设置输出槽

基本步骤

  1. 创建输出槽
  2. 在日志核心中注册已经创建的输出槽

设置输出槽的注意事项

  1. 一般在程序的启动阶段设置输出槽
  2. 快速日志输出使用库内建的输出槽作为默认输出
  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;
} 

示例输出

输出到log文件

可以看到,虽然有输出日志信息,但是格式跟默认的格式不符,没有了时间戳、线程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;
}

示例输出

输出到log文件

这里就不展示自动归档了,很简单,用一个循环或者跳时间试一下就好了。

如果说想要再添加一个文件用来保存日志的话,就使用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;
}

有几个点还是需要注意下的

  1. 可以对不同的日志文件使用不同的输出格式
  2. 可以对不同的日志文件使用不同的过滤器
  3. 可以通过打标签的方式控制不同的日志流向

过滤的存在使得日志可以有更多灵活的处理方式

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我也有尝试着用了一下,感觉也还是挺不错的,后面可能会再学习下这个东西。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值