程序员都知道程序的运行日志在不少时候都非常有用,利于排查、理清逻辑。
一般而言,日志都按天生成,并且具备自动清理多少天以前的旧日志,避免无限增长占用磁盘。
下图展示了2种日志模式。
模式一
1)在某固定根目录(C:/log/)下,每天生成1个日志文件(单个日志文件达到上限大小(比如50M)后,还能自动切换至新文件)。
比较适用于项目中单进程需要日志输出的情形。
模式二
2)在某固定根目录(C:/log/)下,每天生成1个子目录(2022-07-01),在该子目录下,有1个或多个应用对应的日志文件,比如app.log otherapp.log等等。 比较适用于项目中有多进程需要日志输出的情形。
模式一代码:
SysLog.h
#ifndef _CLASS_SYS_LOG_H_
#define _CLASS_SYS_LOG_H_
#include <stdio.h>
#include <string>
#include "common_platform.h"
struct tagDate
{
int year;
int month;
int day;
};
//enum ELOG_LEVEL
//{
// LOG_NRM = 1,//正常
// LOG_WRN = 2,//警告
// LOG_ERR = 3,//错误
//};
class CSysLog
{
public:
/*若是windows系统,参照CSysLog aLogger("D:\\", "MyApp",true),将会在D:\MyAppLog目录下产生日志文件;
* 若是Linux/Unix,系统参照CSysLog aLogger("/home/sunyy/", "MyApp",true),将会在/home/sunyy/MyAppLog目录下产生日志文件;
* Note:注意Windowsx下路径间隔是'\\',lINUX/UNIX下路径间隔是'/';
* 参数isUseWriteMutexMode为写日志时是否开启互斥锁保护,用于多线程,默认为true。
*/
CSysLog(std::string rootPath,std::string appName, bool isUseWriteMutexMode=true);
public:
~CSysLog(void);
int WriteLogFile(const char *fmt, ...);
int WriteLogFile(const std::string& content);
private:
void SetWriteUseMutexMode(bool flag);
private:
char log_dir_path[256];
FILE * fp; //文件描述符
int day;
int changefile; //是否切换文件,为1时切换
char m_szFileFlag[20]; //日志文件名的一个固定标识
int sameFileFlag; //一天可能会有多个日志文件(量大时切分为多个文件存储),对应的日志文件名中会有0,1,2...的标识
std::string m_rootPath;
int nameDelSize;//日志文件名长度。用于删除N天前的文件,可跟随名字格式来变动
bool nameDelSizeFlag;//初始为true,使用一次之后变为false
bool m_isUseMutexMode;//是否启用互斥写日志
#ifdef OS_WINDOWS
CRITICAL_SECTION m_mutexLock;
#else
pthread_mutex_t m_mutexLock;
#endif
private:
void ClearLog(uint32_t nDays); // 删除N天前的日志;
public://现在其余地方也想使用该以下成员函数,将其改为公共、静态。
//以下是日期比较,根据是文件名上的日期和当前系统日期
static int IsLeapYear(int year);//闰年
static int GetLastDay(tagDate date);
static int IsDateValid(tagDate date);
static void AddDay(tagDate *date);
static int Compare(tagDate date1, tagDate date2);
static long DateDiff(tagDate date1, tagDate date2);//返回日期比较的结果
};
#endif //_CLASS_LOG_MY
SysLog.cpp
#include "SysLog.h"
#define _ONE_LOG_FILE_MAX_SIZE (52428800)//10485760 10M, 52428800 50*1024*1024 50M
#define _DELETE_PRE_DATE_LOG (7) //N天前的记录
#define MaxJsonBuffer 1024
//不同系统的路径分割符
#ifdef OS_WINDOWS
#define PATH_SEPARATOR "\\"
#else
#define PATH_SEPARATOR "/"
#endif
CSysLog::CSysLog(std::string rootPath, std::string appName, bool isUseWriteMutexMode)
{
m_rootPath = rootPath;
memset(m_szFileFlag, 0, sizeof(m_szFileFlag));
if (appName.empty())
{
return;
}
memcpy(m_szFileFlag, appName.c_str(), appName.length());
fp = NULL;
day = -1;
changefile = 0;
sameFileFlag = 0;
nameDelSize = 0;
nameDelSizeFlag = true;
std::string strPath = rootPath + appName + std::string("Log") + PATH_SEPARATOR;
memset(log_dir_path,0,sizeof(log_dir_path));
strcpy(log_dir_path, strPath.c_str());//c_str必带 /0 strcpy_s为win独有的函数
// 创建目录这里也得注意区分系统
#ifdef OS_WINDOWS
if (_access(log_dir_path, 0) == -1)
_mkdir(log_dir_path);//如果不存在,就创建子目录
#else
if (access(log_dir_path, 0) == -1)
mkdir(log_dir_path, 0xFFFFFFFF/*S_IROTH|S_IWOTH|S_IXOTH*/);//如果不存在,就创建子目录
#endif
//ClearLog(31);//删除15天前的日志(包括当天),移到write进行检测,自动检测名字长度,并删除
//初始化互斥变量
#ifdef OS_WINDOWS //Windows系统
InitializeCriticalSection(&m_mutexLock);
#else //类Unix系统
int res = pthread_mutex_init(&m_mutexLock,NULL);
if(res != 0)
{
exit(-1);
}
#endif
//写日志的互斥模式
SetWriteUseMutexMode(isUseWriteMutexMode);
}
CSysLog::~CSysLog(void)
{
if (fp != NULL)
{
fclose(fp);
fp = NULL;
}
#ifdef OS_WINDOWS
DeleteCriticalSection(&m_mutexLock);
#else
pthread_mutex_destroy(&m_mutexLock);
#endif
}
int CSysLog::WriteLogFile(const std::string& content)
{
const char* fmt = content.data();
return WriteLogFile(fmt);
}
int CSysLog::WriteLogFile(const char *fmt, ...)
{
if (strlen(fmt) > MaxJsonBuffer)
{
return 0;
}
unsigned long res = 0;
//原本为char* pLoginfo = new char[MaxJsonBuffer];改为局部变量,省的频繁申请与释放
char pLoginfo[MaxJsonBuffer];
char name[4096] = {0};
va_list ap;
memset(pLoginfo,0x00,MaxJsonBuffer);//清空
int year = 0, month = 0,cur_day = 0;
#ifdef OS_WINDOWS //Windows系统
SYSTEMTIME sys;
GetLocalTime( &sys );
//日志时间
sprintf(pLoginfo,"[%4d-%02d-%02d %02d:%02d:%02d.%03d] ",sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond,sys.wMilliseconds);
year = sys.wYear;
month = sys.wMonth;
cur_day = sys.wDay;
#else //类Unix系统
struct timeval tv;
struct timezone tz;
struct tm *t;
gettimeofday(&tv, &tz);
t = localtime(&tv.tv_sec);
//日志时间
sprintf(pLoginfo,"[%4d-%02d-%02d %02d:%02d:%02d.%03d] ",
1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, (int)t->tm_sec, (int)(tv.tv_usec/1000));
year = 1900 + t->tm_year;
month = 1 + t->tm_mon;
cur_day = t->tm_mday;
#endif
va_start(ap,fmt);
vsprintf(pLoginfo+strlen(pLoginfo),fmt,ap);
va_end(ap);
//打开文件开始写
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS
EnterCriticalSection(&m_mutexLock);
#else
pthread_mutex_lock(&m_mutexLock);
#endif
}
do
{
//取得文件名
memset(name, 0x00, sizeof(name));
sprintf(name, "%s%04d-%02d-%02d_%s_%02d.log", log_dir_path, year, month, cur_day, m_szFileFlag, sameFileFlag);
//记录自定义名字长度
if (true== nameDelSizeFlag)
{
std::string nameDelTemp = name;
std::size_t ndelPos = nameDelTemp.find_last_of(PATH_SEPARATOR);
nameDelSize = nameDelTemp.length() - (ndelPos + 1);
ClearLog(_DELETE_PRE_DATE_LOG);//删除15天前的日志(包括当天) 首次开启时
nameDelSizeFlag = false;
}
if (day != cur_day)//非今天时,切换
{
//日期切换时将name重新赋值,因为sameFileFlag也要更新,不能是昨天的,要从0开始计算
sameFileFlag = 0;//置0
memset(name, 0x00, sizeof(name));
sprintf(name, "%s%04d-%02d-%02d_%s_%02d.log", log_dir_path, year, month, cur_day, m_szFileFlag, sameFileFlag);
//日期更换时,需要清除数据
ClearLog(_DELETE_PRE_DATE_LOG);
day = cur_day;
changefile = 1;
if (fp != NULL)//如果fp存在时,关闭并清空
{
fclose(fp);
fp = NULL;
}
}
//将空与变更目录,放在同一段里面
if (fp == NULL || changefile == 1)
{
fp = fopen(name, "a+");//打开或新建一个文本文件,可以读,但只允许在文件末尾追写
if (fp == NULL)
{
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS
LeaveCriticalSection(&m_mutexLock);
#else
pthread_mutex_unlock(&m_mutexLock);
#endif
}
return 0;
}
//查看名字是否有重复,重复侧加1
fflush(fp); //刷新
fseek(fp,0L,SEEK_END);//将指针置后
uint32_t readFileSize = ftell(fp);
if (readFileSize > _ONE_LOG_FILE_MAX_SIZE)
{
//超过约定数值,执行循环
sameFileFlag += 1;
changefile = 1;
}
else//在合理范围内
{
changefile = 0;
}
}
} while (1== changefile);
//无论如何还是能写入的
fwrite(pLoginfo,strlen(pLoginfo),1,fp);//向fp写入pLoginfo中前strlen(pLoginfo)的内容,成功返回1,否则为其他
fwrite("\n", 1, 1, fp);//增加结束符号
fflush(fp); //刷新
res = ftell(fp);//用于得到文件当前位置相对于文件首的偏移字节数
if (res > _ONE_LOG_FILE_MAX_SIZE)
{
//即超过多大的数量后,就要进行切换
fclose(fp);
fp = NULL;
changefile = 1;
}
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS //Windows系统
LeaveCriticalSection(&m_mutexLock);
#else //类Unix系统
pthread_mutex_unlock(&m_mutexLock);
#endif
}
return 1;
}
void CSysLog::SetWriteUseMutexMode(bool flag)
{
m_isUseMutexMode = flag;
}
// 功能:删除nDays天及之前的日志文件
// @nDays: 0-不删除日志,3-删除3天及之前的日志(保留今天、昨天、前天的日志) ...
void CSysLog::ClearLog(uint32_t nDays) // 删除N天前的日志
{
std::string sLogFolder = log_dir_path;
#ifdef OS_WINDOWS
WIN32_FIND_DATAA FindFileData;
char szFileName[512] = { 0 };
strcpy(szFileName, log_dir_path);
strcat(szFileName, "*.log");
HANDLE hFind = ::FindFirstFileA(szFileName, &FindFileData);
if (INVALID_HANDLE_VALUE == hFind)
return;
while (TRUE)
{
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 遇到文件夹
{}
else // 遇到文件
{
try
{
std::string sFileName = FindFileData.cFileName;
if (nameDelSize == sFileName.length())
{
std::string sFileFullPath = sLogFolder /*+ PATH_SEPARATOR*/ + sFileName;// 文件全路径
//int nFileName = atoi(sFileName.c_str()); // 取得文件的日期,如:20101030
SYSTEMTIME systi;
GetLocalTime(&systi);
tagDate dtBegin;
tagDate dtEnd;
//只是部分提取
dtBegin.year = atoi(sFileName.substr(0, 4).c_str());
dtBegin.month = atoi(sFileName.substr(5, 2).c_str());
dtBegin.day = atoi(sFileName.substr(8, 2).c_str());
dtEnd.year = systi.wYear;
dtEnd.month = systi.wMonth;
dtEnd.day = systi.wDay;
if ((uint32_t)DateDiff(dtBegin, dtEnd) >= nDays)
{
::DeleteFileA(sFileFullPath.c_str());
}
}
}
catch (std::exception)
{
}
}
if (!FindNextFileA(hFind, &FindFileData))
break;
}
FindClose(hFind);
#else
//UNIX系统下遍历日志文件
DIR *pDir = NULL;
struct dirent *pFile = NULL;
pDir = opendir(sLogFolder.c_str());
if (pDir == NULL)
return;
while ((pFile = readdir(pDir)) != NULL)
{
if (pFile->d_type & DT_DIR) //忽略目录
{
continue;
}
else
{
try
{
std::string sFileName = pFile->d_name;
std::string sFileFullPath = sLogFolder /*+ PATH_SEPARATOR*/ + sFileName;// 文件全路径
if (nameDelSize == (int)sFileName.length())
{
if (nameDelSize > 4)
{
std::string substr = sFileName.substr( nameDelSize - 4 ,4);
if(substr == std::string(".log"))
{
struct timeval tv;
struct timezone tz;
struct tm *t;
gettimeofday(&tv, &tz);
t = localtime(&tv.tv_sec);
tagDate dtBegin;
tagDate dtEnd;
//只是部分提取
dtBegin.year = atoi(sFileName.substr(0, 4).c_str());
dtBegin.month = atoi(sFileName.substr(5, 2).c_str());
dtBegin.day = atoi(sFileName.substr(8, 2).c_str());
dtEnd.year = 1900 + t->tm_year;
dtEnd.month = 1 + t->tm_mon;
dtEnd.day = t->tm_mday;
if (DateDiff(dtBegin, dtEnd) >= (long)nDays)
{
remove(sFileFullPath.c_str());
}
}
}
}
}
catch (std::exception)
{
}
}
}
closedir(pDir);
#endif
}
//是否闰年
int CSysLog::IsLeapYear(int year)
{
bool tmp = (year % 4 == 0) && (year % 100 != 0);
return tmp || (year % 400 == 0) ;
}
//得到date.month的最大天数
int CSysLog::GetLastDay(tagDate date)
{
int num;
switch (date.month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
num = 31;
break;
case 2:
num = 28 + IsLeapYear(date.year);
break;
default:
num = 30;
}
return num;
}
//日期是否合法
int CSysLog::IsDateValid(tagDate date)
{
if (date.year < 0 || date.month < 1 || date.month> 12)
return 0;
if (date.day <1 || date.day> GetLastDay(date))
return 0;
return 1;
}
//date+1
void CSysLog::AddDay(tagDate *date)
{
date->day++;
if (date->day > GetLastDay(*date))
{
date->day = 1;
date->month++;
if (date->month > 12)
{
date->month = 1;
date->year++;
}
}
}
//date1比date2小返回值为1,否则为0
int CSysLog::Compare(tagDate date1, tagDate date2)
{
if (date1.year < date2.year)
return 1;
if (date1.year <= date2.year && date1.month < date2.month)
return 1;
if (date1.year <= date2.year && date1.month <= date2.month && date1.day < date2.day)
return 1;
return 0;
}
//计算两个日期的间隔天数
long CSysLog::DateDiff(tagDate date1, tagDate date2)
{
long delta = 0;
tagDate date3;
//若date1 > date2,交换date1,date2
if (!Compare(date1, date2))
{
date3 = date2;
date2 = date1;
date1 = date3;
}
//date1比date2少时,date1日期加1
while (Compare(date1, date2))
{
AddDay(&date1);
delta++;
}
return delta;
}
common_platform.h
#ifndef COMMON_PLATFORM_H
#define COMMON_PLATFORM_H
//根据操作系统定义对应的宏
#if defined (_WIN32)|| defined(_WIN64)|| defined(__WIN32__) || defined(__WINDOWS__) //Windows平台
#define OS_WINDOWS
#elif defined(unix) || defined(__unix__) || defined(__unix) //Linux/Unix平台
#define OS_UNIX
#else //暂不支持其他平台
#error "Unsupported_operating_system_platform"
#endif
//若是windows系统
#ifdef OS_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <mmsystem.h>
#include <time.h>
#include <direct.h>
#include <corecrt_io.h>
#pragma comment( lib, "shell32.lib")
#endif
//若是unix系列系统
#ifdef OS_UNIX
#include <unistd.h>
#include <pthread.h>
#include <stdarg.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>// include such as 'memset' 'memcpy' 'strcpy' ..etc
#endif
#endif // COMMON_PLATFORM_H
模式二代码:
Tool.h
#pragma once
#include <iostream>
#include <vector>
void listSubdir(const char * dir, std::vector<std::string>& subdirs);
void ToUnixPath(std::string& s);
bool createMultiDir(const std::string& strPath);
Tool.cpp
#include "Tool.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <regex>
#include <direct.h>
#include <corecrt_io.h>
void listSubdir(const char * dir, std::vector<std::string>& subdirs)
{
subdirs.clear();
std::string dirStr = dir;
if (dirStr.length() <= 1)
return;
if (dirStr.at(dirStr.length() - 1) != '/')
{
dirStr.append("/");
}
dirStr += "*.*";
intptr_t handle;
_finddata_t findData;
handle = _findfirst(dirStr.data(), &findData);
if (handle == -1) // 检查是否成功
return;
do
{
// 是否是子目录并且不为"."或".."
if (findData.attrib & _A_SUBDIR)
{
if (strcmp(findData.name, ".") == 0
|| strcmp(findData.name, "..") == 0)
{
continue;
}
subdirs.push_back(findData.name);
}
else
{
//cout <<"file:"<< findData.name << endl;
}
} while (_findnext(handle, &findData) == 0); // 查找目录中的下一个文件
//cout << "Done!\n";
_findclose(handle);
}
void ToUnixPath(std::string& s)
{
std::string::size_type pos = 0;
while ((pos = s.find('\\', pos)) != std::string::npos)
{
s.replace(pos, 1, "/");
pos = pos + 2;
}
}
bool createMultiDir(const std::string& strPath)
{
std::string path = strPath;
ToUnixPath(path);
if (path[path.length() - 1] != '/')
{
path += '/';
}
std::string::size_type pos = 0;
std::string::size_type prevpos = 0;
std::string tempStr(path);
std::string strFolderPath;
std::string stdFolderName;
while ((pos = tempStr.find('/', pos)) != std::string::npos)
{
strFolderPath = tempStr.substr(0, pos);
stdFolderName = tempStr.substr(prevpos, pos - prevpos);
if (_access(strFolderPath.data(), 0) == 0)//目录存在
{
pos = pos + 1;
prevpos = pos;
continue;
}
_mkdir(strFolderPath.data());
pos = pos + 1;
prevpos = pos;
}
if (_access(path.data(), 0) == 0)
{
return true;
}
return false;
}
SysLog.h
/*M///
//M*/
#ifndef _CLASS_SYS_LOG_H_
#define _CLASS_SYS_LOG_H_
#include <stdio.h>
#include <string>
#include "common_platform.h"
struct tagDate
{
int year;
int month;
int day;
};
class CSysLog
{
public:
CSysLog(bool isUseWriteMutexMode = true);
public:
~CSysLog(void);
int WriteLogFile(const char *fmt, ...);
int WriteLogFile(const std::string& content);
//设置日志文件的基路径目录,路径请以'/'结尾. 若该目录不存在则创建.
void SetRootPath(std::string rootPath);
void SetAppName(std::string appName);
//设置是否删除过期的日志文件. maxDays=0:无保质期,不删除; maxDays>0:保质期天数.
void SetLogOverdueDays(int maxDays);
private:
void SetWriteUseMutexMode(bool flag);
private:
char m_log_dir_path[256];
FILE * m_fp; //文件描述符
int m_day;
int changefile; //是否切换文件,为1时切换
std::string m_appName; //日志文件名的一个固定标识
int sameFileFlag; //一天可能会有多个日志文件(量大时切分为多个文件存储),对应的日志文件名中会有0,1,2...的标识
std::string m_rootPath;
bool m_isUseMutexMode;//是否启用互斥写日志
int m_ExpiredDays;//日志的过期天数
#ifdef OS_WINDOWS
CRITICAL_SECTION m_mutexLock;
#else
pthread_mutex_t m_mutexLock;
#endif
private:
//找parentDirPath(以'/'结尾)下的所有子目录(不递归),根据判断子目录的命名(2022-11-07)来判断其是否过期,
//若过期,则删除该子目录下的m_appName.log文件.
void ClearLog(std::string parentDirPath, uint32_t nDays, tagDate curYYMMDD); // 删除N天前的日志;
public://现在其余地方也想使用该以下成员函数,将其改为公共、静态。
//以下是日期比较,根据是文件名上的日期和当前系统日期
/*static*/ int IsLeapYear(int year);//闰年
/*static*/ int GetLastDay(tagDate date);
/*static*/ int IsDateValid(tagDate date);
/*static*/ void AddDay(tagDate *date);
/*static*/ int Compare(tagDate date1, tagDate date2);
/*static*/ long DateDiff(tagDate date1, tagDate date2);//返回日期比较的结果
};
#endif //_CLASS_LOG_MY
SysLog.cpp
/*M///
//M*/
#include <iostream>
#include <direct.h>
#include <io.h>
#include <regex>
#include <iostream>
using namespace std;
#include "SysLog.h"
#include "Tool.h"
#define _ONE_LOG_FILE_MAX_SIZE (52428800)//10485760 10M, 52428800 50*1024*1024 50M
#define _DELETE_PRE_DATE_LOG (180) //N天前的记录
#define MaxJsonBuffer 1024
//不同系统的路径分割符
#ifdef OS_WINDOWS
const char PATH_SEPARATOR = '/';
#else
const char PATH_SEPARATOR = '/';
#endif
//日志文件的后缀
#define LOG_FILE_SUFF ".log"
CSysLog::CSysLog(bool isUseWriteMutexMode)
{
m_fp = NULL;
m_day = -1;
m_appName = "default_app_name";
changefile = 0;
sameFileFlag = 0;
m_ExpiredDays = _DELETE_PRE_DATE_LOG;
//写日志的互斥模式
SetWriteUseMutexMode(isUseWriteMutexMode);
if (isUseWriteMutexMode)
{
//初始化互斥变量
#ifdef OS_WINDOWS //Windows系统
InitializeCriticalSection(&m_mutexLock);
#else //类Unix系统
int res = pthread_mutex_init(&m_mutexLock, NULL);
if (res != 0)
{
exit(-1);
}
#endif
}
}
CSysLog::~CSysLog(void)
{
if (m_fp != NULL)
{
fclose(m_fp);
m_fp = NULL;
}
#ifdef OS_WINDOWS
DeleteCriticalSection(&m_mutexLock);
#else
pthread_mutex_destroy(&m_mutexLock);
#endif
}
//路径请以'/'结尾.
void CSysLog::SetRootPath(std::string rootPath)
{
if (rootPath.at(rootPath.length() - 1) != PATH_SEPARATOR)
{
std::string tmp = &PATH_SEPARATOR;
rootPath.append(tmp);
}
m_rootPath = rootPath;
memset(m_log_dir_path, 0, sizeof(m_log_dir_path));
strcpy(m_log_dir_path, m_rootPath.c_str());//c_str必带 /0
#ifdef OS_WINDOWS
if (_access(m_log_dir_path, 0) == -1)
createMultiDir(m_rootPath);
//_mkdir(m_log_dir_path);//如果不存在,创建目录
#else
if (access(log_dir_path, 0) == -1)
mkdir(log_dir_path, 0xFFFFFFFF/*S_IROTH|S_IWOTH|S_IXOTH*/);//如果不存在,就创建子目录
#endif
}
void CSysLog::SetAppName(std::string appName)
{
if (appName.empty() || appName.length() > sizeof(m_appName))
return;
m_appName = appName;
}
void CSysLog::SetLogOverdueDays(int maxDays)
{
if (maxDays >= 0)
m_ExpiredDays = maxDays;
}
int CSysLog::WriteLogFile(const std::string& content)
{
const char* fmt = content.data();
return WriteLogFile(fmt);
}
int CSysLog::WriteLogFile(const char *fmt, ...)
{
if (strlen(fmt) > MaxJsonBuffer)
return 0;
char pLoginfo[MaxJsonBuffer];
memset(pLoginfo, 0x00, MaxJsonBuffer);//清空
va_list ap;
int year = 0, month = 0,cur_day = 0;
#ifdef OS_WINDOWS //Windows系统
SYSTEMTIME sys;
GetLocalTime( &sys );
//日志时间
sprintf(pLoginfo,"[%4d-%02d-%02d %02d:%02d:%02d.%03d] ",sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond,sys.wMilliseconds);
year = sys.wYear;
month = sys.wMonth;
cur_day = sys.wDay;
#else //类Unix系统
struct timeval tv;
struct timezone tz;
struct tm *t;
gettimeofday(&tv, &tz);
t = localtime(&tv.tv_sec);
//日志时间
sprintf(pLoginfo,"[%4d-%02d-%02d %02d:%02d:%02d.%03d] ",
1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, (int)t->tm_sec, (int)(tv.tv_usec/1000));
year = 1900 + t->tm_year;
month = 1 + t->tm_mon;
cur_day = t->tm_mday;
#endif
va_start(ap,fmt);
vsprintf(pLoginfo+strlen(pLoginfo),fmt,ap);
va_end(ap);
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS
EnterCriticalSection(&m_mutexLock);
#else
pthread_mutex_lock(&m_mutexLock);
#endif
}
char yymmdd[32] = { 0 };
sprintf(yymmdd, "%4d-%02d-%02d", year, month, cur_day);
tagDate curYYMMDD;
curYYMMDD.year = year;
curYYMMDD.month = month;
curYYMMDD.day = cur_day;
//yymmddFullPath为诸如C:/Log/2022-11-04/
std::string yymmddFullPath = m_rootPath + yymmdd + PATH_SEPARATOR;
//检查并创建诸如C:/Log/2022-11-04/目录
#ifdef OS_WINDOWS
if (_access(yymmddFullPath.data(), 0) == -1)
createMultiDir(yymmddFullPath);
//_mkdir(yymmddFullPath.data());//如果不存在,创建目录
#else
if (access(yymmddFullPath.data(), 0) == -1)
mkdir(yymmddFullPath.data(), 0xFFFFFFFF/*S_IROTH|S_IWOTH|S_IXOTH*/);//如果不存在,就创建子目录
#endif
#ifdef OS_WINDOWS
if (_access(yymmddFullPath.data(), 0) == -1)
{
if (m_isUseMutexMode)
LeaveCriticalSection(&m_mutexLock);
return 0;
}
#else
if (access(yymmddFullPath.data(), 0) == -1)
{
if (m_isUseMutexMode)
pthread_mutex_unlock(&m_mutexLock);
return 0;
}
#endif
logFileFullPath为诸如C:/Log/2022-11-04/myname.log
std::string logFileFullPath = yymmddFullPath + m_appName + LOG_FILE_SUFF;
//检查logFileFullPath是否存在
bool isLogFileExist = true;
#ifdef OS_WINDOWS
if (_access(logFileFullPath.data(), 0) == -1)
isLogFileExist = false;//如果不存在,创建目录
#else
if (access(logFileFullPath.data(), 0) == -1)
isLogFileExist = false;
#endif
if (isLogFileExist)//日志文件已经存在
{
if (m_fp == NULL)
{
m_fp = fopen(logFileFullPath.data(), "a+");//a+ : 可读可写, 可以不存在, 必不能修改原有内容, 只能在结尾追加写, 文件指针只对读有效 (写操作会将文件指针移动到文件尾)
if (m_fp == NULL)
{
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS
LeaveCriticalSection(&m_mutexLock);
#else
pthread_mutex_unlock(&m_mutexLock);
#endif
}
return 0;
}
}
}
else//日志文件不存在
{
if (m_fp != NULL)//需要关闭之前旧的文件描述符
{
fflush(m_fp);
fclose(m_fp);
m_fp = NULL;
}
//新建日志文件
m_fp = fopen(logFileFullPath.data(), "a+");//a+ : 可读可写, 可以不存在, 必不能修改原有内容, 只能在结尾追加写, 文件指针只对读有效 (写操作会将文件指针移动到文件尾)
if (m_fp == NULL)
{
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS
LeaveCriticalSection(&m_mutexLock);
#else
pthread_mutex_unlock(&m_mutexLock);
#endif
}
return 0;
}
}
//无论如何还是能写入的
fwrite(pLoginfo,strlen(pLoginfo),1,m_fp);//向fp写入pLoginfo中前strlen(pLoginfo)的内容,成功返回1,否则为其他
fwrite("\n", 1, 1, m_fp);//增加结束符号
fflush(m_fp); //刷新
//写完了,释放锁
if (m_isUseMutexMode)
{
#ifdef OS_WINDOWS //Windows系统
LeaveCriticalSection(&m_mutexLock);
#else //类Unix系统
pthread_mutex_unlock(&m_mutexLock);
#endif
}
//在这里进行目录文件的超时判断
if (m_ExpiredDays <= 0)
return 1;
if (m_day == cur_day)//不满足 跨天或是程序刚启动,这时无需进行过期判断
return 1;
m_day = cur_day;
//在 rootPath(诸如C:/Log/)下查找所有的类似于2022-11-04命名规则的子目录,并提取具体日期用于判断是否过期.
ClearLog(m_rootPath, m_ExpiredDays, curYYMMDD);
return 1;
}
void CSysLog::SetWriteUseMutexMode(bool flag)
{
m_isUseMutexMode = flag;
}
//是否闰年
int CSysLog::IsLeapYear(int year)
{
bool tmp = (year % 4 == 0) && (year % 100 != 0);
return tmp || (year % 400 == 0) ;
}
//得到date.month的最大天数
int CSysLog::GetLastDay(tagDate date)
{
int num;
switch (date.month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
num = 31;
break;
case 2:
num = 28 + IsLeapYear(date.year);
break;
default:
num = 30;
}
return num;
}
//日期是否合法
int CSysLog::IsDateValid(tagDate date)
{
if (date.year < 0 || date.month < 1 || date.month> 12)
return 0;
if (date.day <1 || date.day> GetLastDay(date))
return 0;
return 1;
}
//date+1
void CSysLog::AddDay(tagDate *date)
{
date->day++;
if (date->day > GetLastDay(*date))
{
date->day = 1;
date->month++;
if (date->month > 12)
{
date->month = 1;
date->year++;
}
}
}
//date1比date2小返回值为1,否则为0
int CSysLog::Compare(tagDate date1, tagDate date2)
{
if (date1.year < date2.year)
return 1;
if (date1.year <= date2.year && date1.month < date2.month)
return 1;
if (date1.year <= date2.year && date1.month <= date2.month && date1.day < date2.day)
return 1;
return 0;
}
//计算两个日期的间隔天数
long CSysLog::DateDiff(tagDate date1, tagDate date2)
{
long delta = 0;
tagDate date3;
//若date1 > date2,交换date1,date2
if (!Compare(date1, date2))
{
date3 = date2;
date2 = date1;
date1 = date3;
}
//date1比date2少时,date1日期加1
while (Compare(date1, date2))
{
AddDay(&date1);
delta++;
}
return delta;
}
void CSysLog::ClearLog(std::string parentDirPath, uint32_t nDays, tagDate curYYMMDD)
{
#ifdef OS_WINDOWS
if (_access(parentDirPath.data(), 0) == -1)
return;
#else
if (access(parentDirPath.data(), 0) == -1)
return;
#endif
if (parentDirPath.length() <= 1)
return;
std::vector<std::string> subdirs;
//查找某目录下的子目录,不递归
listSubdir(parentDirPath.data(), subdirs);
//正则表达式筛选诸如'2022-11-07'规则命名的子目录
std::regex pattern("[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}");
for each (auto var in subdirs)
{
smatch results;
if (std::regex_match(var, results, pattern))
{
smatch::iterator it = results.begin();
int i = 0;
for (; it != results.end(); ++it, ++i)
{
std::string childDir = *it;
tagDate dtBegin;
dtBegin.year = atoi(childDir.substr(0, 4).data());
dtBegin.month = atoi(childDir.substr(5, 2).data());
dtBegin.day = atoi(childDir.substr(8, 2).data());
long diff = DateDiff(dtBegin, curYYMMDD);
if (diff <= nDays)//没有过期
continue;
//删除过期的日志文件
std::string tmp = &PATH_SEPARATOR;
if (parentDirPath.at(parentDirPath.length() - 1) != PATH_SEPARATOR)
parentDirPath.append(tmp);
std::string logToDel = parentDirPath + var + tmp + m_appName + LOG_FILE_SUFF;
remove(logToDel.c_str());
}
}
}
}
common_platform.h与模式一 一致
模式二调用范例:
#include "stdafx.h"
#include <iostream>
#include <regex>
#include "SysLog.h"
#include "Tool.h"
using namespace std;
int main()
{
std::string dirname = "C:/Users/zhangsan/Desktop/TestLog/TestLog/ABCDE/LogDir/";
bool isUseWriteMutexMode = true;
CSysLog logger(isUseWriteMutexMode);
logger.SetAppName("myapp");// 日志文件名为myapp.log
logger.SetRootPath(dirname);
logger.SetLogOverdueDays(60);
logger.WriteLogFile("你好啊 啊 啊啊啊 啊啊啊啊啊啊 啊啊啊啊啊啊啊啊啊 ");
logger.WriteLogFile("%s is %d years old", "sam", 25);
return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)