spdlog日志格式化 标志全指南
一、spdlog格式化核心机制
SPDLOG通过set_pattern()
函数实现灵活的日志格式定制,该函数解析用户提供的格式字符串,生成包含时间、源代码、进程等信息的结构化日志。其底层由pattern_formatter
类处理,通过识别%
+标志符的组合动态生成格式化器对象。
二、格式化标志全集(按功能分类)
-
时间与日期
标志 描述 示例输出 %c
标准日期时间 Tue Apr 23 00:00:17 2024
%x
短日期 (MM/DD/YY) 04/23/24
%H
24小时制小时 14
%M
分钟 05
%S
秒 30
%e
毫秒 981
%F
纳秒 123456789
%z
时区 +0800
-
进程与线程
标志 描述 示例输出 %P
进程ID 22588
%t
线程ID 22600
-
源代码定位
标志 描述 示例输出 %g
完整源文件路径 /home/user/src/main.cpp
%s
短文件名 main.cpp
%#
行号 415
%!
函数名 UploadStatusData(...)
-
日志级别与消息
标志 描述 示例输出 %l
日志级别 [debug]
%v
原始消息内容 data_period=20240423
-
高级功能
标志 描述 %^
颜色范围开始 (需终端支持) %$
颜色范围结束 %u
距离上条日志的纳秒间隔 %i
微秒间隔
三、实战格式示例
// 包含进程、线程、时间、源码、消息的完整格式
my_logger->set_pattern("%^[%c|%x|ms:%e][%l] ""[P:%P|t:%t][Code:%g:%#|%!] %v%$"
);
输出效果:
[Tue Apr 23 00:00:17 2024|04/23/24|ms:981][debug]
[P:22588|t:22600][Code:/src/main.cpp:415|UploadStatusData] device_type=YWIND00
四、工作机制详解
-
格式解析流程:
•set_pattern()
将字符串拆解为%
+标志符的标记。• 每个标记触发
pattern_formatter::handle_flag_()
生成对应的格式化器(如%g
生成源文件格式化器)。• 格式化器存入队列,日志输出时按序拼接信息。
-
时间处理优化:
•need_localtime_
标志控制是否使用本地时间(默认启用),多时区场景需显式设置时区格式%z
。
五、高级定制技巧
• 自定义格式器:继承custom_flag_formatter
实现format()
接口,通过add_flag()
注册到日志器。
• 动态字段截断:使用%10!
格式将函数名截断至10字符,避免过长的日志行。
六、注意事项
• 性能影响:启用源码定位(%#
, %!
)会轻微降低性能,建议在调试时使用。
• 线程安全:daily_logger_mt
为多线程安全,单线程环境使用_st
后缀版本。
通过合理组合格式化标志,开发者可生成适应复杂调试、生产监控等多场景的高效日志系统,实现堪比断点调试的代码追踪能力。
附注源码:
这段代码是SPDLOG日志库的源代码的一部分,具体是格式化日志消息的pattern_formatter
类的成员函数handle_flag_
。
具体的源码如下:路径为:usr->local->include->spdlog->pattern_formatter-inl.h->{}spdlog->handle_flag_(char,detailspadding info)
template<typename Padder>
SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding)
{// process custom flagsauto it = custom_handlers_.find(flag);if (it != custom_handlers_.end()){auto custom_handler = it->second->clone();custom_handler->set_padding_info(padding);formatters_.push_back(std::move(custom_handler));return;}// process built-in flagsswitch (flag){case ('+'): // default formatterformatters_.push_back(details::make_unique<details::full_formatter>(padding));need_localtime_ = true;break;case 'n': // logger nameformatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding));break;case 'l': // levelformatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding));break;case 'L': // short levelformatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding));break;case ('t'): // thread idformatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding));break;case ('v'): // the message textformatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding));break;case ('a'): // weekdayformatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));need_localtime_ = true;break;case ('A'): // short weekdayformatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));need_localtime_ = true;break;case ('b'):case ('h'): // monthformatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));need_localtime_ = true;break;case ('B'): // short monthformatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));need_localtime_ = true;break;case ('c'): // datetimeformatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));need_localtime_ = true;break;case ('C'): // year 2 digitsformatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));need_localtime_ = true;break;case ('Y'): // year 4 digitsformatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));need_localtime_ = true;break;case ('D'):case ('x'): // datetime MM/DD/YYformatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));need_localtime_ = true;break;case ('m'): // month 1-12formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));need_localtime_ = true;break;case ('d'): // day of month 1-31formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));need_localtime_ = true;break;case ('H'): // hours 24formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));need_localtime_ = true;break;case ('I'): // hours 12formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));need_localtime_ = true;break;case ('M'): // minutesformatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));need_localtime_ = true;break;case ('S'): // secondsformatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));need_localtime_ = true;break;case ('e'): // millisecondsformatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));break;case ('f'): // microsecondsformatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));break;case ('F'): // nanosecondsformatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));break;case ('E'): // seconds since epochformatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));break;case ('p'): // am/pmformatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));need_localtime_ = true;break;case ('r'): // 12 hour clock 02:55:02 pmformatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));need_localtime_ = true;break;case ('R'): // 24-hour HH:MM timeformatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));need_localtime_ = true;break;case ('T'):case ('X'): // ISO 8601 time format (HH:MM:SS)formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));need_localtime_ = true;break;case ('z'): // timezoneformatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));need_localtime_ = true;break;case ('P'): // pidformatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));break;case ('^'): // color range startformatters_.push_back(details::make_unique<details::color_start_formatter>(padding));break;case ('$'): // color range endformatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));break;case ('@'): // source location (filename:filenumber)formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding));break;case ('s'): // short source filename - without directory nameformatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding));break;case ('g'): // full source filenameformatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding));break;case ('#'): // source line numberformatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding));break;case ('!'): // source funcnameformatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));break;case ('%'): // % charformatters_.push_back(details::make_unique<details::ch_formatter>('%'));break;case ('u'): // elapsed time since last log message in nanosformatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding));break;case ('i'): // elapsed time since last log message in microsformatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding));break;case ('o'): // elapsed time since last log message in millisformatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding));break;case ('O'): // elapsed time since last log message in secondsformatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));break;default: // Unknown flag appears as isauto unknown_flag = details::make_unique<details::aggregate_formatter>();if (!padding.truncate_){unknown_flag->add_ch('%');unknown_flag->add_ch(flag);formatters_.push_back((std::move(unknown_flag)));}// fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag)// spdlog::set_pattern("[%10!] %v") => "[ main] some message"// spdlog::set_pattern("[%3!!] %v") => "[mai] some message"else{padding.truncate_ = false;formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));unknown_flag->add_ch(flag);formatters_.push_back((std::move(unknown_flag)));}break;}
}
这段代码是SPDLOG日志库的源代码的一部分,具体是格式化日志消息的pattern_formatter
类的成员函数handle_flag_
。
这个函数负责处理日志格式字符串中的各种标志。根据不同的标志,它会创建不同的格式化对象,并将这些对象添加到formatters_
向量中。这些格式化对象会在日志消息被记录时用于格式化消息。
例如,如果标志是'%'
,则函数会创建一个percent_formatter
对象;如果标志是'n'
,则函数会创建一个logger_name_formatter
对象;如果标志是'd'
,则函数会创建一个date_formatter
对象等等。这些格式化对象都是formatter
类的派生类,它们实现了format
函数,该函数用于将日志消息的特定部分格式化为字符串。
此外,函数还处理一些其他标志,例如'+'
,它会设置默认的格式化器;'-'
,它会取消设置默认的格式化器;'#'
,它会在日志消息中包含源代码文件的行号;'&'
,它会在日志消息中包含当前运行的线程ID;'@'
,它会在日志消息中包含源代码文件的名称。
总之,这个函数负责根据日志格式字符串中的标志创建适当的格式化对象,并将这些对象添加到formatters_
向量中,以便在记录日志消息时使用。