[从0到1搭嵌入式工程]日志系统的实现

嵌入式开发的log输出,一种是直接输出到标准输出,通过串口可以看到, 一种是把日志存在内存文件中, 在一般嵌入式开发中, 这两者是并存的。

标准输出通过 printf()函数, 或者通过vfprintf(stdout, format, args)输出到标准输出。

对于放在文件中的日志,因为日志的频繁性, 需要单独的线程或进程对文件进行读写, 同时对log文件进行大小限制、循环覆盖等管理。

下面记录一种实现:

创建一个log server进程,负责收集其他模块发过来的log输出。

消息结构:

{ unsigned int Pid;//模块进程ID

char logMsg[1020];//日志本身}


sockfd = socket()  创建一个socket;

bind(sockfd, “/tmp/socket_log”), 把 这个socket与 /tmp/socket_log 进行绑定;

open("/tmp/log.log" “w+”), 打开我们要把日志写入的文件;

while(1)

    recvfrom(sockfd), 一直监听sockfd。 根据 Pid, 执行"ps | grep -w PID | grep -v grep | awk '{print $5}'", 得到进程名。 为了减少每次都通过这种方式获取进程名, 创建一个数组, 把已经获取到进程名的PID 和 PNAME放进去, 下次通过PID先从数组中找, 对于进程的PID发生变化的情况, 在数组中查找通过命令获取到的PNAME是否已经存在,如果存在,更新PID值, 如果不存在, 写入数组。记录当前数组元素个数。

    这个时候,我们有PNAME, 再通过接口获取到时间信息, 组织格式,写入log.log, 再把logMSG写入,再写入"\n" ,调用flush, 写入完成后,判断totalsize=writensize+这次写入的size, 如果大于 我们设定的极限值,比如 1MB, 就调用seek_set 0, 把文件写指针指到文件的开头位置。

这样,就完成了log server 进程的实现, 总的来说是,从socket接收各模块的msg,封装一下格式,再写入到文件中,并记录当前文件的大小,如果太大,就seek到文件开头。

    再看log client端:

client统一函数format_log(_F_, _FU_, _L_, "HELLO %s", "world"); 文件,函数名,行号,消息体和格式化参数。

函数原型 format_log(char* file, char* func,int line, char* format, ...) 属于不定长参数的函数;

va_list args;

va_start(args,  format); 通过这个函数,就能获取到, format后面的参数列表;

vsprintf(logMsg, format, args);

va_end(args); 通过这种方式, 全部的log信息就会被传送到logMsg这个buff中,通过getpid()获取到当前进程的PID, 就可以使用sendto(client_socket,  MSG_DONTWAIT)把log信息发送到 log server了。

注意,函数format_log函数是多进程复用的,内存不共享,每个进程都需要有自己的 cllient_socket。

在发送消息成功以后 , 仍然调用标准输出, 把log信息发送到标准输出上去。

va_start(args);

vfprintf(stdout, format, args);

va_end(args);

printf("\n");

这样,在log.log中写入的log,也就会同时输出到串口了。


对于每一个模块的log分级, 每一个模块设置一个log级别,如ERROR, 在打印时判断level参数是否高于设置的log级别即可。

如何在串口中打出颜色:

RED "\033[0;32;31m"    NONE "\033[m" 

#define log_red(format, args...)  printf(RED"%s,%d "fmt NONE"\N", __func__, __LINE__, ##args)

就可以在串口中输出为红色日志。

猜你喜欢

转载自blog.csdn.net/bingyu880101/article/details/80224782