日志打印 for C

支持功能:

1.日志等级

2.日志文件路径配置

3.日志名称配置

4.日志保留个数

5.单个日志文件最大大小配置

6.日志是否写入文件配置

7.日志配置多模块共用

头文件printLog.h

#ifndef __PRINTLOG_H__
#define __PRINTLOG_H__


#define LOGMAXLEN 10240 //打印字符串的长度

#define LOGDEBUG __FILE__, __LINE__, 4
#define LOGINFO __FILE__, __LINE__, 3
#define LOGSYS __FILE__, __LINE__, 2
#define LOGWARN __FILE__, __LINE__, 1
#define LOGERROR __FILE__, __LINE__, 0
#define LOGNOLINE __FILE__, __LINE__, -1    //不打印前缀

/*
    日志初始化
    logCfgPath - 日志配置文件路径
    module - 模块名, 没有可为NULL, 日志模块名将默认为default
    成功返回0 失败-1
*/
int logInit(const char *logCfgPath, const char *module);

//打印日志
void printLog(const char *filename, int line, int lv, const char *arg, ...);

#endif

C文件

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <stdarg.h>

#include "cJSON.h"
#include "printLog.h"

/*日志配置文件结构体*/
typedef struct
{
    char log_path[100]; //日志路径
    char log_name[100]; //日志名称
    int logfile_num;    //日志文件个数
    int logfile_size;   //单日志最大大小(MB)
    int log_level;      //日志级别
    int logfile_open;   //日志文件是否打开1-开
} logCfg;

logCfg logcfg_st; //日志配置
int loginit = 0;  //日志初始化标志
char LOGCFGPATH[256];   //LOG配置文件路径
//默认日志配置
char default_config[] = "{"
                        "\n\t\"default\": {"
                        "\n\t\t\"log_path\":\"./\","
                        "\n\t\t\"log_name\":\"default\","
                        "\n\t\t\"logfile_num\":1,"
                        "\n\t\t\"logfile_size\":1,"
                        "\n\t\t\"log_level\":4,"
                        "\n\t\t\"logfile_open\":1"
                        "\n\t}"
                        "\n}";

/*读文件返回文本内容的指针,用完需要释放(日志模块使用)*/
static char *get_logcfg_text(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (!fp)
    {
        if (errno == 2)
        {
            fprintf(stderr, "fopen error: %m \n");
            return 0x00;
        }
        else
        {
            fprintf(stderr, "fopen error: %m \n");
        }
        return NULL;
    }

    //将文件指针移到文件结尾处
    if (fseek(fp, 0, SEEK_END))
    {
        fprintf(stderr, "fseek() error: %m\n");
        fclose(fp);
        return NULL;
    }
    //返回文件指针的当前位置,(字节数),失败<0
    long len = ftell(fp);
    if (len < 0)
    {
        fprintf(stderr, "ftell() error: %m\n");
        fclose(fp);
        return NULL;
    }
    //将文件指针移到文件开头处
    if (fseek(fp, 0, SEEK_SET))
    {
        fprintf(stderr, "fseek() error: %m\n");
        fclose(fp);
        return NULL;
    }
    //申请空间保存配置文件内容
    char *text_ptr = malloc(len + 1);
    if (text_ptr == NULL)
    {
        fprintf(stderr, "malloc error: %m\n");
        fclose(fp);
        return NULL;
    }
    //每次读取1个字节,一共读取len次
    fread(text_ptr, 1, len, fp);

    fclose(fp);
    return text_ptr;
}

