使用C++实现日志(1)
一、使用 C++ 实现日志系统
在项目开发过程中,调试器(Debugger)是便捷的调试工具,但日志系统更是最廉价、最便捷的错误定位工具。当项目交付运行后,日志系统成为程序在运行环境中的 “健康监控仪”—— 当程序执行出错或业务逻辑出现问题时,我们将依赖日志快速定位问题根源。
1.1 开发日志系统的目的
开发日志系统主要为了实现以下目标:
- 追踪程序执行的过程;
- 追踪数据的变化;
- 快速定位问题的根源;
- 数据统计和性能分析;
- 为以后开发其他项目提供支持。
1.2 日志系统的功能需求
1. 日志消息级别
日志消息分为六个级别,按严重程度从高到低依次为:
FATAL(致命错误)
、ERROR(错误)
、WARN(警告)
、INFO(信息)
、DEBUG(调试)
、TRACE(跟踪)
。
2. 日志消息格式示例
2022/09/19 02:07:48 720332Z 234672 INFO Test_Logger.cpp func 20 - func info
2022/09/19 02:07:48 720344Z 234344 INFO Test_Logger.cpp func 23 - func info
格式字段说明:
日期 时间 微秒 线程ID 级别 文件名 函数名 行号 正文
3. 日志消息格式要求
- 每条日志占一行,便于使用
awk
、sed
、grep
等命令行工具联机分析; - 时间戳精确到微秒;
- 始终使用 GMT 时区(标记为
Z
),避免跨洲分布式系统的时区转换问题,便于追查事件顺序; - 打印线程 ID,用于分析多线程程序时序及检测死锁;
- 打印日志级别,在线查错时可优先关注
ERROR
级日志,加速定位问题; - 打印源文件名、函数名、行号,避免修复 bug 时搞错对象。
4. 记录日志的方式
支持两种输出方式:
- 终端显示
- 写入日志文件
5. 滚动日志(rolling log)
日志文件必须支持滚动功能,用于简化日志归档并防止单个文件过大占满硬盘空间。
- 滚动原理:通过设定规则(最大文件大小、最多保存天数),超过阈值后删除较早记录。
- 滚动条件:
- 文件大小(如每写满 1GB 切换到新文件);
- 时间(如每天零点新建日志文件,无论前一个文件是否写满)。
6. 日志级别设置
输出日志时,仅级别高于配置中规定级别的信息会被输出。无需修改代码即可灵活配置不同场景下的日志输出内容,便捷高效。
7. Log 文件名格式
文件名组成规则:
basename + now + hostname + pid + ".log"
basename
:基础名,由用户指定(通常为应用程序名);now
:当前时间,格式为"%Y%m%d-%H%M%S"
;hostname
:主机名;pid
:进程号(通过getpid
从 OS 获取);- 各部分用
.
连接。
示例(basename 为test_log_mt
):
test_log_mt.20220218-134000.ubuntu.12426.log
#include "Logger.hpp"tulun::LogFile* plog = nullptr;void writefile(const string& info)
{plog->append(info);
}void flushfile()
{plog->flush();
}int main()
{tulun::Logger::setOutput(writefile);tulun::Logger::setFlush(flushfile);plog = new tulun::LogFile("yhping", 1024 * 1024 * 1000);for (int i = 0; i < 1000000; ++i){LOG_INFO << "main" << i;}return 0;
}