在嵌入式底层应用开发中,日志功能是非常重要的。今天我要介绍一个开源的log.c代码,它的简洁程度使得它可以直接使用。
log.c是什么?
log.c是一个用于C语言的日志功能模块,你可以在以下链接找到它:log.c GitHub仓库。
简单来说,log.c是一个轻量级的日志库,它的设计和使用都非常简单。只需包含log.h头文件,并按照指定的方式调用相关的日志函数,就可以实现日志的记录和输出。它支持不同级别的日志信息,可以方便地进行日志的过滤和定制。这个模块具备开箱即用的特性,使得嵌入式底层应用开发中的日志功能变得更加简单和便捷。
![image-20231104212208362](https://pic.imgdb.cn/item/654645c6c458853aefa128c5.png)
点击查看大图
log.c 的几个特点:
代码简洁,就一个 .c 和 .h 文件,一共 200 行。
设计优雅,打印日志的 API 只有 1 个。
提供了将 log 输入到不同目标的接口,例如输入到文件。
提供了实现线程安全的接口。
log.c 怎么用?
打印日志的 API:
log_trace(const char *fmt, ...);
log_debug(const char *fmt, ...);
log_info(const char *fmt, ...);
log_warn(const char *fmt, ...);
log_error(const char *fmt, ...);
log_fatal(const char *fmt, ...);
它们都是对 log_log() 的简单封装,用法和 printf() 一样。
示例:
下面的例子会将日志同时输出到标准输出和文件中。
#include "log.h"
int main(int argc, char *argv[])
{
log_set_level(0);
log_set_quiet(0);
FILE *fp1, *fp2;
fp1 = fopen("./log_info.txt", "ab");
if(fp1 == NULL)
return -1;
fp2 = fopen("./log_debug.txt", "ab");
if(fp2 == NULL)
return -1;
log_add_fp(fp1, LOG_INFO);
log_add_fp(fp2, LOG_DEBUG);
log_debug("debug");
log_info("info");
log_warn("warn");
fclose(fp2);
fclose(fp1);
return 0;
}
运行:
$ ./example1
23:31:05 DEBUG example1.c:20: debug
23:31:05 INFO example1.c:21: info
23:31:05 WARN example1.c:22: warn
$ cat log_debug.txt
2022-05-08 23:31:05 DEBUG example1.c:20: debug
2022-05-08 23:31:05 INFO example1.c:21: info
2022-05-08 23:31:05 WARN example1.c:22: warn
$ cat log_info.txt
2022-05-08 23:31:05 INFO example1.c:21: info
2022-05-08 23:31:05 WARN example1.c:22: warn
关于线程安全:
log.c 代码虽然少,但是仍然考虑了线程安全,下面是用法示例。
#include "log.h"
pthread_mutex_t MUTEX_LOG;
void log_lock(bool lock, void *udata);
int main()
{
log_set_level(0);
log_set_quiet(0);
pthread_mutex_init(&MUTEX_LOG, NULL);
log_set_lock(log_lock, &MUTEX_LOG);
/* Insert threaded application code here... */
log_info("I'm threadsafe");
pthread_mutex_destroy(&MUTEX_LOG);
return 0;
}
void log_lock(bool lock, void* udata)
{
pthread_mutex_t *LOCK = (pthread_mutex_t*)(udata);
if (lock)
pthread_mutex_lock(LOCK);
else
pthread_mutex_unlock(LOCK);
}
log.c 的内部实现?
私有数据结构:
![image-20231104212214030](https://pic.imgdb.cn/item/654645c6c458853aefa128cc.png)
点击查看大图
全局变量 L 维护了 log.c 所需要的所有信息。
void *udata 用于保存用户数据,用户可以将其用作任意用途。
lock 是一个函数指针:
typedef void (*log_LockFn)(bool lock, void *udata);
用户可以用它来指定自己想用的锁机制,例如 Pthread 的互斥量。
int level 用于保存当前的 log 等级,等级大于 level 的 log 才会被输出到标准输出。
bool quiet 用于打开、关闭 log 输出。
数组 callbacks 用于保存多种输出方式,目前仅支持输出到标准输出和文件,有需要的话我们还可以将其扩展成输出到 syslog、网络等,每增加一种输出方式就是构造一个 Callback,成员回调函数 log_LogFn 负责真正地 log 输出功能:
typedef void (*log_LogFn)(log_Event *ev);
公共数据结构:
![image-20231104212219142](https://pic.imgdb.cn/item/654645c6c458853aefa12823.png)
点击查看大图
一条 log 信息对应一个 log_Event。暴露这个数据结构是为了用户可以编写自己的 log 打印函数 log_LogFn 以输出 log。
公共的 API:
整个 log.c 其实只提供了一个打印相关的 API:log_log()。log_trace() 等宏只是对 log_log() 的简单封装,这种简洁地设计无论是对库的用户还是对库的开发者而言,都是最幸福的事情。
剩下的几个 API 用于控制和功能扩展。
log_log() 的实现思路:
1> 根据用于提供的 log 信息构造 1个 log_Event。
2> 将 log 信息输出到标准输出。
3> 遍历所有 log Callback,逐一调用它们的打印函数 log_LogFn。
总结
log.c 代码优雅、设计简洁、功能实用,这对库的用户和库的开发者而言,都是一种幸福。
如果你的项目需要一个简单好用的日志功能,可以考虑集成开箱即用的 log.c。
**其他相关的*日志*开源项目:
https://github.com/armink/EasyLogger
https://github.com/HardySimpson/zlog
https://github.com/0xmalloc/c-log
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !
![137e00002230ad9f26e78-265x300](https://pic.imgdb.cn/item/654643a7c458853aef969d9e.png)