int logInit(const char *logCfgPath, const char *module)
{
    int ret = 0;
    char *text_ptr = NULL;
    cJSON *root = NULL;
    loginit = 1;
    memset(&logcfg_st, 0, sizeof(logcfg_st));
    memset(LOGCFGPATH, 0, sizeof(LOGCFGPATH));
    if (strlen(logCfgPath) > sizeof(LOGCFGPATH) - 1)
    {
        fprintf(stderr, "Error : logCfgPath len is too long(max size:%ld)\n",sizeof(LOGCFGPATH));
        return -1;
    }
    strcpy(LOGCFGPATH, logCfgPath);
    
    char module_name[128];
    memset(module_name, 0, sizeof(module_name));
    if (module)
    {
        strcpy(module_name, module);
    }
    else
    {
        strcpy(module_name, "default");
    }

    //配置默认值
    strcpy(logcfg_st.log_path, "./");
    strcpy(logcfg_st.log_name, "default");
    logcfg_st.logfile_num = 1;
    logcfg_st.logfile_size = 1;
    logcfg_st.log_level = 4;
    logcfg_st.logfile_open = 1;

    if (access(LOGCFGPATH, F_OK) == -1) //日志配置文件不存在,自动生成
    {
        fprintf(stdout, "Warn : log cfg file not exist! auto create\n");
        FILE *fp = fopen(LOGCFGPATH, "a+");
        if (fp == NULL)
        {
            printf("fopen error : %m\n");
            return -1;
        }
        fprintf(fp, "%s", default_config);
        fclose(fp);
    }
    // else //文件存在读取日志配置文件
    {
        text_ptr = get_logcfg_text(LOGCFGPATH);
        if (text_ptr == NULL)
        {
            fprintf(stderr, "read log cfg file failed: %m\n");
            return -1;
        }
        //解析json
        root = cJSON_Parse(text_ptr);
        if (!root)
        {
            fprintf(stderr, "Error before: [%s]\n", cJSON_GetErrorPtr());
            ret = -1;
            goto end;
        }
        cJSON *mod = cJSON_GetObjectItem(root, module_name);
        if (!mod)
        {
            fprintf(stderr, "Warn : module[%s] not exist, use default\n",module_name);
            mod = cJSON_GetObjectItem(root, "default");
        }
        
        cJSON *item = cJSON_GetObjectItem(mod, "log_path");
        if (!item)
        {
            fprintf(stderr, "x log_path failed, default [%s]\n", logcfg_st.log_path);
        }
        else
        {
            strncpy(logcfg_st.log_path, item->valuestring, sizeof(logcfg_st.log_path));
        }
        item = cJSON_GetObjectItem(mod, "log_name");
        if (!item)
        {
            fprintf(stderr, "x log_name failed, default [%s]\n", logcfg_st.log_name);
        }
        else
        {
            strncpy(logcfg_st.log_name, item->valuestring, sizeof(logcfg_st.log_name));
        }
        item = cJSON_GetObjectItem(mod, "logfile_num");
        if (!item)
        {
            fprintf(stderr, "x logfile_num failed, default [%d]\n", logcfg_st.logfile_num);
        }
        else
        {
            logcfg_st.logfile_num = item->valueint;
        }
        item = cJSON_GetObjectItem(mod, "logfile_size");
        if (!item)
        {
            fprintf(stderr, "x logfile_size failed, default [%d]\n", logcfg_st.logfile_size);
        }
        else
        {
            logcfg_st.logfile_size = item->valueint;
        }
        item = cJSON_GetObjectItem(mod, "log_level");
        if (!item)
        {
            fprintf(stderr, "x log_level failed, default [%d]\n", logcfg_st.log_level);
        }
        else
        {
            logcfg_st.log_level = item->valueint;
        }
        item = cJSON_GetObjectItem(mod, "logfile_open");
        if (!item)
        {
            fprintf(stderr, "x logfile_open failed, default [%d]\n", logcfg_st.logfile_open);
        }
        else
        {
            logcfg_st.logfile_open = item->valueint;
        }
    }

    fprintf(stdout, "log_path: %s\n", logcfg_st.log_path);
    fprintf(stdout, "log_name: %s\n", logcfg_st.log_name);
    fprintf(stdout, "logfile_num: %d\n", logcfg_st.logfile_num);
    fprintf(stdout, "logfile_size: %d\n", logcfg_st.logfile_size);
    fprintf(stdout, "log_level: %d\n", logcfg_st.log_level);
    fprintf(stdout, "logfile_open: %d\n", logcfg_st.logfile_open);

end:
    if (text_ptr)
    {
        free(text_ptr);
    }
    if (root)
    {
        cJSON_Delete(root);
    }
    return ret;
}

