写程序通常用要输出日志,以便程序修复BUG和查看程序运行情况。下面是C++语言封装的一个日志类,使用的C语言下的通用文件操作函数,因此可以用于window,linux等平台下开发。C语言下的通用文件操作函数具有线程安全性,因此可以用于多线程程序环境。日志类能在指定文件夹下生成以日期命名的日志文件,并且能根据参数保留最近多少天的日志文件,即之前的文件自动清除掉。
下面是日志类的头文件:
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <iostream>
#include <fstream>
#include <time.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
using namespace std;
namespace SgLib
{
class Logger
{
public:
enum EM_LOG_LEVEL
{
TRACE,
DEBUG,
INFORMATION,
WARNING,
ERROR,
FATAL
}; // 日志等级
enum EM_LOG_TARGET
{
TAR_FILE,
TAR_TERMINAL,
TAR_FILE_AND_TAR_TERMINAL
}; // 日志输出目标
public:
Logger();
Logger(EM_LOG_TARGET target, EM_LOG_LEVEL level, const char *pszLogFolder, const char *pszLogFileName, int iKeepLogDays);
~Logger();
void trace(const string &text);
void debug(const string &text);
void information(const string &text);
void warn(const string &text);
void error(const string &text);
void fatal(const string &text);
private:
int getDiffDays(int iYear1, int iMonth1, int iDay1, int iYear2, int iMonth2, int iDay2);
void handleOldLogs(tm *);
FILE *m_pFile;
ofstream m_outfile; // 将日志输出到文件的流对象
EM_LOG_TARGET m_target; // 日志输出目标
char m_szLogFolder[256]; // 日志文件路径
char m_szLogFileName[200];
EM_LOG_LEVEL m_level; // 日志等级
void output(const string &text, EM_LOG_LEVEL act_level); // 输出行为
char m_szOldDay[30];
int m_iKeepLogDays; // 日志保留天数 -1:无穷天
};
}
#endif //_LOGGER_H_
日志进行了分级,具体分为TRACE、DEBUG、INFORMATION、WARNING、ERROR、FATAL六个等级。创建日志对象的时候,level参数指定了日志的最小输出级别,即小于该级别参数的日志信息不会输出。
target参数则指定日志输出到控制台还是文件,还是控制台和日志均同时输出。
下面是日志实现类的具体实现细节。
日志类的构造函数如下:
Logger::Logger(EM_LOG_TARGET target, EM_LOG_LEVEL level, const char *pszLogFolder, const char *pszLogFileName, int iKeepLogDays)
{
m_pFile = NULL;
m_target = target;
m_level = level;
m_iKeepLogDays = iKeepLogDays;
if (strlen(pszLogFolder) > sizeof(m_szLogFolder) - 1)
{
cout << "日志的保存目录过长,程序将不能正确保存日志" << endl;
return;
}
if (strlen(pszLogFileName) > sizeof(m_szLogFileName) - 1)
{
cout << "日志的保存文件名过长,程序将不能正确保存日志" << endl;
return;
}
if (access(pszLogFolder, 0) != 0)
{
mkdir(pszLogFolder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
}
strcpy(m_szLogFolder, pszLogFolder);
strcpy(m_szLogFileName, pszLogFileName);
}
具体级别的日志输出以information为例子,其他级别的日志输出函数类似。
void Logger::information(const string &text)
{
output(text, INFORMATION);
}
void Logger::output(const string &text, EM_LOG_LEVEL act_level)
{
string prefix;
if (act_level == TRACE)
prefix = "[TRA]";
else if (act_level == DEBUG)
prefix = "[DEB]";
else if (act_level == INFORMATION)
prefix = "[INF]";
else if (act_level == WARNING)
prefix = "[WAR]";
else if (act_level == ERROR)
prefix = "[ERR]";
else if (act_level == FATAL)
prefix = "[FAT]";
// 如果时间发生变化,需要关闭老文件,创建新文件
char curDay[64];
char curTime[64];
time_t ptime;
time(&ptime); // time_t time (time_t* timer);
tm * pTm = localtime(&ptime);
strftime(curDay, sizeof(curDay), "%Y-%m-%d", pTm);
strftime(curTime, sizeof(curTime), "%Y-%m-%d %H:%M:%S", pTm);
handleOldLogs(pTm);
if (strcmp(m_szOldDay, curDay) != 0)
{
if (m_pFile != NULL)
{
fclose(m_pFile);
}
char szLogFile[500] = {0};
sprintf(szLogFile, "%s/%s_%s.txt", m_szLogFolder, m_szLogFileName, curDay);
m_pFile = fopen(szLogFile, "w");
if (m_pFile == NULL)
{
cout << "创建文件失败!" << endl;
return;
}
}
char szCon[8000] = {0};
sprintf(szCon, "%s[%s]:%s\n", prefix.c_str(), curTime, text.c_str());
if (m_level <= act_level && m_target != TAR_FILE)
{
// 当前等级设定的等级才会显示在终端,且不能是只文件模式
cout << szCon;
}
if (m_target != TAR_TERMINAL)
fwrite(szCon, strlen(szCon), 1, m_pFile);
fflush(m_pFile); //刷新缓冲区
strcpy(m_szOldDay, curDay);
}
handleOldLogs函数处理了过期的日志文件,即超过保留日志天数的会被程序删除掉。输出日志时候,程序会判断日期是否发生了变化。如果发生变化,将会关闭当天的文件,并创建一个新的日期的日志文件。 下面是工程在vscode里的快照。
程序运行后生成日志的快照:
想要完整源码的朋友,可以去这里下载。
http://www.kbase12.com/linuxcpp/doc/detail?id=4edf01b35f4145c5bcaef0ac94fdb3f2