4.8 KiB
4.8 KiB
日志优化
新版日志是以原来的 spdlog-1.6.1 为基础修改而来, 主要变动有:
- 新增审计日志级别 rtt.LogLevel.Audit
- 新增关闭日志级别 rtt.LogTrace.Off
- 新增日志输出格式
set_backend_logpattern
- 修改
set_backend_logfile
函数, 支持通过task_id
映射日志句柄 - 优化流式打印
- 新增fmt打印
1 日志级别
日志级别共有七类
[07-19 18:36:56 leaker@ubuntu ~/Desktop/tmp/Rosetta0712]$ more cc/python_export/_rosetta.cc
py::enum_<LogLevel>(m, "LogLevel")
.value("Trace", LogLevel::Trace)
.value("Debug", LogLevel::Debug)
.value("Audit", LogLevel::Audit)
.value("Info", LogLevel::Info)
.value("Warn", LogLevel::Warn)
.value("Error", LogLevel::Error)
.value("Fatal", LogLevel::Fatal)
.value("Off", LogLevel::Off)
其中, LogLevel::Audit 为新增日志级别. 当日志级别设为 LogLevel::Off 时, 则关闭所有日志(包括控制台日志). 调用格式为:
#!/usr/bin/env python3
import latticex.rosetta as rtt
import tensorflow as tf
...
rtt.set_backend_loglevel(rtt.LogLevel.Info) # info level
2 日志输出样式
日志输出样式由 set_backend_logpattern
(python调用层)和 set_pattern
(日志底层)函数控制. 其格式定义为: SPDLOG_ROSETTA_LOGGER_PATTERN
. 需要主要的是, Release 版本和 Debug 版本的缺省输出样式有所不同:
- Debug:
%Y-%m-%d %H:%M:%S.%e|%^%l%$|%s:%#|%!|%v
- Release:
%Y-%m-%d %H:%M:%S.%e|%^%l%$|%v
即Debug版本的日志会带有 file:line ( %s:%# ) 和函数名 ( %! )
3 日志文件
日志文件名设置函数为: set_backend_logfile
(python调用层)和 set_filename
(日志底层)函数控制. 相对旧版, 新版新增了一个参数 task_id
. 用于查找日志文件句柄.
函数定义为:
void Logger::set_filename(const std::string& filename, const std::string& task_id="");
函数实现逻辑为:
- 查找日志文件 filename 是否存在, 存在则函数返回. 反之, 转下一步
- 判断
task_id
是否为空字符串. 如果非空, 则创建一个异步非阻塞的常规日志文件(auto logger = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>(task_id.c_str(), filename.c_str())
), 并把filename
和task_id
做关联后, 成功返回. 反之, 转下一步. - 判断缺省的日志句柄
SPDLOG_ROSETTA_LOGGER_NAME
是否已经存在。如果存在, 则创建一个异步非阻塞的常规日志文件, 然后把文件名作为日志文件的句柄. 反之, 转下一步. - 创建一个异步非阻塞的常规日志文件, 然后把缺省的日志文件句柄和该日志文件做关联
4 流式日志
流式日志是通过宏 SPDLOG_LOGGER_STREAM
实现:
#define SPDLOG_LOGGER_STREAM(log, lvl) LogStream(log, lvl, spdlog::source_loc{__FILE__, __LINE__, __FUNCTION__}) == LogLine()
其底层实现为 LogLine 类. 主要是把以前旧版的 继承std::ostringstream 更改为 组合 . 具体实现为:
class LogLine
{
public:
LogLine() {
}
template<typename T> LogLine& operator<<(const T& t) {
ss_ << t;
return *this;
}
std::string str() const {
return ss_.str();
}
private:
std::ostringstream ss_;
};
5 fmt日志
fmt日志是通过宏 SPDLOG_LOGGER_CALL_FUNCTION
实现:
#define SPDLOG_LOGGER_CALL_FUNCTION(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, __FUNCTION__}, level, ##__VA_ARGS__)
每种日志级别的宏定义(以AUDIT为例):
#define TAUDIT_(task_id, ...) \
do {\
if (Logger::Get().get_log_to_stdout()) { \
SPDLOG_LOGGER_CALL_FUNCTION(spdlog::default_logger(), spdlog::level::audit, ##__VA_ARGS__); \
} \
std::shared_ptr<spdlog::logger> logger = Logger::Get().get_logger(task_id); \
if (logger != nullptr && logger != spdlog::default_logger()) {\
SPDLOG_LOGGER_CALL_FUNCTION(Logger::Get().get_logger(task_id), spdlog::level::audit, ##__VA_ARGS__); \
} \
} while(0)
#define AUDIT(...) TAUDIT_(context_->TASK_ID, ##__VA_ARGS__)
6 注意事项
- 流式日志(
log_/tlog_
系列)不支持std::endl强刷, 因为日志带有换行功能 - 如果有隐式的
TASK_ID
, 请使用log_
系列, 不要使用tlog_
系列(其实, 绝大部分日志打印都是使用log_
系列, 除非是单模板函数没有TASK_ID
的日志打印的话, 才需要使用tlog_
系列) - 基于点 2 , 流式日志请直接使用
log_
系列. 因为如果是错误使用, 编译会直接报错 - 如果是 Debug 模式的日志输出, 请不要再次使用
__FUNCTION__
,__func__
,__PRETTY_FUNCTION__
,__LINE__
,__FILENAME__
等和宏定义, 因为Debug模式下, 日志已经带了文件名:行号和函数名的相关信息