//日志
void printLog(const char *filename, int line, int lv, const char *arg, ...)
{
    if (!loginit)
        return;
    if (lv > logcfg_st.log_level)
    {
        return;
    }
    char lv_str[8];
    memset(lv_str, 0, sizeof(lv_str));
    switch (lv)
    {
    case 0:
        strcpy(lv_str, "ERROR");
        break;
    case 1:
        strcpy(lv_str, "WARN");
        break;
    case 2:
        strcpy(lv_str, "SYSTEM");
        break;
    case 3:
        strcpy(lv_str, "INFO");
        break;
    case 4:
        strcpy(lv_str, "DEBUG");
        break;

    default:
        break;
    }
    FILE *fp = NULL;
    if (logcfg_st.logfile_open)
    {
        char logfile[256];
        memset(logfile, 0, sizeof(logfile));
        sprintf(logfile, "%s/%s.log", logcfg_st.log_path, logcfg_st.log_name);
        if (access(logcfg_st.log_path, F_OK) == -1)
        {
            fprintf(stderr, "path [%s] not exist!\n", logcfg_st.log_path);
            return;
        }
        if (access(logfile, F_OK) == 0) //日志文件存在
        {
            //获取文件大小
            struct stat statbuf;
            int rc = stat(logfile, &statbuf);
            if (rc < 0)
            {
                fprintf(stderr, "sys error : %m\n");
                return;
            }
            //判断文件大小,替换文件
            if (statbuf.st_size >= logcfg_st.logfile_size * 1024 * 1024) //超过大小
            {
                char logbakname[256];
                for (int i = logcfg_st.logfile_num; i > 0; i--)
                {
                    memset(logbakname, 0, sizeof(logbakname));
                    sprintf(logbakname, "%s/%s%d.log", logcfg_st.log_path, logcfg_st.log_name, i);
                    int isexist = access(logbakname, F_OK);
                    if (isexist == 0 && i == logcfg_st.logfile_num) //文件存在,且是最后一个文件,删除
                    {
                        unlink(logbakname);
                    }
                    else if (isexist == 0) //文件存在,不是最后一个,重命名
                    {
                        char logrename[256];
                        memset(logrename, 0, sizeof(logrename));
                        sprintf(logrename, "%s/%s%d.log", logcfg_st.log_path, logcfg_st.log_name, i + 1);
                        rename(logbakname, logrename);
                    }
                }
                memset(logbakname, 0, sizeof(logbakname));
                sprintf(logbakname, "%s/%s1.log", logcfg_st.log_path, logcfg_st.log_name);
                rename(logfile, logbakname);
            }
        }

        fp = fopen(logfile, "a+");
        if (fp == NULL)
        {
            printf("fopen error : %m\n");
            return;
        }
    }

    char str[LOGMAXLEN];
    time_t timer;
    time(&timer);
    struct tm *stm = localtime(&timer);
    struct timeval tv;
    // struct timezone tz;
    gettimeofday(&tv, NULL); //取毫秒

    char sdate[10];
    strftime(sdate, 10, "%Y%m%d", stm); //取日期
    char stime_date[80];
    strftime(stime_date, 80, "%G%m%d %T:", stm); //取详细时间

    va_list va;
    va_start(va, arg);
    vsprintf(str, arg, va);
    // vsnprintf(str,1000,arg,va);

    if (lv == -1)
    {
        /* 不打印前缀 */
        printf("%s", str);
        if (logcfg_st.logfile_open)
            fprintf(fp, "%s", str);
    }
    else
    {
        //[20210126 09:14:25:118] [DEBUG] [test.c:50] ..........test
        printf("[%s%ld] [%s] [%s:%d] %s\n", stime_date, tv.tv_usec / 1000, lv_str, filename, line, str);
        if (logcfg_st.logfile_open)
            fprintf(fp, "[%s%ld] [%s] [%s:%d] %s\n", stime_date, tv.tv_usec / 1000, lv_str, filename, line, str);
    }

    if (logcfg_st.logfile_open)
        fclose(fp);
    va_end(va);
    return;
}

Makefile

.PHONY: clean
# CC = arm-linux-gcc -std=gnu99
CC = gcc
RM = rm -f

OBJS = printLog.o cJSON.o
DYNAMIC = libprintlog.so
STATIC = libprintlog.a

all: so a

%.o: %.c
	$(CC) -fpic -c $^

so:$(OBJS)
	$(CC) -shared -o $(DYNAMIC) $(OBJS)
	$(RM) $(OBJS)

a:
	$(CC) -c printLog.c -o printLog.o
	$(CC) -c cJSON.c -o cJSON.o
	ar -crv $(STATIC) $(OBJS)
	$(RM) $(OBJS)

clean:
	$(RM) $(DYNAMIC) $(STATIC)

日志配置文件(JSON)

{
    "default": {        // 模块名,可以多个模块公用一个日志配置文件,未找到模块默认使用default
        "log_path":"./",        // 日志文件路径
        "log_name":"default",   // 日志名称 default.log
        "logfile_num":1,        // 日志文件保留个数 如2 日志文件有xxx.log xxx1.log xxx2.log
        "logfile_size":1,       // 每个日志文件大小(M)
        "log_level":4,          // 日志等级
        "logfile_open":1        // 是否写入文件 1-是 0-否
    },
    "test": {
        "log_path":"./",
        "log_name":"test",
        "logfile_num":1,
        "logfile_size":1,
        "log_level":4,
        "logfile_open":1
    }
}

使用cJSON解析配置文件,cjson源码

GitHub - DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值