专栏首页C/C++基础C风格简易本地log系统

C风格简易本地log系统

1.系统简介

该C风格简易log日志系统,适合与Linux平台系统,主要用于格式化输出日志到本地指定的文件中,可指定log文件数目、最大大小、行数、按时间切换等功能,可满足基本的log日志功能。从项目中提炼出来,附上使用的demo,简单易懂,能快速上手。具体接口说明,参见源码。

源文件也可以到github下载,地址:CEasyLocalLog

2.源码

2.1头文件localLog.h

#ifndef _LOCALLOG_H_
#define _LOCALLOG_H_

#include <stdio.h>
#include <time.h>

typedef struct
{
    FILE *pLogFile;
    char sBaseFileName[80]; //log文件名前缀
    char sLogFileName[80];  //log文件名
    int iShiftType;         //0 -> 大小,  1 -> 文件数, 2 -> shift by interval, 3 ->天, 4 -> 小时, 5 ->分钟
    int iMaxLogNum;         //log文件最大个数
    int lMaxSize;           //单个log文件最大大小,单位Byte
    int lMaxCount;          //单个log文件最大行数,单位line
    int lLogCount;          //当前log文件行数,单位line
    time_t lLastShiftTime;  //上一次切换时间,单位s
}OI_LogFile;

// @brief   OI_InitLogFile: 初始化log
// @param   pstLogFile: log结构指针
// @param   sLogBaseName: Log文件名的前缀字符串
// @param   iShiftType: log文件切换类型,
//      0->shift by size, when size > iMAX bytes
//      1->shift by LogCount, when logcount > iMAX lines
//      2->shift by interval, when elapse seconds > iMAX sec.
//      3->shift by day, 
//      4->shift by hour 
//      5->shift by minute
//      6->shift by day, log filename append with date.
//
// @param   iMaxLogNum: 切换log文件的最大个数. 当iShiftType=6时无意义。
// @param   iMAX: 如果iShiftType为0/1/2,那么表示当前log文件单位到达iMAX时进行切换,否则无意义。
// @return  成功返回0,失败 < 0
int OI_InitLogFile(OI_LogFile * pstLogFile, char *sLogBaseName, int iShiftType, int iMaxLogNum, int iMAX);

//
//@brief:   Log string to logfile.
//@param:   pstLogFile:log结构指针
//          iLogTime:0 = do not log time, 1 = log simple time, 2 = log detail time  
//          sFormat:C string pointed by format to the logfile,format may include format specifiers
//          ...:可变参数
//@return:  <0 means failure
//@ps:__attribute__ ((format (printf, 3, 4))):提示编译器,对这个函数的调用需要像printf一样,用对应的format字符串来check可变参数的数据类型。3和4对应函数第3和第4个形参
int OI_Log(OI_LogFile * pstLogFile, int iLogTime, const char *sFormat, ...) __attribute__ ((format (printf, 3, 4)));

#endif

2.2源文件localLog.cpp

#include "localLog.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>

#include <string.h>
#include <errno.h>
#include <stdarg.h>

//
//获取时间相关函数
//
char * OI_GetShortDateStr(const time_t * mytime)
{
    static char s[50];
    struct tm curr = *localtime(mytime);

    if(curr.tm_year > 50)
        snprintf(s, sizeof(s), "%04d%02d%02d", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday);
    else
        snprintf(s, sizeof(s), "%04d%02d%02d", curr.tm_year + 2000, curr.tm_mon + 1, curr.tm_mday);

    return s;
}

static char* OI_GetDateTimeStr(const time_t * mytime)
{
    static char s[50];
    struct tm curr = *localtime(mytime);

    if(curr.tm_year > 50)
        snprintf(s, sizeof(s), "%04d-%02d-%02d %02d:%02d:%02d", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec);
    else
        snprintf(s, sizeof(s), "%04d-%02d-%02d %02d:%02d:%02d", curr.tm_year + 2000, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec);

    return s;
}

static char * OI_GetCurShortDateStr(void)
{
    time_t mytime = time(NULL);
    return OI_GetShortDateStr(&mytime);
}

static char * OI_GetCurDateTimeStr(void)
{
    time_t mytime = time(NULL);
    return OI_GetDateTimeStr(&mytime);
}


