嵌入式设备log日志的开发设计思想

废话不多说,下面是我的设计思路
在这里插入图片描述
接下来我会根据此设计方案一一描述

1.划分等级
这里我划分了五个等级,如下:

#define LOG_LEVEL_ERROR        1
#define LOG_LEVEL_WARN         2
#define LOG_LEVEL_INFO         3
#define LOG_LEVEL_DEBUG        4
#define LOG_LEVEL_VERBOSE      5

设置默认的等级:

#define LOG_DEFAULT_LEVEL    LOG_LEVEL_INFO

2.重命名
为了让日志函数名更具有可读写

#define XXXX_LOGE(mode, fmt, ...)  log_printf(LOG_LEVEL_ERROR, mode, fmt, ##__VA_ARGS__)
#define XXXX_LOGW(mode, fmt, ...)  log_printf(LOG_LEVEL_WARN,  mode, fmt, ##__VA_ARGS__)
#define XXXX_LOGI(mode, fmt, ...)  log_printf(LOG_LEVEL_INFO,  mode, fmt, ##__VA_ARGS__)
#define XXXX_LOGD(mode, fmt, ...)  log_printf(LOG_LEVEL_DEBUG, mode, fmt, ##__VA_ARGS__)
#define XXXX_LOGV(mode, fmt, ...)  log_printf(LOG_LEVEL_VERBOSE, mode, fmt, ##__VA_ARGS__)

这里的xxxx可以换成公司的名称缩写,让其更具有特性

3.默认日志等级
程序中会设置默认的等级,但是还会提供一个配置文件,这个配置文件可以存放很多的相关东西,比如我们当前在里面写入日志的等级,表示接下来程序运行以这个等级来进行日志的记录 LOG_LEVEL = LOG_LEVEL_INFO,
当然这需要在各个程序记录日志前先操作,这样就可以避免调试过程中因没有日志的问题(需要在程序中详细添加日志,对于频繁且不太重要的日志将其等级放高,比如设置debug等级或者更高,平时运行的时候不不打印,一旦出现问题需要排查的时候,只需要更改一下配置文件里的等级,然后重启设备即可)避免要重新编译版本在运行而带来的开发效率低下的问题

4.当前模块化设计
也就是只是提供一套api接口给到每个模块自行调用,如果考虑到每个模块因为写日志而带来的性能问题,可以设计成一个单独的日志进程或者线程来专门干这样的事情
进程IPC的方式:可以采用socket进程之间的通信模式来设计,创建logservice,然后其他进程IPCA 、B来通过socket接口来连接logservice,连接之后,就可以通过进程之间通信的方式来处理,对应的A、B进程只管把需要记录的日志发送给logservice,logservice来专门把收到的数据记录下来,这样做的另一种好处在于,进程间相处独立,不会因为某一个小问题因为日志进程死亡而导致其他的业务进程受到干扰
线程Thread方式:可以采用queue+signal的方式,线程之间通信我最能想到的就是共享内存模式了,由队列queue+信号量signal来实现操作,目的也一样是让业务线程从写日志带来的性能问题中解放出来

5.嵌入式设备的内存有限,如何在有限的空间里记录更多的日志
1)我们在设备上开发者时常要想的是,如果不需要考虑对应的内存大小,所有的开发调试环境都是无穷大的内存该多好,实际上当然不可能了啊
所以我们需要考虑的事情就是要限制日志文件的总大小,你可以先用linux命令df看一下你当前存放地的文件夹磁盘分区是多大,预留一些其他的空间之后就可以当作总大小来设置了
2)为了避免单个日志文件过大,可以设置单个日志文件大小为10M,超过这个大小之后,就重新新建一个日志文件,日志文件名采取递增的方式,如下:

uint8_t log_size_diagnosis(const char *path, int len)
{
    //1.判断logservice大小,是否有10M,如果超过则新建logservice文件,之前的顺序递增
    if ((get_file_size(path) + len) > LOG_FILE_MAX_SIZE)
    {
        char achWorkPath[128] = {0};
        char *root = getenv("root");
        snprintf(achWorkPath, sizeof(achWorkPath), "%s/logs/", root);

        char achCmd[128] = {0};
        snprintf(achCmd, sizeof(achCmd), "cd %s; ls logservice* | sed -n '$p' > tmp.log ", achWorkPath);
        system(achCmd);

        printf("achCmd:%s\n", achCmd);
        char achFilePath[128] = {0};
        snprintf(achFilePath, sizeof(achFilePath), "%stmp.log", achWorkPath);
        printf("achFilePath:%s\n", achFilePath);

        FILE* fp = fopen(achFilePath, "r");
        if(fp == NULL)
        {
            return 0;
        }

        char logservice[32] = {0};
        fread(logservice, 1, 32, fp);
        fclose(fp);

        printf("logservice:%s\n", logservice);

        char tmp[4] = {0};
        //这里做的是字符串截取操作
        function_substring(logservice, tmp, utils_strlen("logservice"), utils_strlen("logservice") + 2);
        int num = atoi(tmp);
        printf("tmp:%s, num:%d, pfile_name:%s\n", tmp, num, pfile_name);

        if( num == 99)
        {
            snprintf(achCmd, sizeof(achCmd), "rm %s%s", achWorkPath, logservice);
            system(achCmd);
            num = num - 1;
        }

        int index1;
        for(index1 = num; index1 > 0; index1--)
        {
            char achCmd[128] = {0};
            snprintf(achCmd, sizeof(achCmd), "mv %s%s%02d%s %s%s%02d%s ", 
                    achWorkPath, "logservice", index1, ".log", achWorkPath, "logservice", index1 + 1, ".log");
            system(achCmd);
        }

		//文件递增之后,要把文件指针重新只想logservice01.log上
        log_file_deinit();
        log_file_init(pfile_name);
    }
}

如果想记录更多的日志,可以考虑在日志文件一定的情况进行压缩操作,这里不展开了

3)写日志无返避免会频繁操作文件流跟刷新缓冲区的操作,这里其实是很消耗cpu的性能的,从操作系统上来说IO操作非常耗性能,所以在功能可用之后可以对这方面在做一些优化,降低操作文件流跟刷新缓冲区的操作频率

注意点:如果多进程/线程之间的操作,就必须要考虑多个程序同时打开文件写入数据的情况,这种就必须要引入临界值加以控制了,关于这个问题目前还暂没有深入思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值