diff --git a/README.md b/README.md index 77374f2..8ba38cf 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ # EasyLogger +标签(空格分隔): EasyLogger + --- # 1. 介绍 -[EasyLogger](https://github.com/armink/EasyLogger)是一款超轻量级(ROM<1.6K, RAM<0.3K)、高性能的C日志库,非常适合对资源敏感的软件项目,例如:IoT产品、可穿戴设备、智能家居等等。相比log4c、zlog这些知名的C日志库,EasyLogger的功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件形式进行动态扩展。 +[EasyLogger](https://github.com/armink/EasyLogger) 是一款超轻量级(ROM<1.6K, RAM<0.3K)、高性能的 C/C++ 日志库,非常适合对资源敏感的软件项目,例如: IoT 产品、可穿戴设备、智能家居等等。相比 log4c、zlog 这些知名的 C/C++ 日志库, EasyLogger 的功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件形式进行动态扩展。 ## 1.1 主要特性 - 支持用户自定义输出方式(例如:终端、文件、数据库、串口、485、Flash...); - 日志内容可包含级别、时间戳、线程信息、进程信息等; -- 日志输出被设计为线程安全的方式; +- 日志输出被设计为线程安全的方式,并支持 **异步输出** 及 **缓冲输出** 模式; - 支持多种操作系统([RT-Thread](http://www.rt-thread.org/)、UCOS、Linux、Windows...),也支持裸机平台; - 日志支持 **RAW格式** ; - 支持按 **标签** 、 **级别** 、 **关键词** 进行动态过滤; @@ -23,19 +25,19 @@ ## 1.2 插件 -- 1、Flash:使用[EasyFlash](https://github.com/armink/EasyFlash)库提供的Flash操作接口,可以把日志直接存储在Flash中。 +- 1、Flash:使用 [EasyFlash](https://github.com/armink/EasyFlash) 库提供的Flash操作接口,无需文件系统,直接将日志存储在 Flash 中。 - 2、File(正在开发):支持文件转档、软件运行时动态加载配置文件等与文件日志输出相关功能。 - 3、敬请期待…… ## 1.3 Star & Fork -后续我还会提供更多插件。也非常欢迎大家设计、开发更多实用插件和功能,一起来完善EasyLogger **([Github](https://github.com/armink/EasyLogger)|[OSChina](http://git.oschina.net/armink/EasyLogger)|[Coding](https://coding.net/u/armink/p/EasyLogger/git))** 。如果觉得这个开源项目很赞,可以点击[项目主页](https://github.com/armink/EasyLogger) 右上角的**Star**,同时把它推荐给更多有需要的朋友。 +后续我还会提供更多插件。也非常欢迎大家设计、开发更多实用插件和功能,一起来完善 EasyLogger **([Github](https://github.com/armink/EasyLogger)|[OSChina](http://git.oschina.net/armink/EasyLogger)|[Coding](https://coding.net/u/armink/p/EasyLogger/git))** 。如果觉得这个开源项目很赞,可以点击[项目主页](https://github.com/armink/EasyLogger) 右上角的 **Star** ,同时把它推荐给更多有需要的朋友。 # 2. 使用 ### 2.1 参数配置 -EasyLogger拥有过滤方式、输出格式、输出开关这些属性。 +EasyLogger 拥有过滤方式、输出格式、输出开关这些属性。 - 过滤方式支持按照标签、级别、关键词进行过滤; - 可以动态的开启/关闭日志的输出; @@ -45,7 +47,7 @@ EasyLogger拥有过滤方式、输出格式、输出开关这些属性。 ### 2.2 输出级别 -参考Android Logcat,级别最高为0(Assert),最低为5(Verbose)。 +参考 Android Logcat ,级别最高为 0(Assert) ,最低为 5(Verbose) 。 ``` 0.[A]:断言(Assert) @@ -56,7 +58,7 @@ EasyLogger拥有过滤方式、输出格式、输出开关这些属性。 5.[V]:详细(Verbose) ``` -#### 2.2.1 设置不同的颜色及字体风格 +#### 2.2.1 输出缤纷多彩的日志 各个级别日志默认颜色效果如下。用户也可以根据自己的喜好,在 `elog_cfg.h` 对各个级别日志的颜色及字体风格进行单独设置。 @@ -83,20 +85,20 @@ EasyLogger拥有过滤方式、输出格式、输出开关这些属性。 ### 2.6.1 核心功能 -下图为在终端中输入命令来控制日志的输出及过滤器的设置,更加直观的展示了EasyLogger核心功能。 +下图为在终端中输入命令来控制日志的输出及过滤器的设置,更加直观的展示了 EasyLogger 核心功能。 -- Demo路径:[`\demo\os\rt-thread\stm32f10x\`](https://github.com/armink/EasyLogger/tree/master/demo/os/rt-thread/stm32f10x) -- API文档:[`\docs\zh\api\kernel.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/api/kernel.md) +- Demo 路径:[`\demo\os\rt-thread\stm32f10x\`](https://github.com/armink/EasyLogger/tree/master/demo/os/rt-thread/stm32f10x) +- API 文档:[`\docs\zh\api\kernel.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/api/kernel.md) - 移植文档:[`\docs\zh\port\kernel.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/port/kernel.md) ![easylogger](https://raw.githubusercontent.com/armink/EasyLogger/master/docs/zh/images/EasyLoggerDemo.gif) -### 2.6.2 Flash Log(将日志保存到Flash中) +### 2.6.2 Flash Log(将日志保存到 Flash 中) -下图过程为通过控制台输出日志,并将输出的日志存储到Flash中。重启再读取上次保存的日志,最后清空Flash日志。 +下图过程为通过控制台输出日志,并将输出的日志存储到 Flash 中。重启再读取上次保存的日志,最后清空 Flash 日志。 -- Demo路径:[`\demo\os\rt-thread\stm32f10x\`](https://github.com/armink/EasyLogger/tree/master/demo/os/rt-thread/stm32f10x) -- API文档:[`\docs\zh\api\flash.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/api/flash.md) +- Demo 路径:[`\demo\os\rt-thread\stm32f10x\`](https://github.com/armink/EasyLogger/tree/master/demo/os/rt-thread/stm32f10x) +- API 文档:[`\docs\zh\api\flash.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/api/flash.md) - 移植文档:[`\docs\zh\port\flash.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/port/flash.md) ![FlashLog](https://raw.githubusercontent.com/armink/EasyLogger/master/docs/zh/images/LogDemo.gif) @@ -111,7 +113,7 @@ EasyLogger拥有过滤方式、输出格式、输出开关这些属性。 - 2、配置文件:文件系统下的配置文件; - 3、文件转档:文件系统下支持文件按容量转档,按时间区分; - 4、日志助手:开发跨平台的日志助手,兼容Linux、Windows、Mac系统,打开助手即可查看、过滤(支持正则表达式)、排序、保存日志等。前端:[HTML5](https://zh.wikipedia.org/wiki/HTML5) + [Bootstrap](https://github.com/twbs/bootstrap) + [AngularJS](https://angularjs.org/) + [NW.js](http://www.oschina.net/p/nwjs),后端:[Rust](https://github.com/rust-lang/rust) + [iron](https://github.com/iron/iron) + [rust-websocket](https://github.com/cyderize/rust-websocket) + [serial-rs](https://github.com/dcuddeback/serial-rs); -- 5、异步输出:目前日志输出与用户代码之间是同步的方式,这种方式虽然软件简单,也不存在日志覆盖的问题。但在输出速度较低的平台下,会由于增加日志功能,而降低软件运行速度。所以后期会增加 **异步输出** 方式,关键字过滤也可以放到异步输出中去; +- 5、~~异步输出:目前日志输出与用户代码之间是同步的方式,这种方式虽然软件简单,也不存在日志覆盖的问题。但在输出速度较低的平台下,会由于增加日志功能,而降低软件运行速度。所以后期会增加 **异步输出** 方式,关键字过滤也可以放到异步输出中去;~~ - 6、Arduino:增加Arduino lib,并提供其Demo; # 5. 许可 diff --git a/demo/non_os/stm32f10x/components/easylogger/inc/elog_cfg.h b/demo/non_os/stm32f10x/components/easylogger/inc/elog_cfg.h index 6155e4d..c89b671 100644 --- a/demo/non_os/stm32f10x/components/easylogger/inc/elog_cfg.h +++ b/demo/non_os/stm32f10x/components/easylogger/inc/elog_cfg.h @@ -35,8 +35,8 @@ #define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE /* enable assert check */ #define ELOG_ASSERT_ENABLE -/* log buffer size */ -#define ELOG_BUF_SIZE 256 +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 256 /* output line number max length */ #define ELOG_LINE_NUM_MAX_LEN 5 /* output filter's tag max length */ diff --git a/demo/os/linux/easylogger/inc/elog_cfg.h b/demo/os/linux/easylogger/inc/elog_cfg.h index 31c140d..5e97773 100644 --- a/demo/os/linux/easylogger/inc/elog_cfg.h +++ b/demo/os/linux/easylogger/inc/elog_cfg.h @@ -35,8 +35,8 @@ #define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE /* enable assert check */ #define ELOG_ASSERT_ENABLE -/* log buffer size */ -#define ELOG_BUF_SIZE 512 +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 512 /* output line number max length */ #define ELOG_LINE_NUM_MAX_LEN 5 /* output filter's tag max length */ diff --git a/demo/os/rt-thread/stm32f10x/components/easylogger/inc/elog_cfg.h b/demo/os/rt-thread/stm32f10x/components/easylogger/inc/elog_cfg.h index 6155e4d..c89b671 100644 --- a/demo/os/rt-thread/stm32f10x/components/easylogger/inc/elog_cfg.h +++ b/demo/os/rt-thread/stm32f10x/components/easylogger/inc/elog_cfg.h @@ -35,8 +35,8 @@ #define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE /* enable assert check */ #define ELOG_ASSERT_ENABLE -/* log buffer size */ -#define ELOG_BUF_SIZE 256 +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 256 /* output line number max length */ #define ELOG_LINE_NUM_MAX_LEN 5 /* output filter's tag max length */ diff --git a/demo/os/windows/easylogger/inc/elog_cfg.h b/demo/os/windows/easylogger/inc/elog_cfg.h index 31c140d..5e97773 100644 --- a/demo/os/windows/easylogger/inc/elog_cfg.h +++ b/demo/os/windows/easylogger/inc/elog_cfg.h @@ -35,8 +35,8 @@ #define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE /* enable assert check */ #define ELOG_ASSERT_ENABLE -/* log buffer size */ -#define ELOG_BUF_SIZE 512 +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 512 /* output line number max length */ #define ELOG_LINE_NUM_MAX_LEN 5 /* output filter's tag max length */ diff --git a/docs/zh/api/kernel.md b/docs/zh/api/kernel.md index 79024f5..7425f0f 100644 --- a/docs/zh/api/kernel.md +++ b/docs/zh/api/kernel.md @@ -68,17 +68,36 @@ void elog_start(void) **建议**:对于每个文件或者每个模块,可以重新覆盖定义上述日志输出宏定义,如下所示。这样的优点就是降低代码书写量,统一日志书写格式,代码可以做到尽可能少的依赖某个日志库,同时部分复用代码无需修改日志输出方法,可直接拷贝至其他模块或者其他项目,提高软件的可重用性。 ```c -//WiFi协议处理 -#define log_d(...) elog_d("wifi.proto", __VA_ARGS__) -#define log_w(...) elog_w("wifi.proto", __VA_ARGS__) -#define log_e(...) elog_e("wifi.proto", __VA_ARGS__) - -//WiFi数据打包处理 -#define log_d(...) elog_d("wifi.package", __VA_ARGS__) - -//CAN命令解析 -#define log_d(...) elog_d("can.disp", __VA_ARGS__) -#define log_e(...) elog_e("can.disp", __VA_ARGS__) +//WiFi协议处理(/wifi/proto.c) +#define LOG_TAG "wifi.proto" +#define log_e(...) elog_e(LOG_TAG, __VA_ARGS__) +#define log_w(...) elog_w(LOG_TAG, __VA_ARGS__) +#define log_i(...) elog_i(LOG_TAG, __VA_ARGS__) + +#if WIFI_DEBUG + #define log_d(...) elog_d(LOG_TAG, __VA_ARGS__) +#else + #define log_d(...) +#endif + +//WiFi数据打包处理(/wifi/package.c) +#define LOG_TAG "wifi.package" + +#if WIFI_DEBUG + #define log_d(...) elog_d(LOG_TAG, __VA_ARGS__) +#else + #define log_d(...) +#endif + +//CAN命令解析(/can/disp.c) +#define LOG_TAG "can.disp" +#define log_e(...) elog_e(LOG_TAG, __VA_ARGS__) + +#if CAN_DEBUG + #define log_d(...) elog_d(LOG_TAG, __VA_ARGS__) +#else + #define log_d(...) +#endif ``` ### 1.4 断言 @@ -257,6 +276,27 @@ void elog_set_text_color_enabled(bool enabled) |:----- |:----| |enabled |true: 使能,false: 失能| +### 1.16 将缓冲区中的日志全部输出 + +在缓冲输出模式下,执行此方法可以将缓冲区中的日志全部输出干净。 + +``` +void elog_flush(void) +``` + +### 1.17 日志输出接口 + +在异步输出模式下,如果用户没有启动 pthread 库,此时需要启用额外线程来实现日志的异步输出功能。使用此方法即可获取到异步输出缓冲区中的指定长度的日志。如果设定日志长度小于日志缓冲区中已存在日志长度,将只会返回已存在日志长度。 + +```C +size_t elog_async_get_log(char *log, size_t size) +``` + +|参数 |描述| +|:----- |:----| +|log |取出的日志内容| +|size |待取出的日志大小| + ## 2、配置 参照 《EasyLogger 移植说明》([`\docs\zh\port\kernel.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/port/kernel.md))中的 `设置参数` 章节 diff --git a/docs/zh/port/kernel.md b/docs/zh/port/kernel.md index f0d5504..e5b1511 100644 --- a/docs/zh/port/kernel.md +++ b/docs/zh/port/kernel.md @@ -19,6 +19,8 @@ |源文件 |描述 | |:------------------------------ |:----- | |\easylogger\src\elog.c |核心功能源码| +|\easylogger\src\elog_async.c |核心功能异步输出模式源码| +|\easylogger\src\elog_buf.c |核心功能缓冲输出模式源码| |\easylogger\src\elog_utils.c |EasyLogger常用小工具| |\easylogger\port\elog_port.c |不同平台下的EasyLogger移植接口| |\easylogger\plugins\ |插件源码目录| @@ -30,7 +32,7 @@ - 2、将`\easylogger\`(里面包含`inc`、`src`及`port`的那个)文件夹拷贝到项目中; -- 3、添加`\easylogger\src\elog.c`、`\easylogger\src\elog_utils.c`及`\easylogger\port\elog_port.c`这些文件到项目的编译路径中; +- 3、添加`\easylogger\src\elog.c`、`\easylogger\src\elog_utils.c`及`\easylogger\port\elog_port.c`这些文件到项目的编译路径中(elog_async.c 及 elog_buf.c 视情况选择性添加); - 4、添加`\easylogger\inc\`文件夹到编译的头文件目录列表中; ## 3、移植接口 @@ -114,7 +116,6 @@ const char *elog_port_get_t_info(void) 开启后,日志才会被输出。如果关闭,所有日志输出代码都将会被替换为空。 -- 默认状态:开启 - 操作方法:开启、关闭`ELOG_OUTPUT_ENABLE`宏即可 ### 4.2 输出级别 @@ -139,14 +140,13 @@ const char *elog_port_get_t_info(void) 开启后,将会启动断言检查功能。如果关闭,所有断言检查代码都将会被替换为空。 -- 默认状态:开启 - 操作方法:开启、关闭`ELOG_ASSERT_ENABLE`宏即可 -### 4.4 缓冲区大小 +### 4.4 每行日志的缓冲区大小 -缓冲区大小决定了日志一行最多输出多少字符,单位:byte。 +该配置决定了日志一行最多输出多少字符,单位:byte。 -- 操作方法:修改`ELOG_BUF_SIZE`宏对应值即可 +- 操作方法:修改`ELOG_LINE_BUF_SIZE`宏对应值即可 ### 4.5 行号最大长度 @@ -186,6 +186,34 @@ const char *elog_port_get_t_info(void) - 操作方法:增加并修改`ELOG_COLOR_VERBOSE`宏对应值即可,其他级别日志颜色的修改以此类推 +### 4.10 异步输出模式 + +开启异步输出模式后,将会提升用户应用程序的执行效率。应用程序在进行日志输出时,无需等待日志彻底输出完成,即可直接返回。 + +- 操作方法:开启、关闭`ELOG_ASYNC_OUTPUT_ENABLE`宏即可 + +#### 4.10.1 异步输出模式缓冲区大小 + +- 默认大小:`(ELOG_LINE_BUF_SIZE * 10)` ,不定义此宏,将会自动按照默认值设置 +- 操作方法:修改`ELOG_ASYNC_OUTPUT_BUF_SIZE`宏对应值即可 + +#### 4.10.2 启用 pthread 库 + +异步输出模式默认是使用 POSIX 的 pthread 库来实现,用户的平台如果支持 pthread ,则可以开启此宏。对于一些缺少 pthread 的支持平台,可以关闭此宏,参考 `elog_async.c` 中关于日志异步输出线程的实现方式,自己动手实现此功能。 + +- 操作方法:开启、关闭`ELOG_ASYNC_OUTPUT_USING_PTHREAD`宏即可 + +### 4.11 缓冲输出模式 + +开启缓冲输出模式后,如果缓冲区不满,用户线程在进行日志输出时,无需等待日志彻底输出完成,即可直接返回。但当日志缓冲区满以后,将会占用用户线程,自动将缓冲区中的日志全部输出干净。同时用户也可以在非日志输出线程,通过定时等机制使用 `void elog_flush(void)` 将缓冲区中的日志输出干净。 + +- 操作方法:开启、关闭`ELOG_BUFF_OUTPUT_ENABLE`宏即可 + +#### 4.11.1 缓冲输出模式缓冲区大小 + +- 默认大小:`(ELOG_LINE_BUF_SIZE * 10)` ,不定义此宏,将会自动按照默认值设置 +- 操作方法:修改`ELOG_BUF_OUTPUT_BUF_SIZE`宏对应值即可 + ## 5、测试验证 如果`\demo\`文件夹下有与项目平台一致的Demo,则直接编译运行,观察测试结果即可。无需关注下面的步骤。 diff --git a/easylogger/inc/elog.h b/easylogger/inc/elog.h index 32e269d..1dcf5ba 100644 --- a/easylogger/inc/elog.h +++ b/easylogger/inc/elog.h @@ -38,30 +38,6 @@ extern "C" { #endif -#if !defined(ELOG_OUTPUT_LVL) - #error "Please configure static output log level (in elog_cfg.h)" -#endif - -#if !defined(ELOG_LINE_NUM_MAX_LEN) - #error "Please configure output line number max length (in elog_cfg.h)" -#endif - -#if !defined(ELOG_BUF_SIZE) - #error "Please configure log buffer size (in elog_cfg.h)" -#endif - -#if !defined(ELOG_FILTER_TAG_MAX_LEN) - #error "Please configure output filter's tag max length (in elog_cfg.h)" -#endif - -#if !defined(ELOG_FILTER_KW_MAX_LEN) - #error "Please configure output filter's keyword max length (in elog_cfg.h)" -#endif - -#if !defined(ELOG_NEWLINE_SIGN) - #error "Please configure output newline sign (in elog_cfg.h)" -#endif - /* output log's level */ #define ELOG_LVL_ASSERT 0 #define ELOG_LVL_ERROR 1 @@ -74,7 +50,7 @@ extern "C" { #define ELOG_LVL_TOTAL_NUM 6 /* EasyLogger software version number */ -#define ELOG_SW_VERSION "1.10.29" +#define ELOG_SW_VERSION "1.11.13" /* EasyLogger assert for developer. */ #ifdef ELOG_ASSERT_ENABLE @@ -212,18 +188,15 @@ void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_ #define elog_d(tag, ...) elog_debug(tag, __VA_ARGS__) #define elog_v(tag, ...) elog_verbose(tag, __VA_ARGS__) +/* elog_buf.c */ +void elog_flush(void); + +/* elog_async.c */ +size_t elog_async_get_log(char *log, size_t size); + /* elog_utils.c */ size_t elog_strcpy(size_t cur_len, char *dst, const char *src); -/* elog_port.c */ -ElogErrCode elog_port_init(void); -void elog_port_output(const char *log, size_t size); -void elog_port_output_lock(void); -void elog_port_output_unlock(void); -const char *elog_port_get_time(void); -const char *elog_port_get_p_info(void); -const char *elog_port_get_t_info(void); - #ifdef __cplusplus } #endif diff --git a/easylogger/inc/elog_cfg.h b/easylogger/inc/elog_cfg.h index c93f26e..1e399bc 100644 --- a/easylogger/inc/elog_cfg.h +++ b/easylogger/inc/elog_cfg.h @@ -29,23 +29,40 @@ #ifndef _ELOG_CFG_H_ #define _ELOG_CFG_H_ -/* enable log output. default open this macro */ +/* enable log output. */ #define ELOG_OUTPUT_ENABLE -/* setting static output log level */ -#define ELOG_OUTPUT_LVL /* @note you must define it for a value */ +/* setting static output log level. range: from ELOG_LVL_ASSERT to ELOG_LVL_VERBOSE */ +#define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE /* enable assert check */ #define ELOG_ASSERT_ENABLE -/* log buffer size */ -#define ELOG_BUF_SIZE /* @note you must define it for a value */ +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 1024 /* output line number max length */ -#define ELOG_LINE_NUM_MAX_LEN /* @note you must define it for a value */ +#define ELOG_LINE_NUM_MAX_LEN 5 /* output filter's tag max length */ -#define ELOG_FILTER_TAG_MAX_LEN /* @note you must define it for a value */ +#define ELOG_FILTER_TAG_MAX_LEN 30 /* output filter's keyword max length */ -#define ELOG_FILTER_KW_MAX_LEN /* @note you must define it for a value */ +#define ELOG_FILTER_KW_MAX_LEN 16 /* output newline sign */ -#define ELOG_NEWLINE_SIGN /* @note you must define it for a value */ -/* change the verbose(or others) level logs to not default color if you want */ -#define ELOG_COLOR_VERBOSE /* @note you must define it for a value */ +#define ELOG_NEWLINE_SIGN "\n" +/* change the some level logs to not default color if you want */ +#define ELOG_COLOR_ASSERT (F_MAGENTA B_NULL S_NORMAL) +#define ELOG_COLOR_ERROR (F_RED B_NULL S_NORMAL) +#define ELOG_COLOR_WARN (F_YELLOW B_NULL S_NORMAL) +#define ELOG_COLOR_INFO (F_CYAN B_NULL S_NORMAL) +#define ELOG_COLOR_DEBUG (F_GREEN B_NULL S_NORMAL) +#define ELOG_COLOR_VERBOSE (F_BLUE B_NULL S_NORMAL) + +/* enable asynchronous output mode */ +#define ELOG_ASYNC_OUTPUT_ENABLE +/* buffer size for asynchronous output mode */ +#define ELOG_ASYNC_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) +/* asynchronous output mode using POSIX pthread implementation */ +#define ELOG_ASYNC_OUTPUT_USING_PTHREAD + +/* enable buffered output mode */ +#define ELOG_BUFF_OUTPUT_ENABLE +/* buffer size for buffered output mode */ +#define ELOG_BUF_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) #endif /* _ELOG_CFG_H_ */ diff --git a/easylogger/src/elog.c b/easylogger/src/elog.c index 992c7b0..72458b5 100644 --- a/easylogger/src/elog.c +++ b/easylogger/src/elog.c @@ -31,6 +31,30 @@ #include #include +#if !defined(ELOG_OUTPUT_LVL) + #error "Please configure static output log level (in elog_cfg.h)" +#endif + +#if !defined(ELOG_LINE_NUM_MAX_LEN) + #error "Please configure output line number max length (in elog_cfg.h)" +#endif + +#if !defined(ELOG_LINE_BUF_SIZE) + #error "Please configure buffer size for every line's log (in elog_cfg.h)" +#endif + +#if !defined(ELOG_FILTER_TAG_MAX_LEN) + #error "Please configure output filter's tag max length (in elog_cfg.h)" +#endif + +#if !defined(ELOG_FILTER_KW_MAX_LEN) + #error "Please configure output filter's keyword max length (in elog_cfg.h)" +#endif + +#if !defined(ELOG_NEWLINE_SIGN) + #error "Please configure output newline sign (in elog_cfg.h)" +#endif + /** * CSI(Control Sequence Introducer/Initiator) sign * more information on https://en.wikipedia.org/wiki/ANSI_escape_code @@ -83,8 +107,8 @@ /* EasyLogger object */ static EasyLogger elog; -/* log buffer */ -static char log_buf[ELOG_BUF_SIZE] = { 0 }; +/* every line log's buffer */ +static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 }; /* log tag */ static const char *log_tag = "elog"; /* level output info */ @@ -106,24 +130,39 @@ static const char *color_output_info[] = { [ELOG_LVL_VERBOSE] = ELOG_COLOR_VERBOSE, }; - -static void output_lock(void); -static void output_unlock(void); static bool get_fmt_enabled(uint8_t level, size_t set); /* EasyLogger assert hook */ void (*elog_assert_hook)(const char* expr, const char* func, size_t line); +extern void elog_port_output(const char *log, size_t size); +extern void elog_port_output_lock(void); +extern void elog_port_output_unlock(void); + /** * EasyLogger initialize. * * @return result */ ElogErrCode elog_init(void) { + extern ElogErrCode elog_port_init(void); + extern ElogErrCode elog_async_init(void); + ElogErrCode result = ELOG_NO_ERR; /* port initialize */ result = elog_port_init(); + if (result != ELOG_NO_ERR) { + return result; + } + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE + result = elog_async_init(); + if (result != ELOG_NO_ERR) { + return result; + } +#endif + /* enable the output lock */ elog_output_lock_enabled(true); /* output locked status initialize */ @@ -134,6 +173,7 @@ ElogErrCode elog_init(void) { /* set level is ELOG_LVL_VERBOSE */ elog_set_filter_lvl(ELOG_LVL_VERBOSE); + return result; } @@ -242,6 +282,30 @@ void elog_set_filter_kw(const char *keyword) { strncpy(elog.filter.keyword, keyword, ELOG_FILTER_KW_MAX_LEN); } +/** + * lock output + */ +void elog_output_lock(void) { + if (elog.output_lock_enabled) { + elog_port_output_lock(); + elog.output_is_locked_before_disable = true; + } else { + elog.output_is_locked_before_enable = true; + } +} + +/** + * unlock output + */ +void elog_output_unlock(void) { + if (elog.output_lock_enabled) { + elog_port_output_unlock(); + elog.output_is_locked_before_disable = false; + } else { + elog.output_is_locked_before_enable = false; + } +} + /** * output RAW format log * @@ -250,6 +314,7 @@ void elog_set_filter_kw(const char *keyword) { */ void elog_raw(const char *format, ...) { va_list args; + size_t log_len = 0; int fmt_result; /* check output enabled */ @@ -261,20 +326,27 @@ void elog_raw(const char *format, ...) { va_start(args, format); /* lock output */ - output_lock(); + elog_output_lock(); /* package log data to buffer */ - fmt_result = vsnprintf(log_buf, ELOG_BUF_SIZE, format, args); + fmt_result = vsnprintf(log_buf, ELOG_LINE_BUF_SIZE, format, args); /* output converted log */ - if ((fmt_result > -1) && (fmt_result <= ELOG_BUF_SIZE)) { - /* output log */ - elog_port_output(log_buf, fmt_result); + if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { + log_len = fmt_result; } else { - /* output log */ - elog_port_output(log_buf, ELOG_BUF_SIZE); + log_len = ELOG_LINE_BUF_SIZE; } - + /* output log */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(const char *log, size_t size); + elog_async_output(log_buf, log_len); +#elif defined(ELOG_BUFF_OUTPUT_ENABLE) + extern void elog_buf_output(const char *log, size_t size); + elog_buf_output(log_buf, log_len); +#else + elog_port_output(log_buf, log_len); +#endif /* unlock output */ elog_port_output_unlock(); @@ -295,6 +367,10 @@ void elog_raw(const char *format, ...) { */ void elog_output(uint8_t level, const char *tag, const char *file, const char *func, const long line, const char *format, ...) { + extern const char *elog_port_get_time(void); + extern const char *elog_port_get_p_info(void); + extern const char *elog_port_get_t_info(void); + size_t tag_len = strlen(tag), log_len = 0, newline_len = strlen(ELOG_NEWLINE_SIGN); char line_num[ELOG_LINE_NUM_MAX_LEN + 1] = { 0 }; char tag_sapce[ELOG_FILTER_TAG_MAX_LEN / 2 + 1] = { 0 }; @@ -317,7 +393,7 @@ void elog_output(uint8_t level, const char *tag, const char *file, const char *f /* args point to the first variable parameter */ va_start(args, format); /* lock output */ - output_lock(); + elog_output_lock(); /* add CSI start sign and color info */ if (elog.text_color_enabled) { log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START); @@ -388,7 +464,7 @@ void elog_output(uint8_t level, const char *tag, const char *file, const char *f log_len += elog_strcpy(log_len, log_buf + log_len, ")"); } /* package other log data to buffer. '\0' must be added in the end by vsnprintf. */ - fmt_result = vsnprintf(log_buf + log_len, ELOG_BUF_SIZE - log_len - newline_len + 1, format, args); + fmt_result = vsnprintf(log_buf + log_len, ELOG_LINE_BUF_SIZE - log_len - newline_len + 1, format, args); va_end(args); /* add CSI end sign */ @@ -399,22 +475,30 @@ void elog_output(uint8_t level, const char *tag, const char *file, const char *f if (!strstr(log_buf, elog.filter.keyword)) { //TODO ԿDzKMPģʽƥַ /* unlock output */ - output_unlock(); + elog_output_unlock(); return; } /* package newline sign */ - if ((fmt_result > -1) && (fmt_result + log_len + newline_len <= ELOG_BUF_SIZE)) { + if ((fmt_result > -1) && (fmt_result + log_len + newline_len <= ELOG_LINE_BUF_SIZE)) { log_len += fmt_result; log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); } else { - log_len = ELOG_BUF_SIZE; + log_len = ELOG_LINE_BUF_SIZE; /* copy newline sign */ - strcpy(log_buf + ELOG_BUF_SIZE - newline_len, ELOG_NEWLINE_SIGN); + strcpy(log_buf + ELOG_LINE_BUF_SIZE - newline_len, ELOG_NEWLINE_SIGN); } /* output log */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(const char *log, size_t size); + elog_async_output(log_buf, log_len); +#elif defined(ELOG_BUFF_OUTPUT_ENABLE) + extern void elog_buf_output(const char *log, size_t size); + elog_buf_output(log_buf, log_len); +#else elog_port_output(log_buf, log_len); +#endif /* unlock output */ - output_unlock(); + elog_output_unlock(); } /** @@ -455,29 +539,6 @@ void elog_output_lock_enabled(bool enabled) { } } -/** - * lock output - */ -static void output_lock(void) { - if (elog.output_lock_enabled) { - elog_port_output_lock(); - elog.output_is_locked_before_disable = true; - } else { - elog.output_is_locked_before_enable = true; - } -} -/** - * unlock output - */ -static void output_unlock(void) { - if (elog.output_lock_enabled) { - elog_port_output_unlock(); - elog.output_is_locked_before_disable = false; - } else { - elog.output_is_locked_before_enable = false; - } -} - /** * Set a hook function to EasyLogger assert. It will run when the expression is false. * diff --git a/easylogger/src/elog_async.c b/easylogger/src/elog_async.c new file mode 100644 index 0000000..038ccfa --- /dev/null +++ b/easylogger/src/elog_async.c @@ -0,0 +1,264 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Logs asynchronous output. + * Created on: 2016-11-06 + */ + +#include +#include + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE +#if !defined(ELOG_ASYNC_OUTPUT_BUF_SIZE) + #error "Please configure buffer size for asynchronous output mode (in elog_cfg.h)" +#endif + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD +#include +#include +#include +/* thread default stack size */ +#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE +#if PTHREAD_STACK_MIN > 4*1024 +#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE PTHREAD_STACK_MIN +#else +#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE (1*1024) +#endif +/* thread default priority */ +#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY +#define ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY (sched_get_priority_max(SCHED_RR) - 1) +#endif +/* output thread poll get log buffer size */ +#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE +#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE (ELOG_LINE_BUF_SIZE - 4) +#endif +#endif /* ELOG_ASYNC_OUTPUT_USING_PTHREAD */ + +/* asynchronous output log notice */ +static sem_t output_notice; +/* asynchronous output pthread thread */ +static pthread_t async_output_thread; +#endif + +/* Initialize OK flag */ +static bool init_ok = false; +/* asynchronous output mode's ring buffer */ +static char log_buf[ELOG_ASYNC_OUTPUT_BUF_SIZE] = { 0 }; +/* log ring buffer write index */ +static size_t write_index = 0; +/* log ring buffer read index */ +static size_t read_index = 0; +/* log ring buffer full flag */ +static bool buf_is_full = false; +/* log ring buffer empty flag */ +static bool buf_is_empty = true; + +extern void elog_port_output(const char *log, size_t size); +extern void elog_output_lock(void); +extern void elog_output_unlock(void); + +/** + * asynchronous output ring buffer used size + * + * @return used size + */ +static size_t elog_async_get_buf_used(void) { + if (write_index > read_index) { + return write_index - read_index; + } else { + if (!buf_is_full && !buf_is_empty) { + return ELOG_ASYNC_OUTPUT_BUF_SIZE - (read_index - write_index); + } else if (buf_is_full) { + return ELOG_ASYNC_OUTPUT_BUF_SIZE; + } else { + return 0; + } + } +} + +/** + * asynchronous output ring buffer remain space + * + * @return remain space + */ +static size_t async_get_buf_space(void) { + return ELOG_ASYNC_OUTPUT_BUF_SIZE - elog_async_get_buf_used(); +} + +/** + * put log to asynchronous output ring buffer + * + * @param log put log buffer + * @param size log size + * + * @return put log size, the log which beyond ring buffer space will be dropped + */ +static size_t async_put_log(const char *log, size_t size) { + size_t space = 0; + + space = async_get_buf_space(); + /* no space */ + if (!space) { + size = 0; + goto __exit; + } + /* drop some log */ + if (space <= size) { + size = space; + buf_is_full = true; + } + + if (write_index + size < ELOG_ASYNC_OUTPUT_BUF_SIZE) { + memcpy(log_buf + write_index, log, size); + write_index += size; + } else { + memcpy(log_buf + write_index, log, ELOG_ASYNC_OUTPUT_BUF_SIZE - write_index); + memcpy(log_buf, log + ELOG_ASYNC_OUTPUT_BUF_SIZE - write_index, + size - (ELOG_ASYNC_OUTPUT_BUF_SIZE - write_index)); + write_index += size - ELOG_ASYNC_OUTPUT_BUF_SIZE; + } + + buf_is_empty = false; + +__exit: + + return size; +} + +/** + * get log from asynchronous output ring buffer + * + * @param log get log buffer + * @param size log size + * + * @return get log size, the log size is less than ring buffer used size + */ +size_t elog_async_get_log(char *log, size_t size) { + size_t used = 0; + /* lock output */ + elog_output_lock(); + used = elog_async_get_buf_used(); + /* no log */ + if (!used) { + size = 0; + goto __exit; + } + /* less log */ + if (used <= size) { + size = used; + buf_is_empty = true; + } + + if (read_index + size < ELOG_ASYNC_OUTPUT_BUF_SIZE) { + memcpy(log, log_buf + read_index, size); + read_index += size; + } else { + memcpy(log, log_buf + read_index, ELOG_ASYNC_OUTPUT_BUF_SIZE - read_index); + memcpy(log + ELOG_ASYNC_OUTPUT_BUF_SIZE - read_index, log_buf, + size - (ELOG_ASYNC_OUTPUT_BUF_SIZE - read_index)); + read_index += size - ELOG_ASYNC_OUTPUT_BUF_SIZE; + } + + buf_is_full = false; + +__exit: + /* lock output */ + elog_output_unlock(); + return size; +} + +void elog_async_output(const char *log, size_t size) { + /* this function must be implement by user when ELOG_ASYNC_OUTPUT_USING_PTHREAD is not defined */ + extern void elog_async_output_notice(void); + size_t put_size; + + put_size = async_put_log(log, size); + /* notify output log thread */ + if (put_size > 0) { + elog_async_output_notice(); + } +} + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD +void elog_async_output_notice(void) { + sem_post(&output_notice); +} + +static void *async_output(void *arg) { + size_t get_log_size = 0; + static char poll_get_buf[ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE]; + + ELOG_ASSERT(init_ok); + + while(true) { + /* waiting log */ + sem_wait(&output_notice); + /* polling gets and outputs the log */ + while(true) { + get_log_size = elog_async_get_log(poll_get_buf, sizeof(poll_get_buf)); + if (get_log_size) { + elog_port_output(poll_get_buf, get_log_size); + } else { + break; + } + } + } + return NULL; +} +#endif + +/** + * asynchronous output mode initialize + * + * @return result + */ +ElogErrCode elog_async_init(void) { + ElogErrCode result = ELOG_NO_ERR; + + if (init_ok) { + return result; + } + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD + pthread_attr_t thread_attr; + struct sched_param thread_sched_param; + + sem_init(&output_notice, 0, 0); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thread_attr, ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE); + pthread_attr_setschedpolicy(&thread_attr, SCHED_RR); + thread_sched_param.sched_priority = ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY; + pthread_attr_setschedparam(&thread_attr, &thread_sched_param); + pthread_create(&async_output_thread, &thread_attr, async_output, NULL); + pthread_attr_destroy(&thread_attr); +#endif + + init_ok = true; + + return result; +} + +#endif /* ELOG_ASYNC_OUTPUT_ENABLE */ diff --git a/easylogger/src/elog_buf.c b/easylogger/src/elog_buf.c new file mode 100644 index 0000000..b3e3ea2 --- /dev/null +++ b/easylogger/src/elog_buf.c @@ -0,0 +1,87 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Logs buffered output. + * Created on: 2016-11-09 + */ + +#include +#include + +#ifdef ELOG_BUF_OUTPUT_ENABLE +#if !defined(ELOG_BUF_OUTPUT_BUF_SIZE) + #error "Please configure buffer size for buffered output mode (in elog_cfg.h)" +#endif + +/* buffered output mode's buffer */ +static char log_buf[ELOG_BUF_OUTPUT_BUF_SIZE] = { 0 }; +/* log buffer current write size */ +static size_t buf_write_size = 0; + +extern void elog_port_output(const char *log, size_t size); +extern void elog_output_lock(void); +extern void elog_output_unlock(void); + +/** + * output buffered logs when buffer is full + * + * @param log will be buffered line's log + * @param size log size + */ +void elog_buf_output(const char *log, size_t size) { + size_t write_size = 0, write_index = 0; + + while (true) { + if (buf_write_size + size > ELOG_BUF_OUTPUT_BUF_SIZE) { + write_size = ELOG_BUF_OUTPUT_BUF_SIZE - buf_write_size; + memcpy(log_buf + buf_write_size, log + write_index, write_size); + write_index += write_size; + size -= write_size; + buf_write_size += write_size; + /* output log */ + elog_port_output(log_buf, buf_write_size); + /* reset write index */ + buf_write_size = 0; + } else { + memcpy(log_buf + buf_write_size, log + write_index, size); + buf_write_size += size; + break; + } + } +} + +/** + * flush all buffered logs to output device + */ +void elog_flush(void) { + /* lock output */ + elog_output_lock(); + /* output log */ + elog_port_output(log_buf, buf_write_size); + /* reset write index */ + buf_write_size = 0; + /* unlock output */ + elog_output_unlock(); +} +#endif /* ELOG_BUF_OUTPUT_ENABLE */ diff --git a/easylogger/src/elog_utils.c b/easylogger/src/elog_utils.c index fa72411..2d39f4f 100644 --- a/easylogger/src/elog_utils.c +++ b/easylogger/src/elog_utils.c @@ -31,7 +31,7 @@ /** * another copy string function * - * @param cur_len current copied log length, max size is ELOG_BUF_SIZE + * @param cur_len current copied log length, max size is ELOG_LINE_BUF_SIZE * @param dst destination * @param src source * @@ -41,7 +41,7 @@ size_t elog_strcpy(size_t cur_len, char *dst, const char *src) { const char *src_old = src; while (*src != 0) { /* make sure destination has enough space */ - if (cur_len++ <= ELOG_BUF_SIZE) { + if (cur_len++ <= ELOG_LINE_BUF_SIZE) { *dst++ = *src++; } else { break;