//logger.h
/*
//类名:CLogger
//功能介绍:Win平台日志记录功能,多线程安全,支持写日志级别的设置,日志格式包含日志等级,日志时间,文件名,行号信息
//作者:sunflover 2016-1-15 14:31:27
//使用方法:
1:将logger.h,logger.cpp添加到项目中
2:设置logger.cpp的预编译头选项为“不使用预编译头”
3:使用代码示例:
#include "Logger.h"
using namespace LOGGER;
CLogger logger(LogLevel_Info,CLogger::GetAppPathA().append("log\\"));
void main()
{
logger.TraceFatal("TraceFatal %d", 1);
logger.TraceError("TraceError %s", "sun");
logger.TraceWarning("TraceWarning");
logger.TraceInfo("TraceInfo");
logger.ChangeLogLevel(LOGGER::LogLevel_Error);
logger.TraceFatal("TraceFatal %d", 2);
logger.TraceError("TraceError %s", "sun2");
logger.TraceWarning("TraceWarning");
logger.TraceInfo("TraceInfo");
}
执行结果:20160126_101329.log文件内容如下
Fatal 2016-01-26 10:13:29 TraceFatal 1
Error 2016-01-26 10:13:29 TraceError sun
Warning 2016-01-26 10:13:29 TraceWarning
Info 2016-01-26 10:13:29 TraceInfo
Fatal 2016-01-26 10:13:29 TraceFatal 2
Error 2016-01-26 10:13:29 TraceError sun2
*/
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <Windows.h>
#include <stdio.h>
#include <string>
namespace LOGGER
{
//日志级别的提示信息
static const std::string strFatalPrefix = "Fatal\t";
static const std::string strErrorPrefix = "Error\t";
static const std::string strWarningPrefix = "Warning\t";
static const std::string strInfoPrefix = "Info\t";
//日志级别枚举
typedef enum EnumLogLevel
{
LogLevel_Stop = 0, //什么都不记录
LogLevel_Fatal, //只记录严重错误
LogLevel_Error, //记录严重错误,普通错误
LogLevel_Warning, //记录严重错误,普通错误,警告
LogLevel_Info //记录严重错误,普通错误,警告,提示信息(也就是全部记录)
};
class CLogger
{
public:
//nLogLevel:日志记录的等级,可空
//strLogPath:日志目录,可空
//strLogName:日志名称,可空
CLogger(EnumLogLevel nLogLevel = EnumLogLevel::LogLevel_Info, const std::string strLogPath = "", const std::string strLogName = "");
//析构函数
virtual ~CLogger();
public:
//写严重错误信息
void TraceFatal(const char *lpcszFormat, ...);
//写错误信息
void TraceError(const char *lpcszFormat, ...);
//写警告信息
void TraceWarning(const char *lpcszFormat, ...);
//写提示信息
void TraceInfo(const char *lpcszFormat, ...);
//改变写日志级别
void ChangeLogLevel(EnumLogLevel nLevel);
//获取程序运行路径
static std::string GetAppPathA();
//格式化字符串
static std::string FormatString(const char *lpcszFormat, ...);
private:
//写文件操作
void Trace(const std::string &strLog);
//获取当前系统时间
std::string GetTime();
//文件全路径得到文件名
const char *path_file(const char *path, char splitter);
private:
//写日志文件流
FILE * m_pFileStream;
//写日志级别
EnumLogLevel m_nLogLevel;
//日志目录
std::string m_strLogPath;
//日志的名称
std::string m_strLogName;
//日志文件全路径
std::string m_strLogFilePath;
//线程同步的临界区变量
CRITICAL_SECTION m_cs;
};
}
#endif
//logger.cpp
#include "logger.h"
#include <time.h>
#include <stdarg.h>
#include <direct.h>
#include <vector>
#include <Dbghelp.h>
#pragma comment(lib,"Dbghelp.lib")
using std::string;
using std::vector;
namespace LOGGER
{
CLogger::CLogger(EnumLogLevel nLogLevel, const std::string strLogPath, const std::string strLogName)
:m_nLogLevel(nLogLevel),
m_strLogPath(strLogPath),
m_strLogName(strLogName)
{
//初始化
m_pFileStream = NULL;
if (m_strLogPath.empty())
{
m_strLogPath = GetAppPathA();
}
if (m_strLogPath[m_strLogPath.length()-1] != '\\')
{
m_strLogPath.append("\\");
}
//创建文件夹
MakeSureDirectoryPathExists(m_strLogPath.c_str());
//创建日志文件
if (m_strLogName.empty())
{
time_t curTime;
time(&curTime);
tm tm1;
localtime_s(&tm1, &curTime);
//日志的名称如:201601012130.log
m_strLogName = FormatString("%04d%02d%02d_%02d%02d%02d.log", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday, tm1.tm_hour, tm1.tm_min, tm1.tm_sec);
}
m_strLogFilePath = m_strLogPath.append(m_strLogName);
//以追加的方式打开文件流
fopen_s(&m_pFileStream, m_strLogFilePath.c_str(), "a+");
InitializeCriticalSection(&m_cs);
}
//析构函数
CLogger::~CLogger()
{
//释放临界区
DeleteCriticalSection(&m_cs);
//关闭文件流
if (m_pFileStream)
{
fclose(m_pFileStream);
m_pFileStream = NULL;
}
}
//文件全路径得到文件名
const char *CLogger::path_file(const char *path, char splitter)
{
return strrchr(path, splitter) ? strrchr(path, splitter) + 1 : path;
}
//写严重错误信息
void CLogger::TraceFatal(const char *lpcszFormat, ...)
{
//判断当前的写日志级别
if (EnumLogLevel::LogLevel_Fatal > m_nLogLevel)
return;
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); //初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; //获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); //创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); //重置变量参数
}
if (strResult.empty())
{
return;
}
string strLog = strFatalPrefix;
strLog.append(GetTime()).append(strResult);
//写日志文件
Trace(strLog);
}
//写错误信息
void CLogger::TraceError(const char *lpcszFormat, ...)
{
//判断当前的写日志级别
if (EnumLogLevel::LogLevel_Error > m_nLogLevel)
return;
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); //初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; //获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); //创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); //重置变量参数
}
if (strResult.empty())
{
return;
}
string strLog = strErrorPrefix;
strLog.append(GetTime()).append(strResult);
//写日志文件
Trace(strLog);
}
//写警告信息
void CLogger::TraceWarning(const char *lpcszFormat, ...)
{
//判断当前的写日志级别
if (EnumLogLevel::LogLevel_Warning > m_nLogLevel)
return;
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); //初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; //获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); //创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); //重置变量参数
}
if (strResult.empty())
{
return;
}
string strLog = strWarningPrefix;
strLog.append(GetTime()).append(strResult);
//写日志文件
Trace(strLog);
}
//写一般信息
void CLogger::TraceInfo(const char *lpcszFormat, ...)
{
//判断当前的写日志级别
if (EnumLogLevel::LogLevel_Info > m_nLogLevel)
return;
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); //初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; //获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); //创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); //重置变量参数
}
if (strResult.empty())
{
return;
}
string strLog = strInfoPrefix;
strLog.append(GetTime()).append(strResult);
//写日志文件
Trace(strLog);
}
//获取系统当前时间
string CLogger::GetTime()
{
time_t curTime;
time(&curTime);
tm tm1;
localtime_s(&tm1, &curTime);
//2016-01-01 21:30:00
string strTime = FormatString("%04d-%02d-%02d %02d:%02d:%02d ", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday, tm1.tm_hour, tm1.tm_min, tm1.tm_sec);
return strTime;
}
//改变写日志级别
void CLogger::ChangeLogLevel(EnumLogLevel nLevel)
{
m_nLogLevel = nLevel;
}
//写文件操作
void CLogger::Trace(const string &strLog)
{
try
{
//进入临界区
EnterCriticalSection(&m_cs);
//若文件流没有打开,则重新打开
if (NULL == m_pFileStream)
{
fopen_s(&m_pFileStream, m_strLogFilePath.c_str(), "a+");
if (!m_pFileStream)
{
return;
}
}
//写日志信息到文件流
fprintf(m_pFileStream, "%s\n", strLog.c_str());
fflush(m_pFileStream);
//离开临界区
LeaveCriticalSection(&m_cs);
}
//若发生异常,则先离开临界区,防止死锁
catch (...)
{
LeaveCriticalSection(&m_cs);
}
}
string CLogger::GetAppPathA()
{
char szFilePath[MAX_PATH] = { 0 }, szDrive[MAX_PATH] = { 0 }, szDir[MAX_PATH] = { 0 }, szFileName[MAX_PATH] = { 0 }, szExt[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));
_splitpath_s(szFilePath, szDrive, szDir, szFileName, szExt);
string str(szDrive);
str.append(szDir);
return str;
}
string CLogger::FormatString(const char *lpcszFormat, ...)
{
string strResult;
if (NULL != lpcszFormat)
{
va_list marker = NULL;
va_start(marker, lpcszFormat); //初始化变量参数
size_t nLength = _vscprintf(lpcszFormat, marker) + 1; //获取格式化字符串长度
std::vector<char> vBuffer(nLength, '\0'); //创建用于存储格式化字符串的字符数组
int nWritten = _vsnprintf_s(&vBuffer[0], vBuffer.size(), nLength, lpcszFormat, marker);
if (nWritten > 0)
{
strResult = &vBuffer[0];
}
va_end(marker); //重置变量参数
}
return strResult;
}
}
有图有真相: