spdlog简介
(1)按照官方介绍,是一个高性能的C++日志组件,支持跨平台,兼容 C++11;一款开源的、快速的日志库; (2)spdlog是个只有头文件的库,只需要将头文件拷贝到你的工程就可以使用了,编译器需要支持C++11 它使用一个类似python的格式API库fmt:
spdlog优点
- 配置特别简单,仅包含头文件即可(我暂时没测试成功 在我测试时需要引入静态库)
- 写日志方式简单明了;
- 可实现自动按日期创建日志文件/定时创建日志文件;
- 可自定义日志格式;
- 可以输出当前输出日志所在的文件及函数;
- 可自定义文档大小;
- 可将不同级别的信息输出到不同日志文件;
- 多平台等。
一般日志功能设计
一般在实际工作中,日志最好要有以下3个功能: ①:自动按日期创建日志文件; ②:自动定期清理过期日志(spdlog好像没有这个功能…); ③:实时刷新日志。 具体分析: ①:一天只创建一个日志文件(或者将各级别的日志单独创建为一个文件)是为了更加清晰简洁,若程序在每次开启时都创建一个日志文件,可能就会显得很混乱。 ②:一般时间较久的日志不再具有参考价值,如果真是现场出现问题,用户也不会等很久才反馈。所以一般保存7天内的日志就足够了。 ③:当我们需要查看日志时,但又不能关闭现场程序,那么就需要能实时刷新日志信息。不然我们可能会遇到日志文件打不开或者打开以后日志信息未更新的问题。
spdlog安装
[1] 安装配置测试链接 [2] Vcpkg全自动方式 [3] C++参考手册 [4] 总体概括
spdlog琐碎知识点总结
(1)线程安全:spdlog:: 命名空间下的是线程安全的;类似于:spdlog::set_error_handler(log_err_handler); // or logger->set_error_handler(log_err_handler); (2)对于sinks,以 _mt 后缀结尾的是线程安全的,比如:daily_file_sink_mt ,以_st 后缀结尾的是非线程安全的,比如:daily_file_sink_st (3)有关异步设置logger – 多线程 – (4)中相关细节也可查看 (4)使用spdlog::get("...") 访问loggers缺点:loggers可以在任何地方使用线程安全的spdlog::get("logger_name") 来进行访问,返回智能指针;注意:spdlog::get 可能会拖慢你的程序,因为它内部维护了一把锁,所以要谨慎使用。比较推荐的用法是保存返回的shared_ptr<spdlog::logger> ,直接使用它,至少在频繁访问的代码中。详情参考(3)链接
spdlog程序测试
(一)日志输出控制台
链接中包含cmake文件配置
(1)数据全部输出到控制台
spdlog灵魂所在:自动识别类型,避免%d,%s类型错误,输出不了内容或者崩溃
LogDebug(“cmd_id={},bodyLen={}”, 1, 2); LogInfo(“user_id={},app_id={},domainId={},ip={},port={}”, 222, 222, 222, “127.0.0.1”, 8888);
#include <iostream>
#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main01(int argc, char* argv[])
{
auto console = spdlog::stdout_color_mt("ybhy");
console->info("信息");
console->warn("警告");
console->error("错误");
console->critical("危险");
spdlog::info("信息");
spdlog::warn("警告");
spdlog::error("错误");
spdlog::critical("危险");
console->info("Welcome to spdlog!");
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
console->warn("Easy padding in numbers like {:08d}", 12);
console->error("Some error message with arg{}..", 1);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::get("ybhy")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v");
spdlog::drop_all();
return 0;
}
运行结果如下
(2)指定某个等级以上的数据到控制台
spdlog::set_level(spdlog::level::warn);
enum level_enum //等级表 { trace = SPDLOG_LEVEL_TRACE, // 最低 debug = SPDLOG_LEVEL_DEBUG, info = SPDLOG_LEVEL_INFO, warn = SPDLOG_LEVEL_WARN, err = SPDLOG_LEVEL_ERROR, critical = SPDLOG_LEVEL_CRITICAL, // 最高 off = SPDLOG_LEVEL_OFF, n_levels };
#include <iostream>
#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main01(int argc, char* argv[])
{
auto console = spdlog::stdout_color_mt("ybhy");
spdlog::set_level(spdlog::level::warn);
console->info("信息");
console->warn("警告");
console->error("错误");
console->critical("危险");
spdlog::info("信息");
spdlog::warn("警告");
spdlog::error("错误");
spdlog::critical("危险");
console->info("Welcome to spdlog!");
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
console->warn("Easy padding in numbers like {:08d}", 12);
console->error("Some error message with arg{}..", 1);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::get("ybhy")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v");
return 0;
}
运行结果如下
(二)输出格式的自定义方式
熟悉函数:spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v");
- 模式标记
flag | meaning | example |
---|
%v | The actual text to log(要记录的实际文本) | “some user text” | %t | Thread id | “1232” | %P | Process id | “3456” | %n | Logger’s name | “some logger name” | %l | The log level of the message | “debug”, “info”, etc | %L | The log level of the message | “D”, “I”, etc | %a | Abbreviated weekday name(工作日的缩写) | “Thu” | %@ | Source file and line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) | my_file.cpp:123 | %s | Source file (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) | my_file.cpp | %# | Source line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) | 123 | %! | Source function (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc. see tweakme for pretty-print) | my_func |
- 对齐
- 相关程序测试
细节把握 (1)spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v"); 可以放在最后(显示的时候会根据不同的level显示不同的颜色),可以放在最前(无颜色) (2)在使用<%@> 、<%s> 、<%#> 、<%!> 不是直接写上就可以,需要调用相对应的函数宏才可实现;目前调研中掌握了两种方法:①通过调用宏函数 ②自己编写一个函数宏来调用本身程序; (3)具体实现:①通过调用宏函数:引入#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE ,在程序中调用对应宏,比如:SPDLOG_LOGGER_INFO(spdlog::get("ybhy"),"faf"); ②自己编写一个函数宏来调用本身程序:如下代码所示:
#include <iostream>
#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)
int main(int argc, char* argv[])
{
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
logger->flush_on(spdlog::level::warn);
spdlog::flush_every(std::chrono::seconds(10));
auto console = spdlog::stdout_color_mt("ybhy");
spdlog::set_default_logger(console);
spdlog::set_level(spdlog::level::debug);
LOG("test info");
ERROR("test error");
WARN("dadasda");
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n] - <%@>,%v");
spdlog::drop_all();
return 0;
}
运行结果如下
答疑:为什么文件中写进的是“daily_logger”。而控制台是“ybhy”,是因为两者的logger对象不一致’ 细节:在写入文件时注意:spdlog::set_level(spdlog::level::err); ,如果你设计成“err” ,那么文件中也只会保存等级为“error” 以上的数据
(三)数据输出到文件
(1)三种创建文件的方式
- basic_logger_mt
a:说明:日志文件一直会被写入,不断变大 b:案例:auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");
- rotating_logger_mt
a:说明:滚动日志,当日志文件超出规定大小时,会删除当前日志文件中所有内容,重新开始写入 b:函数原型:rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) c:参数介绍:参数max_files 规定了滚动文件的个数。当logger_name存满时,将其名称更改为logger_name.1,再新建一个logger_name文件来存储新的日志。再次存满时,把logger_name.1改名为logger_name.2,logger_name改名为logger_name.1,新建一个logger_name来存放新的日志。max_files 数量为几,就可以有几个logger_name文件用来滚动。 d:案例:auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 256, 2);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
e:测试结果 每个文件内容如下,后缀数字越大,日志内容越早: - daily_logger_mt
a:说明:每天会新建一个日志文件,新建日志文件的时间可自己设定 b:案例:
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
c:测试结果:
|