目录
为什么写
现在已经有很多的开源成熟日志库(spdlog,log4cpp,boost.log等),为什么我还要重复造轮子?
- 最主要的一点我是想充实一下我的业余时间,而且日志与业务和领域都没有关系,只要是写c++的都可以使用。
- 开源的库代码量大,功能也很强大,给我一种比较“重”的感觉,我想写一个简单一点的。
- 使用统一的格式和日志接口(spdlog提供了很多可以写日志的入口,而且可以自定义日志格式,对我这种选择困难症来说很难做出决定)
- 写代码是件好玩的事儿,我为了玩。
实现
特点
- 使用简单(可以看下面使用示例)
- 速度很快
- 输出
- 控制台输出(不同等级日志输出颜色不同,可以选择开关)
- 文件输出(日志文件按天和文件大小分割)
- 不同输出可以设置不同的等级
- 日志级别可在运行时修改
- 使用fmt库进行格式化
- 支持异步模式(可选择)
架构图
优化
- 格式化
- 格式化方式
格式化是一件比较费时的步骤,开始我采用的是sprintf进行格式化,后来改为了fmt库进行格式化,不过我在测试中发现速度并没有得到提升。
- 时间格式化
每次都对时间进行格式化会增加不少时间消耗,我把在同一秒内对日志时间格式给缓存下来,如果接下来需要格式化对日志时间还在该秒内,就省去了秒级对时间格式化操作。这确实提升了不少的性能。
- 格式化方式
- 输出
fflush频率控制,可以设置缓存刷新等级(比如当是warn等级日志或缓存满了才刷新到文件),来减少io操作。有明显的速度提升
这样可能会在程序突然崩溃时丢掉日志记录,不过丢掉的是等级低的日志,影响可能不大 - 异步
写入和格式化都比较费时,于是我把这两个步骤和日志调用分离,采用异步格式化写入,日志调用时把日志记录push到日志队列里面,写入线程不断的取日志队列记录进行格式化和写入。
测试的时候我发现这并不能提升速度,反而速度更慢了。而且当日志量很大的时候,日志队列会爆掉而丢弃数据(可以把队列设置的尽可能大,但这并不是一个解决办法)。
使用示例
#include <dflog/dflog.h>
void exampleLOG()
{
/* 日志, 使用类似于print输出 */
LOG(INFO, "* * * * * * * * * LOG example * * * * * * * * * *");
LOG(TRACE, "i am trace log ({}), ({:.2})", 123, 2.3);
LOG(DEBUG, "i am debug log ({}), ({})", 123, 3.3123);
LOG(INFO, "i am debug log ({:05}), ({:.3})", 123, 3.3123);
LOG(INFO, "i am info log ({})", static_cast<long int>(123));
LOG(WARN, "i am warn log [{:#5}]", 100);
LOG(ERROR, "i am error log {}", "hello wrold");
LOG(CRITICAL, "i am critical log ");
LOG(INFO, "* * * * * * * * * * * * * * * * * * * * * * * * *");
}
void exampleLOGF()
{
/* 日志, 使用类似于print输出 */
LOGF(INFO, "* * * * * * * * LOGF example * * * * * * * * * * *");
LOGF(TRACE, "i am trace log (%d), (%.2f)", 123, 2.3);
LOGF(DEBUG, "i am debug log (%d), (%f)", 123, 3.3123);
LOGF(INFO, "i am info log (%ld)", static_cast<long int>(123));
LOGF(WARN, "i am warn log %d", 100);
LOGF(ERROR, "i am error log %s", "hello wrold");
LOGF(CRITICAL, "i am critical log ");
LOGF(INFO, "* * * * * * * * * * * * * * * * * * * * * * * * *");
}
void init()
{
/*
* 初始化日志
* 1. 可以添加不同的定向log输出
* 2. 可以设置异步或同步模式
* */
dflog::InitLog("./example.log", dflog::loggerOption::FILELOG | dflog::loggerOption::CONSOLE,dflog::Method::SYNC);
dflog::SetLevel(TRACE); /* default DEBUG */
/* 不同的log输出, 可以控制不同的日志等级 */
// dflog::SetLevel(DEBUG, dflog::loggerOption::FILELOG | dflog::loggerOption::CONSOLE); /* 默认所有日志等级改动 */;
// dflog::SetFlushLevel(WARN) /* 默认每次刷新(TRACE) */;
}
int main(void)
{
init();
exampleLOG();
exampleLOGF();
return 0;
}
输出格式
[2020-08-04 20:00:48.530] [INFO] [12859] [example.cpp](17): i am info log
测试
测试用例写在代码仓库里面,可以进入代码仓库查看tests文件夹。
速度上的测试用例我只测试了一部分情况,只输出如下内容
[2020-08-04 20:00:48.530] [INFO] [12859] [example.cpp](17): hello wrold (1)
hello world 后面括号里为一个整型数值。
每秒写入200k多的日志记录,与spdlog输出相同内容的速度不相上下。
测试机配置
2 Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz
参考
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 245292011@qq.com