同步异步日志系统
一、日志系统框架设计
1.1模块划分
1.1.1 日志等级模块
- OFF:关闭
- DEBUG:调试,调试时的关键信息输出。
- INFO:提示,普通的提⽰型⽇志信息。
- WARN:警告,不影响运⾏,但是需要注意⼀下的⽇志。
- ERROR:错误,程序运⾏出现错误的⽇志。
- FATAL:致命,⼀般是代码异常导致程序⽆法继续推进运⾏的⽇志。
1.1.2 日志消息模块
- 时间:描述本条⽇志的输出时间。
- 线程ID:描述本条⽇志是哪个线程输出的。
- ⽇志等级:描述本条⽇志的等级。
- ⽇志数据:本条⽇志的有效载荷数据。
- ⽇志⽂件名:描述本条⽇志在哪个源码⽂件中输出的。
- ⽇志⾏号:描述本条⽇志在源码⽂件的哪⼀⾏输出的。
1.1.3 日志消息格式化模块
系统默认的输出格式: [%d{%H:%M:%S}]%T[%t]%T[%p]%T[%c]%T%f:%l%T%m%n
[12:38:45] [12345678] [FATAL] [root] main.c:178 套接字创建失败…\n
- %d{%H:%M:%S}:表⽰⽇期时间,花括号中的内容表示日期时间的格式。
- %T:表⽰制表符缩进。
- %t:表⽰线程ID。%p:表⽰⽇志级别。
- %c:表⽰⽇志器名称,不同的开发组可以创建⾃⼰的⽇志器进⾏⽇志输出,⼩组之间互不影响。
- %f:表⽰⽇志输出时的源代码⽂件名。
- %l:表⽰⽇志输出时的源代码⾏号。
- %m:表⽰给与的⽇志有效载荷数据 。
- %n:表⽰换行。
- 设计思想:设计不同的⼦类,不同的⼦类从⽇志消息中取出不同的数据进⾏处理。
1.1.4 日志落地模块(日志落地的方向是工厂模式)
- 标准输出:表⽰将⽇志进⾏标准输出的打印。
- ⽇志⽂件输出:表⽰将⽇志写⼊指定的⽂件末尾。
- 滚动⽂件输出:当前以⽂件⼤⼩进⾏控制,当⼀个⽇志⽂件⼤⼩达到指定⼤⼩,则切换下⼀个⽂件进⾏输出 。
- 后期,也可以扩展远程⽇志输出,创建客⼾端,将⽇志消息发送给远程的⽇志分析服务器。
- 设计思想:设计不同的⼦类,不同的⼦类控制不同的⽇志落地⽅向。
1.1.5 日志器模块(日志器的生成是建造者模式)
- 同步日志器模块—完成日志的同步输出功能。
- 异步日志器模块—完成日志的异步输出功能(将日志消息发送到日志缓冲器内存)
1.1.6 异步线程模块(日志的输出是用宏完成的代理模式)
- 实现对⽇志的异步输出功能,⽤⼾只需要将输出⽇志任务放⼊任务池,异步线程负责⽇志的落地输出功能,提供了更加⾼效的⾮阻塞的⽇志输出。
1.1.7 单例的日志器管理模块(单例模式实现对日志器的管理)
- 为了降低项⽬开发的⽇志耦合,不同的项⽬组可以有⾃⼰的⽇志器来控制输出格式以及落地⽅向,因此本项⽬是⼀个多⽇志器的⽇志系统。
- 管理模块就是对创建的所有⽇志器进⾏统⼀管理。并提供⼀个默认⽇志器,提供标准输出的⽇志输出。
1.2 模块关系图
通过模块关系图,能够简单快速的了解模块之间的关系。
二、代码设计
2.1 实用类设计
- 首先需要把架子搭起来,功能先声明好。
// 通⽤功能类,与业务⽆关的功能实现
// 1. 获取系统时间
// 2. 判断文件是否存在
// 3. 获取⽂件所在⽬录的路径
// 4. 创建⽬录
//静态接口可以直接使用,不需要实例化对象
#include <iostream>
namespace logsLearn {
namespace util{
//日期类
class Data{
public:
//获取系统时间
static time_t now();
};
//文件类
class File{
public:
//判断文件是否存在
static bool exists(const std::string &pathname);
//获取⽂件所在⽬录的路径
static std::string path(const std::string &pathname);
//创建⽬录
static void createDirectory(const std::string &pathname);
};
}
}
- 其次在实现各个功能。
// 条件编译,防止头文件重复包含
#ifndef __M_UTIL_H__
#define __M_UTIL_H__
// 通⽤功能类,与业务⽆关的功能实现
// 1. 获取系统时间
// 2. 判断文件是否存在
// 3. 获取⽂件所在⽬录的路径
// 4. 创建⽬录
#include <iostream>
#include <ctime>
#include <sys/stat.h>
#include <sys/types.h>
namespace logsLearn
{
namespace util
{
// 日期类
class Data
{
public:
// 获取系统时间
static time_t now()
{
// 返回当前系统时间
return time(nullptr);
}
};
// 文件类
class File
{
public:
// 判断文件是否存在
static bool exists(const std::string &pathname)
{ // 定义了一个stat的结构体变量
struct stat st;
// 获取文件信息不成功,进入条件
if (stat(pathname.c_str(), &st) < 0)
{
return false;
}
return true;
}
// 获取⽂件所在⽬录的路径
static std::string path(const std::string &pathname)
{
if (pathname.empty()) return ".";
// pos是当前查找的最后“/\\”的位置
size_t pos = pathname.find_last_of("/\\");
// 没有找到"/\\"
if (pos == std::string::npos)
return ".";
// 找到了,返回路径,左闭右开原则,要想包括pos位置,需要加1
return pathname.substr(0, pos + 1);
}
// 创建⽬录
static void createDirectory(const std::string &pathname)
{ // pos找到的位置,idx是存的位置
size_t pos = 0, idx = 0;
while (idx < pathname.size())
{
// 遍历路径每找到最后一个"/\\"创建文件夹
size_t pos = pathname.find_first_of("/\\", idx);
if (pos == std::string::npos)
{ // 创建文件夹,pathname.c_str()表示路径名,0777表示权限
mkdir(pathname.c_str(), 0777);
}
// 前面的路径
std::string parent_dir = pathname.substr(0, pos + 1);
// 文件是否存在
if (exists(parent_dir) == true)
{
idx = pos + 1;
continue;
}
// 文件不存在,创建文件
mkdir(parent_dir.c_str(), 0777);
idx = pos + 1;
}
}
};
}
}
#endif
- 我们每次编写完一个模块后,要对其进行单元测试,确保程序的准确性。
//测试代码
#include "util.hpp"
int main()
{
//工具类测试
std::cout<<logsLearn::util::Data::now()<<std::endl;
std::string pathname="./abc/bcd/a.txt";
std::cout<<pathname;
logsLearn::util::File::createDirectory(logsLearn::util::File::path(pathname));
return 0;
}
- 界面展示
make运行之前
make运行之后
补充:
1.stat函数
stat函数是用于获取文件信息,比如文件权限,文件类型信息等等。
函数的声明:
int stat(const char *pathname, struct stat *buf);
参数说明:
pathname:表示文件路径名
buf:用于保存获取到的文件属性信息(这是一个传出参数)
返回值说明:成功返回0,失败返回-1并设置errno
2.mkdir函数
mkdir函数用于创建一个新的目录。如果指定的目录已经存在,并且没有设置相应的标志来允许覆盖或忽略已存在的目录,则函数会失败;它允许用户为新创建的目录设置权限,这些权限决定了谁可以访问该目录。
函数声明:
int mkdir(const char * pathname , mode_t mode);
参数说明:
pathname(const char * ):指向以null结尾的字符串的指针,该字符串指定了要创建的目录的路径。路径可以是相对路径或绝对路径。
mode(mode_t):指定新目录的权限。这些权限位使用八进制数表示(例如,0755),并且会受进程的文件模式创建掩码(umask)的影响。最终权限是请求权限与umask的补码的逻辑与结果。通常,mode参数包括文件所有者(user)、组(group)和其他人(other)的读(r)、写(w)和执行(x)权限的组合。
返回值说明:成功时,返回0;失败时,返回-1,并设置全局变量errno以指示错误类型(如EACCES表示权限不足,EEXIST表示目录已存在等)。