Windows 平台下轻量级日志记录

 

最近接手一个需求,编译环境为c++ 98, 

我是开发一小块独立功能,日志还要自己打

 

头文件 Logger.h

#ifndef LOGGER_H_
#define LOGGER_H_
#include <Windows.h>
#include <stdio.h>
#include <time.h>
#include <functional>
#pragma warning(disable:4996)
#pragma warning(disable:4482)




/*
* 类名:Logger
* 作用:提供写日志功能,支持多线程,支持可变形参数操作,支持写日志级别的设置
* 接口:SetLogLevel:设置写日志级别
TraceKeyInfo:忽略日志级别,写关键信息
TraceError:写错误信息
TraceWarning:写警告信息
TraceInfo:写一般信息

*使用:
#include "Common/Logger/Logger.h"
Logger log;

log.SetLogLevel(LogLevelAll);
log.TraceInfo("Ready Go");
*/
//日志级别的提示信息
static const char * KEYINFOPREFIX = " [Key]:";
static const char * ERRORPREFIX = " [Error]:";
static const char * WARNINGPREFIX = " [Warning]:";
static const char * INFOPREFIX = " [Info]:";

static const int MAX_STR_LEN = 20480;
//日志级别枚举
enum EnumLogLevel
{
    LogLevelAll = 0,    //所有信息都写日志
    LogLevelMid,        //写错误、警告信息
    LogLevelNormal,     //只写错误信息
    LogLevelStop        //不写日志
};

class Logger
{
public:
    //默认构造函数
    Logger();
    //构造函数
    Logger(const char * strLogPath, EnumLogLevel nLogLevel = EnumLogLevel::LogLevelNormal);
    //析构函数
    virtual ~Logger();
public:
    //写关键信息
    void TraceKeyInfo(const char * strInfo, ...);
    //写错误信息
    void TraceError(const char* strInfo, ...);
    //写警告信息
    void TraceWarning(const char * strInfo, ...);
    //写一般信息
    void TraceInfo(const char * strInfo, ...);
    //设置写日志级别
    void SetLogLevel(EnumLogLevel nLevel);
    //是否设置控制台输出
    void SetConsoleOutput(bool flag){ m_isStandOut = flag; }
private:
    //写文件操作
    void Trace(const char * strInfo);
    //获取当前系统时间
    char * GetCurrentTime();
    //创建日志文件名称
    void GenerateLogName();
    //创建日志路径
    void CreateLogPath();
private:
    //是否同步控制台输出
    bool m_isStandOut;
    //日志切割的时间
    tm  m_pTimeInfo;
    //写日志文件流
    FILE * m_pFileStream;
    //写日志级别
    EnumLogLevel m_nLogLevel;
    //日志的路径
    char m_strLogPath[MAX_STR_LEN];
    //日志的名称
    char m_strCurLogName[MAX_STR_LEN];
    //线程同步的临界区变量
    CRITICAL_SECTION m_cs;
};
#endif

 

实现文件 Logger.cpp

#include "Logger.h"
#include <time.h>
#include <string.h>
#include <stdarg.h>
#include <imagehlp.h>
#include <stdio.h>
#include <direct.h>
#pragma comment(lib, "DbgHelp.lib")


Logger AILog;
#include <iostream>
//默认构造函数
Logger::Logger()
{
    //初始化
    memset(m_strLogPath, 0, MAX_STR_LEN);
    memset(m_strCurLogName, 0, MAX_STR_LEN);
    m_pFileStream = NULL;
    //设置默认的写日志级别
    m_nLogLevel = EnumLogLevel::LogLevelNormal;
    //初始化临界区变量
    InitializeCriticalSection(&m_cs);
    //创建日志文件名
    CreateLogPath();
    GenerateLogName();
    SetLogLevel(LogLevelAll);
    m_isStandOut = false;
}

//构造函数
Logger::Logger(const char * strLogPath, EnumLogLevel nLogLevel) :m_nLogLevel(nLogLevel)
{
    //初始化
    m_pFileStream = NULL;
    strcpy(m_strLogPath, strLogPath);
    InitializeCriticalSection(&m_cs);
    CreateLogPath();
    GenerateLogName();
    SetLogLevel(LogLevelAll);
    m_isStandOut = false;
}


