实现一个 C++ 简单日志类,具备以下特性:
Logger.h 文件内容:
#ifndef LOGGER_H
#define LOGGER_H
// 引入 ATL 字符串处理头文件,提供 CString 类
#include
// 引入 Windows API 核心头文件
#include
// Logger 类:实现日志记录功能
class Logger
{
private:
// 单个日志文件最大
size_t MAX_FILE_SIZE;
// 日志文件保留天数
int LOG_RETENTION_DAYS ;
// 文件名前缀
CString prefix;
// 日志目录路径
CString logDirectory;
// 当前日志文件路径
CString currentLogFile;
// 日志文件指针
FILE* pLogFile;
// 临界区,保证多线程安全
CRITICAL_SECTION cs;
// 获取当前日期和时间字符串
CString getCurrentDateTime();
// 生成日志文件名
CString getLogFileName();
// 创建日志目录
void createLogDirectory();
// 打开新的日志文件
void openNewLogFile();
// 检查日志文件大小
void checkFileSize();
// 删除过期日志文件
void deleteOldLogs();
public:
// 构造函数,初始化对象 文件名前缀,单个文件大小(MB),文件保留天数
Logger(CString pre = "log", int mb = 20, int max_day = 365);
// 析构函数,释放资源
~Logger();
// 支持可变参数的日志记录函数
void log(const char* format, ...);
// 记录 CString 类型日志
void log(CString data);
};
#endif // LOGGER_H
Logger.cpp文件内容:
#include "stdafx.h"
#include "Logger.h"
#include
#include
#include
#include
#include
#include
// 链接 Kernel32 库
#pragma comment(lib, "Kernel32.lib")
// 获取当前日期和时间字符串
CString Logger::getCurrentDateTime()
{
SYSTEMTIME st;
GetLocalTime(&st);
CString timestamp;
// 格式化时间
timestamp.Format("%04d-%02d-%02d %02d:%02d:%02d.%03d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
return timestamp;
}
// 生成日志文件名
CString Logger::getLogFileName()
{
SYSTEMTIME st;
GetLocalTime(&st);
CString timestamp;
// 格式化时间
timestamp.Format("%04d-%02d-%02d-%02d-%02d-%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
CString fileName;
fileName.Format("%s%s.txt", prefix, timestamp);
return fileName;
}
// 创建日志目录
void Logger::createLogDirectory()
{
if(_mkdir(logDirectory) != 0 && errno != EEXIST)
{
std::cerr << "Failed to create log directory: " << logDirectory << std::endl;
}
}
// 打开新的日志文件
void Logger::openNewLogFile()
{
if(pLogFile)
{
fclose(pLogFile);
}
currentLogFile = logDirectory + "\\" + getLogFileName();
pLogFile = fopen(currentLogFile, "a");
if(!pLogFile)
{
std::cerr << "Failed to open log file: " << currentLogFile << std::endl;
}
}
// 检查日志文件大小
void Logger::checkFileSize()
{
if(pLogFile)
{
fseek(pLogFile, 0, SEEK_END);
long fileSize = ftell(pLogFile);
if(fileSize >= static_cast
{
openNewLogFile();
}
fseek(pLogFile, 0, SEEK_SET);
}
}
//删除超过天数的日志文件
void Logger::deleteOldLogs()
{
// WIN32_FIND_DATA 结构体用于存储文件信息
WIN32_FIND_DATA findData;
// 构造搜索模式字符串,查找日志目录下所有 文件
CString searchPattern = logDirectory + "\\*.*";
// 使用 FindFirstFile 查找第一个匹配的文件
HANDLE hFind = FindFirstFile(searchPattern, &findData);
// 检查是否成功找到文件
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
// 构造文件的完整路径
CString filePath = logDirectory + "\\" + findData.cFileName;
// FILETIME 结构体用于存储文件的创建时间
FILETIME ftCreation;
// 打开文件以获取其创建时间
HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 检查文件是否成功打开
if(hFile != INVALID_HANDLE_VALUE)
{
// 获取文件的创建时间
GetFileTime(hFile, &ftCreation, NULL, NULL);
// 关闭文件句柄
CloseHandle(hFile);
// SYSTEMTIME 结构体用于存储转换后的系统时间
SYSTEMTIME stCreation;
// 将 FILETIME 转换为 SYSTEMTIME
FileTimeToSystemTime(&ftCreation, &stCreation);
// 获取当前系统时间
SYSTEMTIME now;
GetLocalTime(&now);
// 定义 tm 结构体用于存储当前时间和文件创建时间
struct tm tmNow, tmCreation;
// 初始化当前时间结构体
tmNow.tm_sec = now.wSecond; // 秒
tmNow.tm_min = now.wMinute; // 分钟
tmNow.tm_hour = now.wHour; // 小时
tmNow.tm_mday = now.wDay; // 日
tmNow.tm_mon = now.wMonth - 1; // 月(tm_mon 的范围是 0-11)
tmNow.tm_year = now.wYear - 1900; // 年(tm_year 是从 1900 年开始的偏移量)
tmNow.tm_isdst = 0; // 不考虑夏令时
// 初始化文件创建时间结构体
tmCreation.tm_sec = stCreation.wSecond; // 秒
tmCreation.tm_min = stCreation.wMinute; // 分钟
tmCreation.tm_hour = stCreation.wHour; // 小时
tmCreation.tm_mday = stCreation.wDay; // 日
tmCreation.tm_mon = stCreation.wMonth - 1; // 月(tm_mon 的范围是 0-11)
tmCreation.tm_year = stCreation.wYear - 1900; // 年(tm_year 是从 1900 年开始的偏移量)
tmCreation.tm_isdst = 0; // 不考虑夏令时
// 将 tm 结构体转换为 time_t 类型的时间戳
time_t tNow = mktime(&tmNow); // 当前时间的时间戳
time_t tCreation = mktime(&tmCreation); // 文件创建时间的时间戳
// 计算时间差(以天为单位)
double diff = difftime(tNow, tCreation) / (60 * 60 * 24);
// 如果文件创建时间超过 LOG_RETENTION_DAYS 天,则删除文件
if(diff > LOG_RETENTION_DAYS)
{
DeleteFile(filePath);
}
}
}
while(FindNextFile(hFind, &findData) != 0); // 查找下一个匹配的文件
// 关闭查找句柄
FindClose(hFind);
}
}
// 构造函数,初始化日志系统
Logger::Logger(CString pre, int mb , int max_day )
{
prefix = pre;
MAX_FILE_SIZE = mb * 1024 * 1024;
LOG_RETENTION_DAYS = max_day;
InitializeCriticalSection(&cs);
char exePath[MAX_PATH];
GetModuleFileName(NULL, exePath, MAX_PATH);
CString exeDir = exePath;
int pos = exeDir.ReverseFind('\\');
if(pos != -1)
{
exeDir = exeDir.Left(pos);
}
logDirectory = exeDir + "\\log";
createLogDirectory();
openNewLogFile();
deleteOldLogs();
}
// 析构函数,释放资源
Logger::~Logger()
{
if(pLogFile)
{
fclose(pLogFile);
}
DeleteCriticalSection(&cs);
}
// 可变参数日志记录函数,用于将格式化的日志信息写入日志文件
// 参数 format: 格式化字符串,类似于 printf 函数的第一个参数,用于指定日志信息的格式
// ...: 可变参数,根据 format 字符串的要求传入相应的参数
void Logger::log(const char* format, ...)
{
// 进入临界区,确保多线程环境下对日志文件的操作是线程安全的
EnterCriticalSection(&cs);
// 检查当前日志文件的大小 如果文件大小超过预设的最大文件大小,会创建一个新的日志文件
checkFileSize();
// 检查日志文件指针是否有效
if (pLogFile)
{
// 定义一个 va_list 类型的变量 args,用于处理可变参数
va_list args;
// 初始化 va_list 变量 args,使其指向可变参数列表的起始位置
va_start(args, format);
// 使用 _vscprintf 函数计算格式化后的字符串所需的缓冲区大小
int len = _vscprintf(format, args);
// 检查计算得到的字符串长度是否大于 0
if (len > 0)
{
// 创建一个 std::vector
// 额外的 1 个字节用于存储字符串结束符 '\0'
std::vector
// 使用 vsprintf_s 函数将可变参数按照 format 字符串的格式写入缓冲区
// vsprintf_s 是安全版本的函数,会检查缓冲区大小,避免缓冲区溢出
// 如果写入成功,返回写入的字符数(不包括字符串结束符),该值应该大于等于 0
if (vsprintf_s(buffer.data(), buffer.size(), format, args) >= 0)
{
// 将缓冲区中的字符数组转换为 CString 类型的日志消息
CString logMessage(buffer.data());
// 获取当前的日期和时间
CString timestamp = getCurrentDateTime();
// 将时间戳、日志消息和换行符拼接成完整的日志记录
CString fullLog = timestamp + " " + logMessage + "\n";
// 使用 fprintf 函数将完整的日志记录写入日志文件
fprintf(pLogFile, "%s", fullLog);
// 调用 fflush 函数强制将缓冲区中的数据写入文件
fflush(pLogFile);
}
}
// 结束可变参数的处理,释放 va_list 变量占用的资源
va_end(args);
}
// 离开临界区,允许其他线程进入并操作日志文件
LeaveCriticalSection(&cs);
}
// 记录 CString 类型日志
void Logger::log(CString data)
{
EnterCriticalSection(&cs);
checkFileSize();
if(pLogFile)
{
CString timestamp = getCurrentDateTime();
CString fullLog = timestamp + " " + data + "\n";
fprintf(pLogFile, "%s", fullLog);
fflush(pLogFile);
}
LeaveCriticalSection(&cs);
}
用法举例:
Logger logger("log",20,365);
logger.log("ANSI文本转换为UTFB,并设置到控件出错");
logger.log("temp width :%d 中心点的温度:%.1f 延时:%d", tempInfo->width, g_dTemp[y1 * Width + x1], page->m_iTempDelay);