基于Linux C的简单日志库

博客介绍了作者为嵌入式开发项目创建的日志库alog,该库小巧简单,仅依赖头文件,支持设置日志输出级别、保存路径、文件大小限制及数量限制。关键功能包括文件命名按时间顺序、循环覆盖旧日志。源代码展示了alog_printf的核心实现,包括日志文件的管理和内容写入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        最近由于接手了几个小的开发项目,一直苦于寻找一个调用简单,方便调试,占用资源小的日志库,因为我大部分的嵌入式开发项目都是C/C++的开发。就想着做一个小的日志调用接口C/C++库。最近有时间整理一下。

        alog 由此而生,目前我几个项目中都用到了这个库,个人觉得非常简单,容易理解,使用简单,能马上融入到小型嵌入式项目中去。

首先看调用示例:

#include "alog_printf.h"

int main(void)
{
    alog_printf(ALOG_LVL_DEBUG, TURE,"\n************TEST START**************\n");
    int a = 0;

    while (1)
    {
        alog_printf(ALOG_LVL_DEBUG, TURE, "test a=%d \n", a);
        a++;
        sleep(1);
    }

    return 0;
}

        示例中可以看到,这个日志调用类似于打印函数(其实就是改造的),说是一个日志库文件,不如说就是一个头文件,(这里主要是为了简单-不用去引用其他东西)是不是很简单。

调用原型:alog_printf(level, outputEnable, ...);

首先这个日志库具备这几个功能:

  • 可以设置输出到指定路径下保存成文件-方便导出查看
  • 日志文件以创建时间先后命名
  • 日志文件的大小和个数可设置,当超出设置限制后根据创建时间循环覆盖
  • 日志的保存等级可设置
  • 可控制是否打印在终端
  • 可输出打印时间、调用函数名和行数,方便定位打印

附一段核心实现:

/**
     * @brief :  Ari日志打印
     * @param {unsigned char} level
     * @param {_BOOL} outputEnable
     * @param {const char} *func
     * @param {const long} line
     * @param {const char} *fmt
     * @return {*}
     * @author: LR
     * @Date: 2021-06-25 12:11:49
     */
    static void log_printf(unsigned char level, bool outputEnable, const char *file, const long line, const char *fmt, ...)
    {
        if (level < ALOG_LVL_SET)
            return;

        pthread_mutex_lock(&alogMutex); /* 日志部分上锁 */

        char printf_buf[ALOG_BUF_MAX_SIZE]={0};
        va_list args;
        int printed;
        va_start(args, fmt);
        printed = vsnprintf(printf_buf, ALOG_BUF_MAX_SIZE, fmt, args);
        va_end(args);

        char buf[ALOG_BUF_MAX_SIZE + 200] = {0};
        int g_Count = 0;

        char temp[256];
        char logFileName[50] = {0};
        char logFileNameTemp[50] = {0};
        char logFileNameLast[50] = {"1580-05-05_00-00-00.log"};
        char logFileNameMix[50] = {"3000-05-05_00-00-00.log"};

        int iMax = ALOG_MAX_SIZE;

        FILE *fp = NULL;
        time_t timep;
        struct tm *p;
        time(&timep);
        p = localtime(&timep); //获取当前系统时间

        //将要保存的日志信息和时间戳信息整合
        memset(buf, 0, sizeof(buf));
        sprintf(buf, "▶[%d-%02d-%02d %02d:%02d:%02d -> %s:%ld]  :  ", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, (p->tm_hour + 8) % 24, p->tm_min, p->tm_sec, file, line); //星期p->tm_wday
        strcat(buf, printf_buf);
        //strcat(buf, "\0");

        if (outputEnable == true)
            puts(buf);

        DIR *dir;
        struct dirent *ptr;
        if ((dir = opendir(ALOG_PATH)) == NULL) //打开日志的文件目录,如果没有则建立
        {
            sprintf(temp, "mkdir -p %s", ALOG_PATH);
            system(temp);
            dir = opendir(ALOG_PATH);
        }
        while ((ptr = readdir(dir)) != NULL) //循环读取当前目录下到所有文件
        {
            if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
                continue;
            strcpy(logFileNameTemp, ptr->d_name);
            if (strlen(logFileNameTemp) < 18)
            {
                //g_Count++; //统计符合要求的文件个数
                continue;
            }
            if (strcmp(logFileNameTemp, logFileNameLast) > 0) //找到最新的文件
            {
                strcpy(logFileNameLast, logFileNameTemp); //将最新日期的文件名字保存在logFileNameLast
            }
            if (strcmp(logFileNameTemp, logFileNameMix) < 0) //找到最老的文件
            {
                strcpy(logFileNameMix, logFileNameTemp); //将最老日期的文件名字保存在logFileNameMix
            }
            g_Count++; //统计符合要求的文件个数
        }
        closedir(dir);
        sprintf(logFileName, "%s%s", ALOG_PATH, logFileNameLast); //将最新的文件名字匹配到路径中去,如果没有用默认文件名

        fp = fopen(logFileName, "r+");
        if (fp == NULL)
        {
            //进入这个里面证明没有日志文件产生过,第一次创建+
            sprintf(logFileName, "%s%d-%02d-%02d_%02d-%02d-%02d.log", ALOG_PATH, (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, (p->tm_hour + 8) % 24, p->tm_min, p->tm_sec);
            fp = fopen(logFileName, "w+");
        }
        else
        {
            fseek(fp, 0, 2);       //SEEK_END值为2
            if (ftell(fp) >= iMax) //如果大小已经超出限制
            {
                fclose(fp);
                if (g_Count >= ALOG_MAX_NUM) //如果日志文件的个数达到路限制10个,则按日期进行循环覆盖
                {
                    sprintf(logFileName, "%s%s", ALOG_PATH, logFileNameMix);
                    remove(logFileName); //删除最老的一个日志文件
                    sprintf(logFileName, "%s%d-%02d-%02d_%02d-%02d-%02d.log", ALOG_PATH, (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, (p->tm_hour + 8) % 24, p->tm_min, p->tm_sec);
                }
                else
                {
                    sprintf(logFileName, "%s%d-%02d-%02d_%02d-%02d-%02d.log", ALOG_PATH, (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, (p->tm_hour + 8) % 24, p->tm_min, p->tm_sec);
                }
                fp = fopen(logFileName, "w+");
            }
            else
            {
                fclose(fp);
                fp = fopen(logFileName, "a+"); //以追加的方式打开文件
            }
        }
        fwrite(buf, 1, strlen(buf), fp);
        fflush(fp);        //立即刷新缓存区到指定文件流中
        //fsync(fileno(fp)); //将缓存区数据写入磁盘,并等待操作结束
        //fdatasync(fileno(fp));
        fclose(fp);

        pthread_mutex_unlock(&alogMutex); /*写日志部分解锁 */
    }

        这里面最关键的,个人认为根据设定来把时间段内的日志发片。然后根据时间来循环的覆盖日志,这样在我们有限的嵌入式资源中就能把握日志的占用大小。不用人为的去维护,在有需要的时候下载下来,方便记录调试定位问题。

        当然这个只是项目需要写的一个东西,里面我现在想到的就还有很多可以优化增加的功能。现在只是记录一下,拿来用还是没问题的。大家有想法的也可以给我留言。

完整的实现+示例请下载:

优快云:alog_printf.h-C文档类资源-优快云下载

GitHub:GitHub - HelloAriLiu/alog: 一个简单的Linux 日志库适用于小型嵌入式


创作不易,您的点赞、评论、收藏是对我最大的支持!

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值