NKDBsec/third_party/spdlog/readme.md

138 lines
4.8 KiB
Markdown
Raw Permalink Normal View History

2024-01-20 14:14:46 +08:00
# 日志优化
新版日志是以原来的 __spdlog-1.6.1__ 为基础修改而来, 主要变动有:
1. 新增审计日志级别 __rtt.LogLevel.Audit__
2. 新增关闭日志级别 __rtt.LogTrace.Off__
3. 新增日志输出格式 __`set_backend_logpattern`__
4. 修改 __`set_backend_logfile`__ 函数, 支持通过 __`task_id`__ 映射日志句柄
5. 优化流式打印
6. 新增fmt打印
## 1 日志级别
日志级别共有七类
```python
[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__ 时, 则关闭所有日志(包括控制台日志). 调用格式为:
```python
#!/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__ 版本的缺省输出样式有所不同:
1. Debug: __`%Y-%m-%d %H:%M:%S.%e|%^%l%$|%s:%#|%!|%v`__
2. Release: __`%Y-%m-%d %H:%M:%S.%e|%^%l%$|%v`__
即Debug版本的日志会带有 __file:line__ ( __%s:%#__ ) 和函数名 ( __%!__ )
## 3 日志文件
日志文件名设置函数为: __`set_backend_logfile`__ (python调用层)和 __`set_filename`__ (日志底层)函数控制. 相对旧版, 新版新增了一个参数 __`task_id`__. 用于查找日志文件句柄.
函数定义为:
```cpp
void Logger::set_filename(const std::string& filename, const std::string& task_id="");
```
函数实现逻辑为:
1. 查找日志文件 __filename__ 是否存在, 存在则函数返回. 反之, 转下一步
2. 判断 __`task_id`__ 是否为空字符串. 如果非空, 则创建一个异步非阻塞的常规日志文件( __`auto logger = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>(task_id.c_str(), filename.c_str())`__ ), 并把 __`filename`__ 和 __`task_id`__ 做关联后, 成功返回. 反之, 转下一步.
3. 判断缺省的日志句柄 __`SPDLOG_ROSETTA_LOGGER_NAME `__ 是否已经存在。如果存在, 则创建一个异步非阻塞的常规日志文件, 然后把文件名作为日志文件的句柄. 反之, 转下一步.
4. 创建一个异步非阻塞的常规日志文件, 然后把缺省的日志文件句柄和该日志文件做关联
## 4 流式日志
流式日志是通过宏 __`SPDLOG_LOGGER_STREAM`__ 实现:
```c
#define SPDLOG_LOGGER_STREAM(log, lvl) LogStream(log, lvl, spdlog::source_loc{__FILE__, __LINE__, __FUNCTION__}) == LogLine()
```
其底层实现为 __LogLine__ 类. 主要是把以前旧版的 __继承std::ostringstream__ 更改为 __组合__ . 具体实现为:
```cpp
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`__ 实现:
```cpp
#define SPDLOG_LOGGER_CALL_FUNCTION(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, __FUNCTION__}, level, ##__VA_ARGS__)
```
每种日志级别的宏定义(以AUDIT为例):
```c++
#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 注意事项
1. 流式日志( __`log_/tlog_`__ 系列)不支持std::endl强刷, 因为日志带有换行功能
2. 如果有隐式的 __`TASK_ID`__ , 请使用 __`log_`__ 系列, 不要使用 __`tlog_`__ 系列(其实, 绝大部分日志打印都是使用 __`log_`__ 系列, 除非是单模板函数没有 __`TASK_ID`__ 的日志打印的话, 才需要使用 __`tlog_`__ 系列)
3. 基于点 __2__ , 流式日志请直接使用 __`log_`__ 系列. 因为如果是错误使用, 编译会直接报错
4. 如果是 __Debug__ 模式的日志输出, 请不要再次使用 __`__FUNCTION__`__ , __`__func__`__, __`__PRETTY_FUNCTION__`__, __`__LINE__`__, __`__FILENAME__`__ 等和宏定义, 因为Debug模式下, 日志已经带了文件名:行号和函数名的相关信息