提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录- 系列文章目录
- example运行:
- bench_mark运行
- 简单代码分析
- 普通用法
- 编译时日志等级
example运行:
示例中的程序,可以通过教程简单的编译出来
$ git clone https://github.com/gabime/spdlog.git $ cd spdlog && mkdir build && cd build $ cmake .. && make -j
执行结果如下:
输出好像并没有自带代码行号,不过应该可以自行配置吧
不知为何,bench目录中的 CMakelist.txt不能直接使用。通过先安装这个库,再使用g++编译运行
$ sudo make install $ g++ latency.cpp -lspdlog -lpthread -o latency
以下四个文件,有两个文件因为缺少头文件编译出错
- async_bench.cpp
- bench.cpp
- formatter-bench.cpp
- latency.cpp
?─? g++ formatter-bench.cpp -lspdlog -lpthread -o formatter-bench
formatter-bench.cpp:6:10: fatal error: benchmark/benchmark.h: No such file or directory
6 | #include "benchmark/benchmark.h"
| ^~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
?─ ~/learn/spdlog/bench v1.x ?3 ? gyl@mi 22:47:01
?─? g++ latency.cpp -lspdlog -lpthread -o latency
latency.cpp:10:10: fatal error: benchmark/benchmark.h: No such file or directory
10 | #include "benchmark/benchmark.h"
| ^~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
另外两个程序运行如下
-
bench
-
async_bench
该方法用于估计自己程序中的效率。以如下参数运行该程序,结果如下
#include "spdlog/spdlog.h"
int main()
{ // 直接输出字符串
spdlog::info("Welcome to spdlog!");
// 支持多种格式化模板
// 各种排版参数
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); // 格式化排版: 多种输出格式
spdlog::info("Support for floats {:03.2f}", 1.23456); // 浮点数
spdlog::info("Positional args are {1} {0}..", "too", "supported");// 定制顺序
spdlog::info("{:<30}", "left aligned");// 排版格式
//运行时log等级
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// 改变日志格式change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// 编译时设置日志级别
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
}
普通用法
不考虑日志等级,代码中展示了两种输出日志的方式:仅字符串输出和格式化参数输出,对应代码如下:
知识点:
- 除了普通的格式化字符串,还提供了宽字符的格式化字符串wformat_string。
- 使用可变参数:fmt是一个用于格式化的对象。该类还是一个模板,高级语法,需要仔细研究下实现
- 使用完美转发:将变参传递给logger
default_logger_raw()是实现在spdlog-inl.h。所以spdlog库的组织是 xxx.h是头文件,xxx.-inl.h是实现文件。
default_logger_raw()返回了一个裸指针。没有使用智能指针,恐怕是为了效率,而且仅供内部使用。
registry是实现在details命名空间的类,而且是一个单例,使用时直接调用静态函数instance()。
定义在spdlog/details/registry.h
实现在spdlog/details/registry-inl.h
插一句题外话,c++11 要求类内静态函数要线程安全,所以c++11以后实现单例模式,这就是标准做法。C++函数内的静态变量初始化以及线程安全问题
get_default_raw()返回一个registry对象内部保存的logger对象。
而且上面有描述:返回默认logger的裸指针,给默认的api使用,效率更高,但是不能和set_default_logger()一起使用。也就是说,不要在不同的线程分别调用set_defautl_logger()和std::info()等默认的日志输出api
初探几个类:
registry
logger是一个绝对的核心类,但是其非常大,随后再研究。从上面看registry应该是用于管理logger的一个类了。
class SPDLOG_API registry
{
public:
using log_levels = std::unordered_map;
registry(const registry &) = delete;
registry &operator=(const registry &) = delete; // 不可拷贝与赋值
void register_logger(std::shared_ptr new_logger); // 注册logger
void initialize_logger(std::shared_ptr new_logger); // 初始化logger
std::shared_ptr get(const std::string &logger_name); //根据名字获取一个logger
std::shared_ptr default_logger(); // 获取默认logger
// Return raw ptr to the default logger.
// To be used directly by the spdlog default api (e.g. spdlog::info)
// This make the default API faster, but cannot be used concurrently with set_default_logger().
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
logger *get_default_raw(); //获取默认logger的裸指针
// set default logger.
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
void set_default_logger(std::shared_ptr new_default_logger); // 设置默认logger
void set_tp(std::shared_ptr tp); // 设置 线程池
std::shared_ptr get_tp(); //获取线程池
// Set global formatter. Each sink in each logger will get a clone of this object
void set_formatter(std::unique_ptr formatter); //设置格式化输出
void enable_backtrace(size_t n_messages); // 开启trace记录
void disable_backtrace(); // 关闭trace记录
void set_level(level::level_enum log_level); // 设置日志等级
void flush_on(level::level_enum log_level); // 设置刷新日志等级
void flush_every(std::chrono::seconds interval); // 设置自动刷新周期
void set_error_handler(err_handler handler); // 设置错误处理函数
void apply_all(const std::function)> &fun); // ??
void flush_all(); //立即刷新所有日志
void drop(const std::string &logger_name); //删除某个日志
void drop_all(); // 删除所有日志
// clean all resources and threads started by the registry
void shutdown(); // 删除所有这个类的资源和线程
std::recursive_mutex &tp_mutex(); // 获取递归锁
void set_automatic_registration(bool automatic_registration); //??
// set levels for all existing/future loggers. global_level can be null if should not set.
void set_levels(log_levels levels, level::level_enum *global_level); // 为所有的 日志输出日志等级
static registry &instance(); // 单例
private:
registry();
~registry();
void throw_if_exists_(const std::string &logger_name);
void register_logger_(std::shared_ptr new_logger);
bool set_level_from_cfg_(logger *logger);
std::mutex logger_map_mutex_, flusher_mutex_; # 两把锁
std::recursive_mutex tp_mutex_; # 递归锁
std::unordered_map> loggers_; # 存储所有logger
log_levels log_levels_; // 日志等级
std::unique_ptr formatter_; // 格式化?
spdlog::level::level_enum global_log_level_ = level::info; // 默认全局日志等级info
level::level_enum flush_level_ = level::off; // 刷新等级 off
err_handler err_handler_; // 错误处理?
std::shared_ptr tp_; // 专门用来输出日志的线程池?
std::unique_ptr periodic_flusher_; // 定时刷新
std::shared_ptr default_logger_; // 默认的logger
bool automatic_registration_ = true; // 自动注册?
size_t backtrace_n_messages_ = 0; // backtrace的循环缓冲存储消息数量
};
编译时日志等级
由宏实现,从这里大致也可以猜到是用宏的大小比较,决定在编译时将宏映射为输出还是不输出。从这里也可以看到日志库的日志等级安排。
SPDLOG_ACTIVE_LEVEL
设置日志等级:#define define SPDLOG_ACTIVE_LEVEL
其内部实现,还是用了 default_logger_raw()
根据实现,所有>=该设置的日志等级,都会被输出。
日志等级由低到高分别是:
// SPDLOG_LEVEL_TRACE, // SPDLOG_LEVEL_DEBUG, // SPDLOG_LEVEL_INFO, // SPDLOG_LEVEL_WARN, // SPDLOG_LEVEL_ERROR, // SPDLOG_LEVEL_CRITICAL, 上面6个应该是大多数日志库用的日志等级排序,程序员最好记住。起码要知道,当发现写的日志打印不出来时,怎么调高一个等级输出。 CRITICAL是最高的日志等级,INFO比DEBUG的日志等级要高。 // SPDLOG_LEVEL_OFF 是为了关闭所有日志而存在的,
以trace来看宏的嵌套调用
- 两种宏,一种可以自定义logger,一种使用默认logger。
- SPDLOG_LOGGER_CALL调用了logger的日志输出。
- source_loc用于输出代码的文件名、行号、函数名等信息。
- SPDLOG_FUNCTION 将__FUNCTION__强制转换为 const char* ,不知道为什么