static int ShiftFiles(OI_LogFile * pstLogFile)
{
    struct stat stStat;
    char sLogFileName[300];
    char sNewLogFileName[300];
    int i;
    struct tm stLogTm, stShiftTm;

    if (pstLogFile->iShiftType == 6)
        return 0;

    snprintf(sLogFileName,sizeof(sLogFileName),"%s.log", pstLogFile->sBaseFileName);

    if(stat(sLogFileName, &stStat) < 0) 
    {
        if (errno == ENOENT) {
            FILE *pfile;
            if ((pfile = fopen(sLogFileName, "a+")) == NULL) 
                return -1;
            fclose(pfile);
            if (stat(sLogFileName, &stStat) < 0)
                return -1;
        } else {
            return -1;
        }
    }
    switch (pstLogFile->iShiftType)
    {
        case 0:
            if(stStat.st_size < pstLogFile->lMaxSize)
                return 0;
            break;
        case 2:
            if(stStat.st_mtime - pstLogFile->lLastShiftTime < pstLogFile->lMaxCount)
                return 0;
            break;
        case 3:
            if(pstLogFile->lLastShiftTime - stStat.st_mtime > 86400)
                break;
            memcpy(&stLogTm, localtime(&stStat.st_mtime), sizeof(stLogTm));
            memcpy(&stShiftTm, localtime(&pstLogFile->lLastShiftTime), sizeof(stShiftTm));
            if(stLogTm.tm_mday == stShiftTm.tm_mday)
                return 0;
            break;
        case 4:
            if(pstLogFile->lLastShiftTime - stStat.st_mtime > 3600)
                break;
            memcpy(&stLogTm, localtime(&stStat.st_mtime), sizeof(stLogTm));
            memcpy(&stShiftTm, localtime(&pstLogFile->lLastShiftTime), sizeof(stShiftTm));
            if(stLogTm.tm_hour == stShiftTm.tm_hour)
                return 0;
            break;
        case 5:
            if(pstLogFile->lLastShiftTime - stStat.st_mtime > 60)
                break;
            memcpy(&stLogTm, localtime(&stStat.st_mtime), sizeof(stLogTm));
            memcpy(&stShiftTm, localtime(&pstLogFile->lLastShiftTime), sizeof(stShiftTm));
            if(stLogTm.tm_min == stShiftTm.tm_min)
                return 0;
            break;
        default:
            if(pstLogFile->lLogCount < pstLogFile->lMaxCount)
                return 0;
            pstLogFile->lLogCount = 0;
    }

    for(i = pstLogFile->iMaxLogNum - 2; i >= 0; i--)
    {
        if(i == 0)
            snprintf(sLogFileName,sizeof(sLogFileName),"%s.log", pstLogFile->sBaseFileName);
        else
            snprintf(sLogFileName,sizeof(sLogFileName),"%s%d.log", pstLogFile->sBaseFileName, i);

        if(access(sLogFileName, F_OK) == 0)
        {
            snprintf(sNewLogFileName,sizeof(sNewLogFileName),"%s%d.log", pstLogFile->sBaseFileName, i + 1);
            if(rename(sLogFileName, sNewLogFileName) < 0)
            {
                return -1;
            }
        }
    }
    time(&pstLogFile->lLastShiftTime);
    return 0;
}

//@brief:初始化LOG对象
int OI_InitLogFile(OI_LogFile* pstLogFile, char *sLogBaseName, int iShiftType, int iMaxLogNum, int iMAX)
{
    memset(pstLogFile, 0, sizeof(OI_LogFile));
    strncat(pstLogFile->sLogFileName, sLogBaseName, sizeof(pstLogFile->sLogFileName) - 10);
    strcat(pstLogFile->sLogFileName, ".log");

    strncpy(pstLogFile->sBaseFileName, sLogBaseName, sizeof(pstLogFile->sBaseFileName) - 15);
    pstLogFile->iShiftType = iShiftType;
    pstLogFile->iMaxLogNum = iMaxLogNum;
    pstLogFile->lMaxSize = iMAX;
    pstLogFile->lMaxCount = iMAX;
    pstLogFile->lLogCount = iMAX;
    time(&pstLogFile->lLastShiftTime);

    return ShiftFiles(pstLogFile);
}

int OI_Log(OI_LogFile * pstLogFile, int iLogTime,const char *sFormat, ...)
{
    va_list ap;
    struct timeval stLogTv;

    if (pstLogFile->iShiftType == 6) {
        snprintf(pstLogFile->sLogFileName, sizeof(pstLogFile->sLogFileName), "%s_%s.log", 
            pstLogFile->sBaseFileName, OI_GetCurShortDateStr());
    }

    if((pstLogFile->pLogFile = fopen(pstLogFile->sLogFileName, "a+")) == NULL)
        return -1;
    va_start(ap, sFormat);
    if(iLogTime == 1)
    {
        fprintf(pstLogFile->pLogFile, "[%s] ", OI_GetCurDateTimeStr());
    }
    else if(iLogTime == 2)
    {
        gettimeofday(&stLogTv, NULL);
        fprintf(pstLogFile->pLogFile, "[%s.%.6d] ", 
            OI_GetDateTimeStr((const time_t *) &(stLogTv.tv_sec)), (int) stLogTv.tv_usec);
    }
    vfprintf(pstLogFile->pLogFile, sFormat, ap);
    fprintf(pstLogFile->pLogFile, "\n");
    va_end(ap);
    pstLogFile->lLogCount++;
    fclose(pstLogFile->pLogFile);
    return ShiftFiles(pstLogFile);
}

