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