log

log.h
#ifndef __LOG_H__
#define __LOG_H__
#include <stdio.h>
#include <pthread.h>

namespace log
{

#define LOG_MAX_LENGTH 4096
#define FLUSH_LINE 2

#define EMERG 8   //
#define ALERT 7   //
#define CRIT  6   //
#define WARNING 5 //
#define ERR   4   //错误
#define NOTICE 3  //注意信息
#define INFO  2   //普通信息
#define DEBUG 1   //调试信息
#define NONE  0   //

static const char g_alertStr[128] = "[Alert]:" ;//比warning级别要高,表示系统可能处于异常运行状态,如连接数据库失败,无法正常向数据库进行操作等
static const char g_errorStr[128] = "[Error]:" ;//可以确定是程序上bug,需开发人员检查程序逻辑
static const char g_warningStr[128] = "[Warning]:" ;//虽不是服务器端错误,但需要高度关注
static const char g_infoStr[128] = "[Info]:" ;//一些有用信息,它比普通信息要略高,但可能没有Warning那么严重
static const char g_messageStr[128] = "[Message]:" ;//普通信息
static const char g_sqlStr[128] = "[Sql]:";//数据库操作语句

#ifndef TIME_CHN_FMT
#define TIME_CHN_FMT "%Y-%m-%d %H:%M:%S"
#endif

/*
    time_t转时间字符串
*/
char* timeToString(time_t tmTime, const char *fmt,char *strTime, int len);

class CLog
{
public:
    //the singleton
    static CLog* getInstance();

    //初始化
    int init(const char* path,const char* fileName, short level=DEBUG,unsigned int logFileMaxNum=50,unsigned int logFileMaxSize=10*1024*1024);

    //关闭
    int close();

    //写日志
    void plog(short level, const char *fmt, ...);
private:
    CLog();

    virtual ~CLog();

    //检测文件大小是否超过上限
    bool fileSizeOverflow(const char *fileName);

    //创建新文件
    void createNewFile();
private:
    FILE *mFile;

    //日志文件路径
    char mFilePath[1024];

    //日志缓冲区
    char mLogBuf[LOG_MAX_LENGTH];

    //lock
    pthread_mutex_t mWriteLogLock;

    //上一条日志重复次数
    int  mRepeatCount;

    //当前日志文件大小
    unsigned int mCurrLogFileSize;

    //日志文件大小上限
    unsigned int mLogFileMaxSize;

    //当前的日志级别
    int mLogLevel;

    //是否已关毕
    bool mClosed;

    //日志的最大文件数
    unsigned int mLogFileMaxNum;

    //日志的当前文件数
    unsigned int mLogFileCurrNum;
};

}//end of namespace log

#endif



log.cpp
#include "log.h"
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <string.h>

using std::cout;
using std::endl;