//析构函数
Logger::~Logger()
{
    //释放临界区
    DeleteCriticalSection(&m_cs);
    //关闭文件流
    if (m_pFileStream)
        fclose(m_pFileStream);
}

//写关键信息接口
void Logger::TraceKeyInfo(const char * strInfo, ...)
{
    if (!strInfo)
        return;
    char pTemp[MAX_STR_LEN] = { 0 };
    char *pTime = GetCurrentTime();
    strcpy(pTemp, pTime);
    strcat(pTemp, KEYINFOPREFIX);
    //获取可变形参
    va_list arg_ptr = NULL;
    va_start(arg_ptr, strInfo);
    vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
    va_end(arg_ptr);
    //写日志文件
    Trace(pTemp);
    arg_ptr = NULL;
    delete pTime;

}

//写错误信息
void Logger::TraceError(const char* strInfo, ...)
{
    //判断当前的写日志级别,若设置为不写日志则函数返回
    if (m_nLogLevel >= EnumLogLevel::LogLevelStop)
        return;
    if (!strInfo)
        return;
    char pTemp[MAX_STR_LEN] = { 0 };
    char *pTime = GetCurrentTime();
    strcpy(pTemp, pTime);
    strcat(pTemp, ERRORPREFIX);
    va_list arg_ptr = NULL;
    va_start(arg_ptr, strInfo);
    vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
    va_end(arg_ptr);
    Trace(pTemp);
    arg_ptr = NULL;
    delete pTime;
}

//写警告信息
void Logger::TraceWarning(const char * strInfo, ...)
{
    //判断当前的写日志级别,若设置为只写错误信息则函数返回
    if (m_nLogLevel >= EnumLogLevel::LogLevelNormal)
        return;
    if (!strInfo)
        return;
    char pTemp[MAX_STR_LEN] = { 0 };
    char *pTime = GetCurrentTime();
    strcpy(pTemp, pTime);
    strcat(pTemp, WARNINGPREFIX);
    va_list arg_ptr = NULL;
    va_start(arg_ptr, strInfo);
    vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
    va_end(arg_ptr);
    Trace(pTemp);
    arg_ptr = NULL;
    delete pTime;
}


//写一般信息
void Logger::TraceInfo(const char * strInfo, ...)
{
    //判断当前的写日志级别,若设置只写错误和警告信息则函数返回
    if (m_nLogLevel >= EnumLogLevel::LogLevelMid)
        return;
    if (!strInfo)
        return;
    char pTemp[MAX_STR_LEN] = { 0 };
    char *pTime = GetCurrentTime();
    strcpy(pTemp, pTime);
    strcat(pTemp, INFOPREFIX);
    va_list arg_ptr = NULL;
    va_start(arg_ptr, strInfo);
    vsprintf(pTemp + strlen(pTemp), strInfo, arg_ptr);
    va_end(arg_ptr);
    Trace(pTemp);
    arg_ptr = NULL;
    delete pTime;
}

//获取系统当前时间
char * Logger::GetCurrentTime()
{
    time_t curTime;
    struct tm * pTimeInfo = NULL;
    time(&curTime);
    pTimeInfo = localtime(&curTime);
    char *temp = new char[MAX_STR_LEN];
    sprintf(temp, "%04d-%02d-%02d %02d:%02d:%02d", pTimeInfo->tm_year + 1990, pTimeInfo->tm_mon + 1, pTimeInfo->tm_mday, pTimeInfo->tm_hour, pTimeInfo->tm_min, pTimeInfo->tm_sec);
    char * pTemp = temp;
    return pTemp;
}

//设置写日志级别
void Logger::SetLogLevel(EnumLogLevel nLevel)
{
    m_nLogLevel = nLevel;
}

