C++ 日志系统实战第四步:设计与代码实现详解
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~
本文将加入项目代码编写!
目录
日志系统框架设计
模块划分
模块关系图
代码设计
实用类设计
日志等级设计
日志消息类
日志输出格式
日志落地(LogSink)类设计(简单工厂模式)
日志系统框架设计
本项目实现的是一个多日志器日志系统,主要实现的功能是让程序员能够轻松的将程序运行日志信息落地到指定的位置,且支持同步与异步两种方式的日志落地方式。
项目的框架设计将项目分为以下几个模块来实现。
模块划分
- 日志等级模块:对输出日志的等级进行划分,以便于控制日志的输出,并提供等级枚举转字符串功能。
- OFF:关闭
- DEBUG:调试,调试时的关键信息输出。
- INFO:提示,普通的提示型日志信息。
- WARN:警告,不影响运行,但是需要注意一下的日志。
- ERROR:错误,程序运行出现错误的日志
- FATAL:致命,一般是代码异常导致程序无法继续推进运行的日志
- 日志消息模块:中间存储日志输出所需的各项要素信息
- 时间:描述本条日志的输出时间。
- 线程 ID:描述本条日志是哪个线程输出的。
- 日志等级:描述本条日志的等级。
- 日志数据:本条日志的有效载荷数据。
- 日志文件名:描述本条日志在哪个源码文件中输出的。
- 日志行号:描述本条日志在源码文件的哪一行输出的。
- 日志消息格式化模块:设置日志输出格式,并提供对日志消息进行格式化功能。
- 系统的默认日志输出格式:% d {% H:% M:% S}% T% t% T% p% T% c% T% f:% l% T% m% n
- 示例:-> 13:26:32 [2343223321] [FATAL] [root] main.c:76 套接字创建失败 \n
- % d {% H:% M:% S}:表示日期时间,花括号中的内容表示日期时间的格式。
- % T:表示制表符缩进。
- % t:表示线程 ID
- % p:表示日志级别
- % c:表示日志器名称,不同的开发组可以创建自己的日志器进行日志输出,小组之间互不影响。
- % f:表示日志输出时的源代码文件名。
- % l:表示日志输出时的源代码行号。
- % m:表示给与的日志有效载荷数据
- % n:表示换行
- 设计思想:设计不同的子类,不同的子类从日志消息中取出不同的数据进行处理。
- 日志消息落地模块:决定了日志的落地方向,可以是标准输出,也可以是日志文件,也可以滚动文件输出……
- 标准输出:表示将日志进行标准输出的打印。
- 日志文件输出:表示将日志写入指定的文件末尾。
- 滚动文件输出:当前以文件大小进行控制,当一个日志文件大小达到指定大小,则切换下一个文件进行输出
- 后期,也可以扩展远程日志输出,创建客户端,将日志消息发送给远程的日志分析服务器。
- 设计思想:设计不同的子类,不同的子类控制不同的日志落地方向。
- 日志器模块:
- 此模块是对以上几个模块的整合模块,用户通过日志器进行日志的输出,有效降低用户的使用难度。
- 包含有:日志消息落地模块对象,日志消息格式化模块对象,日志输出等级
- 日志器管理模块:
- 为了降低项目开发的日志耦合,不同的项目组可以有自己的日志器来控制输出格式以及落地方向,因此本项目是一个多日志器的日志系统。
- 管理模块就是对创建的所有日志器进行统一管理。并提供一个默认日志器提供标准输出的日志输出。
- 异步线程模块:
- 实现对日志的异步输出功能,用户只需要将输出日志任务放入任务池,异步线程负责日志的落地输出功能,以此提供更加高效的非阻塞日志输出。
模块关系图
代码设计
实用类设计
/* 实用工具类的实现 1.获取系统时间 2.判断文件是否存在 3.获取文件所在路径 4.创建目录 */ #include #include namespace mylog { namespace util { // 获取系统时间 class Date { public: static size_t getTime()//返回当前时间戳 { return (size_t)time(nullptr); } }; // 判断文件是否存在 class File { public: //静态接口,直接使用,不用实例化对象 static bool exists(const std::string& pathname); static std::string path(const std::string& filename); static bool createDir(const std::string& dirname); }; } }
判断文件是否存在函数(exists):
access 函数是 C 语言中的一个文件访问权限检测函数,定义在 头文件中 。
- 函数原型:int access(const char *pathname, int mode);
- 参数
- pathname:要检测的文件或目录的路径名。
- mode:指定访问模式,常用取值有:
- R_OK:检查是否有读权限。
- W_OK:检查是否有写权限。
- X_OK:检查是否有执行权限。
- F_OK:检查文件是否存在。
- 返回值:调用成功时返回 0 ;调用失败时返回 -1 ,同时会设置 errno 来指示错误原因,比如 ENOENT 表示文件不存在等。
这是Linux下的系统调用,为了项目在各个操作系统都支持。我们可以使用stat函数
stat函数是 C 语言中用于获取文件状态信息的函数 ,定义在、和头文件中。
函数原型
int stat(const char *pathname, struct stat *buf);
参数
- pathname:指向要获取状态信息的文件路径名。
- buf:是一个struct stat结构体指针,用于存储获取到的文件状态信息。struct stat包含文件的 inode 编号、文件类型、权限、大小、修改时间等众多属性。
返回值
成功调用时返回 0 ;失败时返回 -1 ,并设置errno来指示错误类型,如ENOENT表示文件不存在。
#include static bool exists(const std::string& pathname) { // return access(pathname.c_str(), F_OK) != -1; Linux下可用 struct stat buffer; if (stat(pathname.c_str(), &buffer) == 0) { return true; } else { return false; } }
获取文件的所在路径函数(path):
就是查找最后一个‘/’
static std::string path(const std::string& filename) { size_t pos = filename.find_last_of("/\\"); if (pos == std::string::npos) { return "."; // 当前目录下 } else { return filename.substr(0, pos); } }
创建目录(creatdir):
对于.abc/b/a.text 要找到前面的才可以创建后面的
mkdir 函数用于创建一个新的目录,在不同操作系统的 C 语言标准库中均有提供。
函数原型
在 POSIX 系统(如 Linux、macOS)中,其原型定义在 和 头文件中:
int mkdir(const char *pathname, mode_t mode);
参数说明
- pathname:要创建的目录的路径名,可以是绝对路径或相对路径。
- mode:指定新目录的访问权限,是一个八进制数,如 0777 表示所有用户都有读、写、执行权限。
返回值
- 成功时返回 0。
- 失败时返回 -1,并设置 errno 以指示错误原因,常见的错误包括:
- EEXIST:指定路径的目录已存在。
- EACCES:没有足够权限创建目录。
- ENOENT:路径中的父目录不存在。
static bool createDir(const std::string &dirname) { if (dirname.empty()) { return false; // 空字符串无法创建目录 } int pos = 0, index = 0; while (index
- 实现对日志的异步输出功能,用户只需要将输出日志任务放入任务池,异步线程负责日志的落地输出功能,以此提供更加高效的非阻塞日志输出。
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。