日志
日志与策略模式
一、什么是设计模式
软件开发里,很多场景都是重复遇到的经典问题。前辈工程师针对这些高频场景,总结出一套通用、成熟、可复用的解决方案 ,这套方案就叫做设计模式。
简单理解:写代码的 "模板套路",目的是让代码更好扩展、更容易维护、可读性更强,团队协作时大家也能看懂彼此的代码。
二、日志认识
日志是用来记录程序、系统运行过程中发生事件的文件。 核心用途:
- 监控程序运行状态
- 保存报错、异常信息
- 线上故障排查、定位 BUG
- 运维安全审计
日志是排查线上问题最重要的工具,程序出问题无法调试时,只能依靠日志回溯现场。
规范日志必备字段(标准日志组成)
- ✅ 时间戳:事件发生的精确时间,用来定位先后顺序
- 日志等级(DEBUG/INFO/WARN/ERROR/FATAL)
- 进程 / 线程 ID
- 报错文件、行号
- 具体描述信息(业务内容、异常堆栈)
了解日志相关的函数
cpp
[可读性很好的时间] [日志等级] [进程 pid] [打印对应日志的文件名][行号] - 消息内容,支持可变参数
2 [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
3 [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [17] - hello world
4 [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [18] - hello world
5 [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [20] - hello world
6 [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [21] - hello world
7 [2024-08-04 12:27:03] [WARNING] [202938] [main.cc] [23] - hello world
先思考,这个日志模块需要哪些东西?
先梳理依赖元素:
- 日志有不同等级:DEBUG / WARNING / ERROR / INFO → 用
enum枚举 - 核心打印函数:接收【等级、文件名字符串、行号、格式化字符串、可变参数】→ 函数声明
- 多线程安全:打印的时候不能多个线程穿插输出 → 需要互斥锁
- 对外简易接口宏:自动填充
__FILE____LINE__,使用者不用手动传文件、行号 - 需要用到的头文件:
<cstdarg>可变参数、<ctime>时间、<pthread.h>互斥锁、<string>字符串处理
代码
gettimeofday()

作用:获取系统当前时间,得到秒 + 微秒高精度时间
参数:
tv:传出参数,传入struct timeval指针,函数把当前时间填进tv->tv_sec(秒)、tv->tv_usec(微秒)tz:struct timezone*,时区参数已经废弃,直接传 nullptr / NULL
返回值:
0:成功-1:失败,设置 errno
localtime_r

_r 系列函数:把输出缓冲区由库内部全局,改成调用者自己提供内存。
参数:
timep:输入,time_t 指针(Unix 纪元秒)result:调用者自己定义的 struct tm 变量地址 ,函数把解析出来的年月日时分秒,填到你给的这块内存。 返回值:成功返回result指针;失败返回NULL。




我们先进行策略的制定

由于有多种策略,我们这里实现向显示器和文件写入,
这里就可以使用基类和子类的方法进行

向显示器打印

向文件写入
我们需要文件名,文件路径

设计一个类调用

我们想要外部包含头文件就可以使用

测试


设计日志生成
这里我们采用内部类的实现
stringstream 详解
头文件:#include <sstream>
stringstream 是 C++ 字符串流,把字符串当成内存里的流设备 ,和 cin/cout 用法几乎一样。
cout:把数据输出到屏幕cin:从键盘读取数据stringstream:把数据输出 / 读取到内存字符串,不操作文件、不操作终端。
分为三类:
ostringstream:只输出(把变量转字符串)istringstream:只输入(把字符串解析成变量)stringstream:既可以读,也可以写。
1. 核心用途
- 任意类型转字符串(int、double、bool 转 std::string)
- 字符串切割解析,按空格分割字符串
- 拼接多类型字符串 ,不用反复
+=处理数字转字符串 - 把任意类型日志内容转成字符串,拼到日志缓冲区。
构造函数

我们还需要往里面写入内容,也就是说我们需要重载<<

功能作用
- 模板
template<typename T>:支持任意类型(字符串、int、double 等)日志内容输入,不用写多个重载版本。 - 参数接收待写入日志的数据
info。 - 局部
stringstream ss:把任意类型 T 的数据,统一转为字符串。 _loginfo += ss.str():把转换后的字符串,追加到类私有成员_loginfo(日志消息缓冲区),累积整条日志内容。return *this返回对象引用 :支持链式调用,实现obj << "a" << 123 << 3.14连续拼接日志。