namespace log
{

/*
    时间转字符串
*/
char* timeToString(time_t tmTime, const char *fmt,char *strTime, int len)
{
    struct tm gmTime;
    localtime_r(&tmTime, &gmTime);
    strftime(strTime, (size_t) len, fmt, &gmTime);
    return strTime;
}

/*
    the singleton
*/
CLog* CLog::getInstance()
{
    static CLog log;
    return &log;
}

CLog::CLog()
{
    mFile = NULL;
    mRepeatCount = 0;
    mCurrLogFileSize = 0;
    mLogFileMaxSize = 0;
    mLogFileMaxNum = 0;
    memset(mLogBuf, 0, sizeof(mLogBuf));
    mClosed = false;
    mLogFileCurrNum = 0;
}

CLog::~CLog()
{
    close();
}

/*
    关闭
*/
int CLog::close()
{
    if(mClosed){
        return -1;
    }
    if (mFile)
    {
        fflush(mFile);
        fclose(mFile);
    }

    pthread_mutex_destroy(&mWriteLogLock);
    mClosed = true;
    return 0;
}

/*
    初始化
*/
int CLog::init(const char *path, const char *fileName, short level, unsigned int logFileMaxNum, unsigned int logFileMaxSize)
{
    if (path == NULL || fileName == NULL || logFileMaxNum<1 || level < NONE || logFileMaxSize<1){
        return -1;
    }

    if(pthread_mutex_init(&mWriteLogLock, NULL)!=0)
    {
        pthread_mutex_destroy(&mWriteLogLock);
        return -1;
    }

    mLogFileMaxNum = logFileMaxNum;
    mLogFileMaxSize = logFileMaxSize;

    if(access(path,F_OK)<0)
    {
#ifdef _WIN32
        mkdir(path);
#else
        mkdir(path,0777);
#endif
    }

    char fileFullName[256];
    memset(fileFullName,0,sizeof(fileFullName));
    strcpy(fileFullName,path);
    strcat(fileFullName,fileName);

    memset(mFilePath, 0, sizeof(mFilePath));
    strncpy(mFilePath, fileFullName, sizeof(mFilePath)-1);

    //备份老日志,创建新日志。
    if(fileSizeOverflow(fileFullName)){
        createNewFile();
    }

    mFile = fopen(fileFullName, "a+");
    if (mFile == NULL){
        return -1;
    }

    mLogLevel = level;
    fprintf(mFile, "\n\n\n\n\n");
    fprintf(mFile, "--------------------------------------------------------\n");
    fprintf(mFile, "                   日志建立日期: %s %s\n", __DATE__, __TIME__);
    fprintf(mFile, "--------------------------------------------------------\n");
    fprintf(mFile, "\n");
    fflush(mFile);
    return 0;
}

/*
    写日志
*/
void CLog::plog(short level, const char *fmt, ...)
{
    //与配置文件中的日志级别对比,选择输出日志
    if (0 == mLogLevel || level < mLogLevel){
        return;
    }
    if (NULL == mFile)
    {
        mFile = fopen(mFilePath, "a+");
        if (NULL == mFile){
            return;
        }
        mCurrLogFileSize = 0;
    }
    pthread_mutex_lock(&mWriteLogLock);
    static int lastBufLen = 0;
    static char lastBuf[LOG_MAX_LENGTH];
    static int flushCount = 0;

    time_t t = time(NULL);
    char timeStr[64];
    memset(timeStr,0,sizeof(timeStr));
    timeToString(t, TIME_CHN_FMT, timeStr, sizeof(timeStr)-1);

    char printf_buf[LOG_MAX_LENGTH];
    memset(printf_buf,0,sizeof(printf_buf));
    va_list args;
    va_start(args, fmt);
    vsprintf(printf_buf, fmt, args);
    va_end(args);

    int len = strlen(printf_buf) + 1;
    if (len == lastBufLen)
    {
        if (strcmp(lastBuf, printf_buf) == 0)
        {
            ++mRepeatCount;
            pthread_mutex_unlock(&mWriteLogLock);
            return;
        }
    }
    else
    {
        if (mRepeatCount > 0){
            mCurrLogFileSize += fprintf(mFile, "[%s]: 上一条日志重复%u次。\n", timeStr, mRepeatCount);
        }

        lastBufLen = len;
        mRepeatCount = 0;
        memcpy(lastBuf, printf_buf, len);
    }

    mCurrLogFileSize += fprintf(mFile, "[%s] ", timeStr);
    mCurrLogFileSize += fprintf(mFile, "%s", printf_buf);

    if (INFO < level)
    {
        fflush(mFile);
    }else if (++flushCount >= FLUSH_LINE)
    {
        flushCount = 0;
        fflush(mFile);
    }

    if (mCurrLogFileSize > mLogFileMaxSize)
    {
        createNewFile();
        mFile = fopen(mFilePath, "a+");
        if (mFile)
        {
            mCurrLogFileSize = 0;
            timeToString(t, TIME_CHN_FMT, timeStr, sizeof(timeStr)-1);
            mCurrLogFileSize += fprintf(mFile, "[%s]: ", timeStr);
            mCurrLogFileSize += fprintf(mFile, "%s", printf_buf);
        }
    }
    //如果是调试模式,则会将日志输出在终端上
    if(mLogLevel==DEBUG){
        cout << printf_buf << endl;
    }
    pthread_mutex_unlock(&mWriteLogLock);
}

/*
    重命名旧文件,创建新文件
*/
void CLog::createNewFile()
{
    char newFullName[1024];
    memset(newFullName,0,sizeof(newFullName));

    //如果是第一次则先遍历一次
    if(mLogFileCurrNum==0)
    {
        for(unsigned int i=1;i<=mLogFileMaxNum;i++)
        {
            snprintf(newFullName,sizeof(newFullName), "%s.%02d", mFilePath, i);
            //不存在,则创建
             if((access(newFullName,F_OK))<0)
             {
                 mLogFileCurrNum = i;
                 break;
             }
            //如果文件存在,则继续找,如果找到最后也存在则删除第一个
            if(i==mLogFileMaxNum)
            {
                snprintf(newFullName,sizeof(newFullName), "%s.%02d", mFilePath, 1);
                unlink(newFullName);
                mLogFileCurrNum = 1;
            }
        }
    }else
    {
        mLogFileCurrNum++;
        if(mLogFileCurrNum>mLogFileMaxNum){
            mLogFileCurrNum = 1;
        }
        snprintf(newFullName,sizeof(newFullName), "%s.%02d", mFilePath, mLogFileCurrNum);
        //已存在,则删除
         if((access(newFullName,F_OK))>=0){
             unlink(newFullName);
         }
         fclose(mFile);
         mFile = NULL;
    }

    if (0 != rename(mFilePath, newFullName)){
        return;
    }

    int ret = creat(mFilePath, 0664);
    ::close(ret);
}

/*
    检测文件大小是否超过上限
*/
bool CLog::fileSizeOverflow(const char *fileName)
{
    if (NULL == fileName){
        return false;
    }
    char path[1024];
    memset(path,0,sizeof(path));
    strncpy(path, fileName, sizeof(path)-1);
    path[sizeof(path)-1] = '\0';

    FILE *fp = fopen(path, "r");
    if (fp == NULL){
        return false;
    }
    int ret = fseek(fp, 0, SEEK_END);
    if (0 != ret)
    {
        fclose(fp);
        return false;
    }
    mCurrLogFileSize = ftell(fp);
    if (mCurrLogFileSize < mLogFileMaxSize)
    {
        fclose(fp);
        return false;
    }
    mCurrLogFileSize = 0;
    fclose(fp);
    return true;
}

}//end of namespace log




main
#include "log.h"
#include <unistd.h>
using namespace log;

int main()
{
    CLog::getInstance()->init("/usr/local/code/MyLog_debug/","test.log"/*,1,10,1024*1024*/);
    int i = 0;
    while(true)
    {
        i++;
        CLog::getInstance()->plog(NOTICE,"%s h:%d\n",g_messageStr,i);
        usleep(1000);
    }
}

你可能感兴趣的:(log)