常用簡寫:
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;
要點:
- 結構圖要牢記在心;
trivial
頭文件可用於一般的控制台輸出,日志等級被定義在改頭文件;
- 全局日志等級過濾使用
logging::core::get()->set_filter();
- 如果不僅僅需要簡單的控制台輸出,這時候就要添加
sink
,使用logging::add_file_log
可添加文件sink后端,格式如下:
1
2
3
4
5
6
7
|
logging::add_file_log
(
keywords::file_name="sample_%N.log", //文件名格式
keywords::rotation_size=10*1024*1024, //超過此大小自動建立新文件
keywords::time_based_rotation=sinks::file::rotation_at_time_point(0,0,0), //每隔指定時間重建新文件
keywords::format="[%TimeStamp%]:%Message%" //日志消息格式
); |
除了這種語法外,也可以建立sinks::text_file_backend
后端,用之初始化一個sink。 file rotation的選項有很多,可以指定日期、時間間隔、文件大小,甚至可以指定自己的謂詞。 此外,可以設置在建立新文件后首先執行(sink->locked_backend()->set_open_handler(xx)
),以及rotation之前最后執行的語句。 可以綜合管理這些日志文件,限制輸出文件夾,日志總大小限制,以及最低磁盤空間限制。如下:
1
2
3
4
5
|
sink->locked_backend()->set_file_collector(sinks::file::make_collector(
keywords::target="logs", //目標文件夾
keywords::max_size=16*1024*1024, //所有日志加起來的最大大小,
keywords::min_free_space=100*1024*1024 //最低磁盤空間限制
)); |
如果多個后端制定了相同的存放文件夾,限制取其中最嚴格的,另外,注意避免不同后端日志的命名沖突問題。 注意使用sink->locked_backend()->scan_for_files()
來掃描其他實例建立的日志文件。
以上都是使用程序本身使用單日志文件的情況,如果需要根據請求的不同將消息分發到不同的文件,可以使用sinks::text_mutlifile_backend
。該后端常用於多線程調試。
-
除了文件后端,更常用的是文本流后端sinks::text_ostream_backend
,與別的后端不同,文本流后端可以添加多個輸出對象,這些對象由於都在同一個sink
中,所以輸出格式是一樣的,這種做法的性能比添加文件后端更高,但是會失去對文件的控制能力。
-
對於大規模應用程序,為了方便查看記錄,各模塊的日志應該相互獨立,因此一個logger一般是不夠的,我們需要自己建立logger。logger的建立方法很簡單,new一個src::logger就可以了…
如果真的只需要一個logger,除了使用前面提到的trivial中的宏以外,也可以用BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger,src::logger_mt)
,自己定義屬於自己的全局logger。然后在需要的時候使用src::logger_mt& lg=my_logger::get()
獲得logger的單例引用(這里最好是線程安全的logger,顯然)。
logger的使用方法:
1
2
3
4
5
6
7
8
|
logging::record rec=log.open_record();
if(rec)
{
logging::record_ostream strm(rec);
strm<<"Hello,World!";
strm.flush();
lg.push_record(boost::move(rec));
} |
以上可以簡化成一個宏:BOOST_LOG(lg)<<"Hello World"
attribute
是log record的附加信息,不同於一般的消息記錄,屬性可以被單獨拿出來處理,作為某種過濾條件,或者其他使用。屬性分為全局屬性,特定線程屬性和特定源的屬性。
常用的屬性,如時間戳、計數器,boost.log
都已經有實現好的版本,直接使用logging::add_common_attributes()
可以一次性獲得LineID
, TimeStamp
, ProcessID
, ThreadID
這些常用屬性(單線程程序沒有線程ID)。
可以自己注冊安全等級,定義相關枚舉,然后使用其作為src::serverity_logger<>
的模板參數初始化,就得到了自定義Severity
屬性的logger。這種logger可以使用BOOST_LOG_SEV(logger,serverity)
來記錄。
特定范圍的屬性可以用來做一些特殊日志,比如需要評估性能的地方可以使用BOOST_LOG_SCOPED_THREAD_ATTR("Timeline",attrs::timer());
注冊一個時間線標簽。
此外,可以給屬性定義占位符,相當於注冊為關鍵字,方便在流中使用。關於屬性的函數大多定義在expr
命名空間里面。
- 格式化輸出消息,前文有
set_formatter
的用法。格式化消息可以使用stl格式expr::stream<<xxx
,也可以使用boost::format
格式,即expr::format("%1%)%xxx
這種。
為了取得最大靈活性,可以自定義一個formatter
。
-
過濾子,如前文,可以使用sink.set_filter
來設置,可以使用的過濾條件包括布爾表達式和lambda表達式等,如果想要確定有沒有某個屬性,可使用expr::has_attr()
。
-
寬字符。使用windows的話,這塊算是最惡心的部分了
輔助函數
boost.log
定義了大量輔助函數實現常用功能。
logging::add_console_log()
可以直接添加控制台sink,返回boost::shared_ptr<sinks::synchronous_sink<sinks::text_ostream_backend>>
,可利用返回值設置過濾器或其他屬性。
logging::add_file_log()
,前文已經提過,可以直接得到文件sink…
最后logging::add_common_attributes
可以獲得常用屬性,前文也有詳解。
示例
完全封裝boost.log
的工程量比較大,不過對於小規模工程,使用一個初始化函數就足夠了,但是這會導致整個項目與boost
深度耦合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
/*
* FILE: boost.log.init.hpp
* INSTRUCTION: 這個文件用於初始化boost.log
* DETAIL: 這里重新定義了日志級別和對應的文本內容.
* 文件大小限制等信息被硬編碼在cpp文件中,如有需求可以修改。
*/
#ifndef BOOST_LOG_INIT_TAIRAN_HPP
#define BOOST_LOG_INIT_TAIRAN_HPP
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/keyword.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/attributes/timer.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_file_backend.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/attributes/named_scope.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;
enum SeverityLevel
{
Log_Info,
Log_Notice,
Log_Debug,
Log_Warning,
Log_Error,
Log_Fatal
};
// The formatting logic for the severity level
template< typename CharT, typename TraitsT >
inline std::basic_ostream< CharT, TraitsT >& operator<< (
std::basic_ostream< CharT, TraitsT >& strm, SeverityLevel lvl)
{
static const char* const str[] =
{
"Info",
"Notice",
"Debug",
"Warning",
"Error",
"Fatal"
};
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(log_severity, "Severity", SeverityLevel)
BOOST_LOG_ATTRIBUTE_KEYWORD(log_timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(log_uptime, "Uptime", attrs::timer::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(log_scope, "Scope", attrs::named_scope::value_type)
void g_InitLog();
#endif |
實現:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#include "boost.log.init.hpp"
void g_InitLog()
{
logging::formatter formatter=
expr::stream
<<"["<<expr::format_date_time(log_timestamp,"%H:%M:%S")
<<"]"<<expr::if_(expr::has_attr(log_uptime))
[
expr::stream<<" ["<<format_date_time(log_uptime,"%O:%M:%S")<<"]"
]
<<expr::if_(expr::has_attr(log_scope))
[
expr::stream<<"["<<expr::format_named_scope(log_scope,keywords::format = "%n")<<"]"
]
<<"<"<<log_severity<<">"<<expr::message;
logging::add_common_attributes();
auto console_sink=logging::add_console_log();
auto file_sink=logging::add_file_log
(
keywords::file_name="%Y-%m-%d_%N.log", //文件名
keywords::rotation_size=10*1024*1024, //單個文件限制大小
keywords::time_based_rotation=sinks::file::rotation_at_time_point(0,0,0) //每天重建
);
file_sink->locked_backend()->set_file_collector(sinks::file::make_collector(
keywords::target="logs", //文件夾名
keywords::max_size=50*1024*1024, //文件夾所占最大空間
keywords::min_free_space=100*1024*1024 //磁盤最小預留空間
));
file_sink->set_filter(log_severity>=Log_Warning); //日志級別過濾
file_sink->locked_backend()->scan_for_files();
console_sink->set_formatter(formatter);
file_sink->set_formatter(formatter);
file_sink->locked_backend()->auto_flush(true);
logging::core::get()->add_global_attribute("Scope",attrs::named_scope());
logging::core::get()->add_sink(console_sink);
logging::core::get()->add_sink(file_sink);
} |
使用:
調用g_initlog()
初始化一次以后,在任意位置聲明logger,使用宏BOOST_LOG_SEV(logger,SeverityLevel)<<"..."
來寫入日志即可。