文章目录
最近在写项目,在处理到日志模块的过程中考虑到使用不方便,所以特此实现一个日志系统,并且里面要实现一个和printf效果类似的一个可变参数版的打印信息
在这篇博客中,我们将深入探讨一个用C++编写的自定义日志类的设计与实现细节。该日志类允许开发者根据不同的需求选择输出日志的方式(屏幕输出、单文件输出或按等级分类输出到不同文件),并提供了便捷的方法生成格式化的日志信息。
首先,我们引入了必要的头文件,包括iostream用于标准输入输出,time.h用于获取时间信息,以及一些系统调用相关头文件如stdarg.h、sys/types.h、sys/stat.h、fcntl.h、unistd.h和stdlib.h,这些在打开、关闭文件和文件操作中起到关键作用。
接下来定义了一些宏常量,如SIZE作为缓冲区大小,以及各种日志级别(Info、Debug、Warning、Error、Fatal)和输出方式(Screen、Onefile、Classfile)。同时预设了一个默认的日志文件名"log.txt"。
Log
类的核心功能如下:
构造函数初始化时,默认设置输出方式为屏幕打印,并设定日志存储路径为当前目录下的"log/"。
Enable()
方法用于启用指定的日志输出方式。levelToString()
是一个转换函数,将整型的日志级别转换为对应的字符串形式。printLog()
是核心的打印日志方法,根据设置的输出方式进行日志打印。如果选择的是Screen,则直接输出到控制台;如果是Onefile则将所有日志写入同一文件"log.txt";若选择Classfile,则按日志级别创建不同文件分别记录对应级别的日志。printOneFile()
和printClassFile()
负责具体文件的写入操作。其中,printOneFile()
直接写入单一文件,printClassFile()
则是先调用levelToString()
得到对应日志级别的字符串,然后附加到"log.txt"后面形成新的文件名进行写入。类中还定义了一个析构函数,确保资源正确释放。
最后,重载了运算符(),以实现简洁的日志记录语法。它接受日志级别、格式化字符串及可变参数列表,通过va_arg处理可变参数,并结合当前时间信息生成格式化的日志文本,最后调用
printLog()
进行输出。
实例化后的全局对象lg,使得开发人员可以方便地在程序任何地方使用lg(level, format, ...)
来记录日志,从而大大简化了日志系统的使用和管理。
// 头文件包含部分
#pragma once // 防止头文件被重复包含
#include <iostream> // 提供标准输入输出流操作
#include <time.h> // 提供与时间相关的函数,如获取当前时间
#include <stdarg.h> // 提供可变参数列表的支持
#include <sys/types.h> // 定义基本系统数据类型
#include <sys/stat.h> // 提供文件状态结构体和文件操作权限等信息
#include <fcntl.h> // 提供文件描述符标志和文件打开模式常量
#include <unistd.h> // 提供与文件I/O、进程控制等相关函数
#include <stdlib.h> // 提供通用的函数,如内存分配和错误处理
// 定义日志级别和输出方式常量
#define SIZE 1024 // 日志缓冲区大小
#define Info 0 // 信息级别日志
#define Debug 1 // 调试级别日志
#define Warning 2 // 警告级别日志
#define Error 3 // 错误级别日志
#define Fatal 4 // 致命级别日志
#define Screen 1 // 输出到屏幕
#define Onefile 2 // 所有日志记录在一个文件中
#define Classfile 3 // 按日志级别分类存放在不同文件中
#define LogFile "log.txt" // 默认日志文件名
// 定义日志类
class Log
{
public:
// 构造函数,初始化日志对象
Log()
{
// 设置默认的日志输出方式为屏幕打印
printMethod = Screen;
// 设置日志文件存放路径为当前目录下的"log/"子目录
path = "./log/";
}
// 启用指定的日志输出方式
void Enable(int method)
{
printMethod = method;
}
// 将日志级别转换为对应的字符串形式
std::string levelToString(int level)
{
// 使用switch-case语句根据不同日志级别返回相应字符串
switch (level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warning";
case Error:
return "Error";
case Fatal:
return "Fatal";
default:
return "None"; // 如果级别不合法,则返回"None"
}
}
// 核心日志打印方法,根据设置的输出方式输出日志
void printLog(int level, const std::string &logtxt)
{
switch (printMethod)
{
case Screen:
// 输出到标准输出流(通常指控制台)
std::cout << logtxt << std::endl;
break;
case Onefile:
// 将所有日志记录在同一文件中
printOneFile(LogFile, logtxt);
break;
case Classfile:
// 按日志级别分类输出到不同文件
printClassFile(level, logtxt);
break;
default:
// 若未指定有效输出方式,则不做任何操作
break;
}
}
// 将日志写入指定文件
void printOneFile(const std::string &logname, const std::string &logtxt)
{
// 构造完整的文件路径
std::string _logname = path + logname;
// 打开或追加写入文件,创建文件不存在时
int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
if (fd < 0)
{
// 文件打开失败,则返回
return;
}
// 将日志内容写入文件
write(fd, logtxt.c_str(), logtxt.size());
// 关闭已打开的文件描述符
close(fd);
}
// 按日志级别分类输出到不同文件
void printClassFile(int level, const std::string &logtxt)
{
// 构造带有日志级别后缀的文件名
std::string filename = LogFile;
filename += ".";
filename += levelToString(level);
// 调用printOneFile方法,将日志写入具有级别后缀的文件中
printOneFile(filename, logtxt);
}
// 析构函数,无特殊操作
~Log()
{
}
// 重载运算符(),用于简化日志记录过程
void operator()(int level, const char *format, ...)
{
// 获取当前时间
time_t t = time(nullptr);
struct tm *ctime = localtime(&t);
// 创建左侧时间与日志级别的标签
char leftbuffer[SIZE];
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]",
levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
// 处理可变参数列表
va_list s;
va_start(s, format);
// 创建右侧格式化日志内容
char rightbuffer[SIZE];
vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
// 结束可变参数列表的处理
va_end(s);
// 将左右两部分拼接成完整日志文本
char logtxt[SIZE * 2];
snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);
// 打印最终的格式化日志
printLog(level, logtxt);
}
private:
// 成员变量
int printMethod; // 当前使用的日志输出方式
std::string path; // 日志文件存放路径
// 私有方法:由于已经通过printLog公开调用,因此这里仅列出而不做注释
// void printOneFile(const std::string &, const std::string &);
// void printClassFile(int, const std::string &);
};
// 全局Log对象实例,便于在程序任何地方调用记录日志
Log lg;
总结来说,这个C++日志类提供了一种灵活且实用的方式来管理和输出日志信息,可以根据项目需求调整日志输出模式,并支持日志级别分类和时间戳标记,有助于提高软件的调试效率和运行状态监控能力。