//写文件操作
void Logger::Trace(const char * strInfo)
{
    if (!strInfo)
        return;
    try
    {
        //进入临界区
        EnterCriticalSection(&m_cs);
        time_t curTime;
        struct tm * pTimeInfo = NULL;
        time(&curTime);
        pTimeInfo = localtime(&curTime);
        if (
            pTimeInfo->tm_year != m_pTimeInfo.tm_year 
            || pTimeInfo->tm_mon != m_pTimeInfo.tm_mon 
            || pTimeInfo->tm_mday != m_pTimeInfo.tm_mday
//             || pTimeInfo->tm_hour != m_pTimeInfo.tm_hour 
//             || pTimeInfo->tm_min != m_pTimeInfo.tm_min
            ){
            //std::cout << "日志需要切割了" << std::endl;
            GenerateLogName();
        }

        //若文件流没有打开,则重新打开
        if (!m_pFileStream)
        {
            char temp[1024] = { 0 };
            strcat(temp, m_strLogPath);
            strcat(temp, m_strCurLogName);
            m_pFileStream = fopen(temp, "a+");
            if (!m_pFileStream)
            {
                return;
            }
        }
        //写日志信息到文件流
        fprintf(m_pFileStream, "%s\n", strInfo);
        fflush(m_pFileStream);
        //写日志到控制台
        if (m_isStandOut)
        {
            printf("%s\n",strInfo);
        }

        //离开临界区
        LeaveCriticalSection(&m_cs);
    }
    //若发生异常,则先离开临界区,防止死锁
    catch (...)
    {
        LeaveCriticalSection(&m_cs);
    }
}

//创建日志文件的名称
void Logger::GenerateLogName()
{
    time_t curTime;
    struct tm * pTimeInfo = NULL;
    time(&curTime);
    pTimeInfo = localtime(&curTime);
    char temp[1024] = { 0 };
    //日志的名称如:2013-01-01.log
    sprintf(temp, "%04d-%02d-%02d-%02d_AI.log", (pTimeInfo->tm_year + 1900), (pTimeInfo->tm_mon + 1), pTimeInfo->tm_mday, pTimeInfo->tm_hour);
    if (0 != strcmp(m_strCurLogName, temp))
    {
        strcpy(m_strCurLogName, temp);
        if (m_pFileStream)
        {
            fflush(m_pFileStream);
            fclose(m_pFileStream);
        }
            
        char newTemp[1024] = { 0 };
        strcat(newTemp, m_strLogPath);
        strcat(newTemp, m_strCurLogName);
        //以追加的方式打开文件流
        m_pFileStream = fopen(newTemp, "a+");
    }
    m_pTimeInfo = *(pTimeInfo);
    //std::cout << m_pTimeInfo.tm_year << "-" << m_pTimeInfo.tm_mon << "-" << m_pTimeInfo.tm_mday << " " << m_pTimeInfo.tm_hour << ":" << m_pTimeInfo.tm_min << std::endl;

}

//创建日志文件的路径
void Logger::CreateLogPath()
{
    //if (0 != strlen(m_strLogPath))
    //{
    //    strcat(m_strLogPath, "\\");
    //}
    //MakeSureDirectoryPathExists(m_strLogPath);
    memset(m_strLogPath, 0, sizeof(m_strLogPath));
    char *buffer;
    //也可以将buffer作为输出参数
    if ((buffer = getcwd(NULL, 0)) == NULL)
    {
        strcat(m_strLogPath, "\\");
    }
    else
    {
        strcat(m_strLogPath, buffer);
        strcat(m_strLogPath, "\\LOGAI\\");
        free(buffer);
    }
    MakeSureDirectoryPathExists(m_strLogPath);

}

 

简单测试一下

#include <iostream>
#include "Logger.h"

using namespace std;
extern Logger AILog;//使用已经生成对象,也可以构建自己的对象

int main(int argc, char** argv){
     AILog.SetConsoleOutput(false);//控制是否控制台也同步输出

    log.TraceInfo("Ready Go");//打印一行日志试一试


    system("pause");
    return 0;
}

 

posted @ 2019-11-25 09:54  huluBrother  阅读(266)  评论(0)    收藏  举报