2.3使用示例demo.cpp

/*
*@brief:C风格简易log系统使用demo
*@author:dablelv,1589276509@qq.com
*@date:20171207
*/

#include "localLog.h"

//定义三个级别LOG
OI_LogFile stErrorLog;      //错误日志
OI_LogFile stDebugLog;      //调试日志
OI_LogFile stEventLog;      //流水日志

//宏定义调用log函数
//ps:_args_...:GCC支持的可变参数名;"##对符号进行连接,如果_args_为空,则不进行连接,使函数宏可省略可变参数
#define ErrorLog(_fmt_, _args_...) do{ OI_Log(&stErrorLog, 2, _fmt_, ##_args_); } while (0)
#define DebugLog(_fmt_, _args_...) do{ OI_Log(&stDebugLog, 2, _fmt_, ##_args_); } while (0)
#define EventLog(_fmt_, _args_...) do{ OI_Log(&stEventLog, 2, _fmt_, ##_args_); } while (0)

int main(int argc,char* argv[])
{
    //初始化logFile
    //保留最多10个log文件,每个最大大小约为100MB
    OI_InitLogFile(&stErrorLog,"/home/dablelv/test/logSys/log/error",0,10,100000000);
    OI_InitLogFile(&stDebugLog,"/home/dablelv/test/logSys/log/debug",0,10,100000000);
    OI_InitLogFile(&stEventLog,"/home/dablelv/test/logSys/log/event",0,10,100000000);

    //向log文件写日志
    ErrorLog("this is error log");
    ErrorLog("this is error log whith %s","argument");
    DebugLog("this is debug log");
    DebugLog("this is debug log whith %s","argument");
    EventLog("this is event log");
    EventLog("this is event log whith %s","argument");
}

3.C++风格的简易log系统

除了上面C风格的log系统,还有一款C++风格的log系统可供使用,参见本人的另一篇博文:C++实现简易log日志系统

后续将总结提炼出一款远程log系统分享给大家,支持将日志输出到指定的远程主机。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++实现简易log日志系统

    在软件开发周期中,不管是前台还是后台,系统一般会采用一个持久化的日志系统来记录运行情况。

    Dabelv
  • RTTI简介

    RTTI(Runtime Type Identification)是“运行时类型识别”的意思。面向对象的编程语言,像C++,Java,Delphi都提供了对RT...

    Dabelv
  • RTTI简介

    RTTI是Runtime Type Identification的缩写,是“运行时类型识别”的意思。面向对象的编程语言,象C++,Java,Delphi都提供了...

    Dabelv
  • Nginx日志配置

    众所周知,线上如果出现事故我们通常都是查看日志去进行问题定位并且进行修复。使用好Nginx日志有利于我们线上进行修复异常问题。在Nginx中日志主要分为两种:...

    逆月翎
  • 【MySQL (六) | 详细分析MySQL事务日志redo log】

    为了最大程度避免数据写入时 IO 瓶颈带来的性能问题,MySQL 采用了这样一种缓存机制:

    周三不加班
  • Serverless实践系列(十):全新命令行工具帮你快速部署云函数

    SCF CLI 是腾讯云云函数(Serverless Cloud Function,SCF)产品的命令行工具,想必很多小伙伴已经有所了解,或者试用过了。作为一...

    腾讯云serverless团队
  • 使用SCVMM跨集群迁移虚拟机失败(2904)

      最近为客户部署了一个测试Hyper-V集群,其希望从原来的Hyper-V集群中将某些测试的虚拟机迁移到新部署的测试集群中,两个集群采用的Hyper-V版本均...

    SuperDream
  • VMware虚拟机NAT模式上网设置

    很多初学者不知道如何正确设置,VMware虚拟机与主机互联,在NAT模式下实现上网。这里详细介绍其设置过程,讲解详细而易懂实用,希望能帮到需要的人。

    DataScience
  • 面向亿万级用户的QQ一般做什么?——兴趣部落的Web同构直出分享

    所以可以把直出定义为:“以node作为后端语言实现的服务端渲染并输出HTML字符串到客户端的一项技术”。这样浏览器渲染首屏的过程就由非直出下的先请求HTML,再...

    WeTest质量开放平台团队
  • 面向亿万级用户的QQ一般做什么?——兴趣部落的 Web 同构直出分享

    本文的目的在于解决两个问题:1、 部落是怎样从一个纯前端项目改造成同构直出项目的。2、在访问量这么大的情况下,如何保证直出服务的可用性的问题。

    WeTest质量开放平台团队

扫码关注云+社区

领取腾讯云代金券