编译器:VisualStudio 2019
C++标准:C++17
.h文件
#pragma once
/*
#define _CRT_SECURE_NO_WARNINGS
添加这宏定义来禁用备份函数的警告,
例如:sprintf,mkdir,fopen。
如果不用这个则需要修改函数,
例如:sprintf_s,_mkdir,fopen_s。
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*MacOS Linux 系统用<sys/stat.h> / Windows系统用 <direct.h>*/
#include <direct.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include<iostream>
#include <vector>
#include <filesystem>
#include <algorithm>
using namespace std;
namespace fs = std::filesystem;
using namespace std::chrono;
class Logger
{
private:
int isSaveInFile;
int saveMonth;
char folderPath[64];
int FileIsExist(char* folderPath, char* filename);
int autoClearLogs;
public:
Logger();
Logger(int isSaveInFile, const char* folderPath, int saveMonth = 3, int autoClearLogs = 1);
~Logger();
void Init(int isSave, const char* folderPath, int saveMonth = 3, int autoClearLogs = 1);
void Error(const char* format, ...);
void Warn(const char* format, ...);
void Info(const char* format, ...);
void Debug(const char* format, ...);
void CleanOldLogs();
};
.cpp文件
#include "Logger.h"
/*
* 判断日志文件是否存在
* @param folderPath:文件夹,filename:文件名
* @retval 0:文件不存在并且创建失败,1:文件存在或者创建成功
*/
int Logger::FileIsExist(char* folderPath, char* filename)
{
int result = 0;
char filepath[100]; // 完整的文件路径
sprintf(filepath, "%s\\%s", folderPath, filename); // 拼接文件路径
if (_mkdir(folderPath) == -1) // 检查文件夹是否存在,如果不存在则创建
{
//printf("文件夹已存在或创建失败\n");
}
else
{
//printf("文件夹已创建\n");
}
FILE* file = fopen(filepath, "r");// 尝试打开文件
if (file == NULL)
{
file = fopen(filepath, "w"); // 如果文件不存在,则创建文件
if (file != NULL)
{
fclose(file); //printf("文件已创建\n");
if (this->autoClearLogs!=0)//如果启动了文件自动清理,则清理过期文件
{
CleanOldLogs();
}
result = 1;
}
else
{
//printf("文件创建失败\n");
result = 0;
}
}
else
{
//printf("文件已存在\n");
result = 1;
fclose(file);
}
return result;
}
Logger::Logger()
{
char path[64] = "Log";
this->Init(0, path, 0, 0);
}
Logger::Logger(int isSaveInFile, const char* folderPath, int saveMonth, int autoClearLogs)
{
this->Init(isSaveInFile, folderPath, saveMonth, autoClearLogs);
}
Logger::~Logger()
{
}
/*
* 初始化日志系统
* @param isSave:是否保存到文件,folderPath:文件夹,saveMonth:保存几个月(默认3个月),autoClearLogs:是否开启自动清理(默认开启)
* @retval 无
*/
void Logger::Init(int isSave, const char* folderPath, int saveMonth, int autoClearLogs)
{
this->isSaveInFile = isSave;
strcpy(this->folderPath, folderPath);
this->saveMonth = saveMonth;
this->autoClearLogs = autoClearLogs;
if (this->isSaveInFile)
{
struct tm timeinfo;
time_t rawtime;
time(&rawtime);
localtime_s(&timeinfo, &rawtime);
char filename[16];
sprintf(filename, "%d-%02d-%02d.log", timeinfo.tm_year + 1900,timeinfo.tm_mon + 1, timeinfo.tm_mday);
if (this->FileIsExist(this->folderPath,filename))
{
char filepath[128] = "";
sprintf(filepath, "%s/%s", this->folderPath, filename);
FILE* file = fopen(filepath, "a");
if (file != NULL) {
fclose(file);
printf("启用日志输出,日志位置: /%s\n", filepath);
}
else
{
printf("日志文件异常!\n");
}
}
else
{
printf("日志文件异常!\n已关闭日志输出,启用控制台输出。\n");
this->isSaveInFile = 0;
}
}
else
{
printf("启用控制台输出。\n");
}
}
/*
* 输出Error信息
*/
void Logger::Error(const char* format, ...)
{
va_list args;
struct tm timeinfo;
time_t rawtime;
time(&rawtime);
localtime_s(&timeinfo, &rawtime);
va_start(args, format);
if (this->isSaveInFile)
{
char filename[24];
sprintf(filename, "%d-%02d-%02d.log", timeinfo.tm_year + 1900,timeinfo.tm_mon + 1, timeinfo.tm_mday);
if (FileIsExist(this->folderPath, filename))
{
char filepath[128] = "";
sprintf(filepath, "%s/%s", this->folderPath, filename);
FILE* file = fopen(filepath, "a");
if (file)
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Error: ",timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vfprintf(file, headInfo, 0);
vfprintf(file, format, args);
vfprintf(file, "\n", 0);
fclose(file);
}
}
}
else
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Error: [",timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vprintf(headInfo, 0);
vprintf(format, args);
vprintf("]\n", 0);
}
va_end(args);
}
/*
* 输出Warn信息
*/
void Logger::Warn(const char* format, ...)
{
va_list args;
struct tm timeinfo;
time_t rawtime;
time(&rawtime);
localtime_s(&timeinfo, &rawtime);
va_start(args, format);
if (this->isSaveInFile)
{
char filename[24];
sprintf(filename, "%d-%02d-%02d.log", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
if (FileIsExist(this->folderPath, filename))
{
char filepath[128] = "";
sprintf(filepath, "%s/%s", this->folderPath, filename);
FILE* file = fopen(filepath, "a");
if (file)
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Warn: ", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vfprintf(file, headInfo, 0);
vfprintf(file, format, args);
vfprintf(file, "\n", 0);
fclose(file);
}
}
}
else
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Warn: [", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vprintf(headInfo, 0);
vprintf(format, args);
vprintf("]\n", 0);
}
va_end(args);
}
/*
* 输出Info信息
*/
void Logger::Info(const char* format, ...)
{
va_list args;
struct tm timeinfo;
time_t rawtime;
time(&rawtime);
localtime_s(&timeinfo, &rawtime);
va_start(args, format);
if (this->isSaveInFile)
{
char filename[24];
sprintf(filename, "%d-%02d-%02d.log", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
if (FileIsExist(this->folderPath, filename))
{
char filepath[128] = "";
sprintf(filepath, "%s/%s", this->folderPath, filename);
FILE* file = fopen(filepath, "a");
if (file)
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Info: ", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vfprintf(file, headInfo, 0);
vfprintf(file, format, args);
vfprintf(file, "\n", 0);
fclose(file);
}
}
}
else
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Info: [", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vprintf(headInfo, 0);
vprintf(format, args);
vprintf("]\n", 0);
}
va_end(args);
}
/*
* 输出Debug信息
*/
void Logger::Debug(const char* format, ...)
{
va_list args;
struct tm timeinfo;
time_t rawtime;
time(&rawtime);
localtime_s(&timeinfo, &rawtime);
va_start(args, format);
if (this->isSaveInFile)
{
char filename[24];
sprintf(filename, "%d-%02d-%02d.log", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
if (FileIsExist(this->folderPath, filename))
{
char filepath[128] = "";
sprintf(filepath, "%s/%s", this->folderPath, filename);
FILE* file = fopen(filepath, "a");
if (file)
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Debug: ", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vfprintf(file, headInfo, 0);
vfprintf(file, format, args);
vfprintf(file, "\n", 0);
fclose(file);
}
}
}
else
{
char headInfo[128];
sprintf(headInfo, "Aplication run at %02d:%02d:%02d Debug: [", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
vprintf(headInfo, 0);
vprintf(format, args);
vprintf("]\n", 0);
}
va_end(args);
}
/*
* 清理saveMonth个月前的log文件
* @param 无
* @retval 无
*/
void Logger::CleanOldLogs()
{
vector<string> log_files;
for (const auto& entry : std::filesystem::directory_iterator(this->folderPath))
{
if (entry.is_regular_file() && entry.path().extension() == ".log")
{
log_files.push_back(entry.path().string());
}
}
try
{
int num = (int)log_files.size();
int saveFileNum = this->saveMonth * 30;
if (num > saveFileNum)
{
sort(log_files.begin(), log_files.end());
for (int i = 0; i < num - saveFileNum; ++i)
{
filesystem::remove(log_files[i]);
}
this->Warn("日志文件已清理!");
}
}
catch (const exception& e)
{
this->Error("清理日志文件时出现异常: ");
this->Error(e.what());
this->Error("\r\n--------------------------\r\n");
}
}