diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..a6ec1ac29ee9ec4459a962fec9f0412e46b745e5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,40 @@ +* text=auto + +*.S text +*.asm text +*.c text +*.cc text +*.cpp text +*.cxx text +*.h text +*.htm text +*.html text +*.in text +*.ld text +*.m4 text +*.mak text +*.mk text +*.py text +*.rb text +*.s text +*.sct text +*.sh text +*.txt text +*.xml text +Makefile text +AUTHORS text +COPYING text + +*.LZO -text +*.Opt -text +*.Uv2 -text +*.ewp -text +*.eww -text +*.vcproj -text +*.bat -text +*.dos -text +*.icf -text +*.inf -text +*.ini -text +*.sct -text +*.xsd -text diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9d56606172da538d5f3b0b81dcb07336a2863632 --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# EasyLogger + +--- + +# 1. 介绍 + +[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、Nuttx...),也支持裸机平台; +- 日志支持 **RAW格式** ,支持 **hexdump** ; +- 支持按 **标签** 、 **级别** 、 **关键词** 进行动态过滤; +- 各级别日志支持不同颜色显示; +- 扩展性强,支持以插件形式扩展新功能。 + +> 名词解释: +> +> - 1、RAW格式:未经过格式化的原始日志。 +> - 2、标签:在软件中可以按照文件、模块、功能等方面,对需要打印的日志设定标签,实现日志分类。 + +## 1.2 插件 + +- 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** ,同时把它推荐给更多有需要的朋友。 + +# 2. 使用 + +### 2.1 参数配置 + +EasyLogger 拥有过滤方式、输出格式、输出开关这些属性。 + +- 过滤方式支持按照标签、级别、关键词进行过滤; +- 可以动态的开启/关闭日志的输出; +- 可设定动态和静态的输出级别 + - 静态:一级开关,通过宏定义,在编译阶段使用; + - 动态:二级开关,通过API接口,在运行阶段使用。 + +> 注:目前参数配置及输出方式都是单例模式,即全局只支持一种配置方式。此模式下,软件会较为简单,但是无法支持复杂的输出方式。 + +### 2.2 输出级别 + +参考 Android Logcat ,级别最高为 0(Assert) ,最低为 5(Verbose) 。 + +``` +0.[A]:断言(Assert) +1.[E]:错误(Error) +2.[W]:警告(Warn) +3.[I]:信息(Info) +4.[D]:调试(Debug) +5.[V]:详细(Verbose) +``` + +#### 2.2.1 输出缤纷多彩的日志 + +各个级别日志默认颜色效果如下。用户也可以根据自己的喜好,在 `elog_cfg.h` 对各个级别日志的颜色及字体风格进行单独设置。 + +![TextColor](https://raw.githubusercontent.com/armink/EasyLogger/master/docs/zh/images/TextColor.png) + +### 2.3 输出过滤 + +支持按照 **级别、标签及关键词** 进行过滤。日志内容较多时,使用过滤功能可以更快定位日志,保证日志的可读性。更多的过滤功能设置方法及细节请阅读[`\docs\zh\api\kernel.md`](https://github.com/armink/EasyLogger/blob/master/docs/zh/api/kernel.md)文档 + +注:RAW格式、hexdump 格式日志不支持标签、关键词过滤 + +### 2.4 输出格式 + +输出格式支持:级别、时间、标签、进程信息、线程信息、文件路径、行号、方法名。每种优先级别可以独立设置输出格式。 + +### 2.5 输出方式 + +通过用户的移植,可以支持任何一种输出方式。只不过对于某种输出方式可能引入的新功能,需要通过插件实现,例如:文件转存,检索Flash日志等等。后期会有更多的插件开源出来。下面简单对比下部分输出方式使用场景: + +- 终端:方便用户动态查看,不具有存储功能; +- 文件与Flash:都具有存储功能,用户可以查看历史日志。但是文件方式需要文件系统的支持,而Flash方式更加适合应用在无文件系统的小型嵌入式设备中。 + +### 2.6 Demo + +### 2.6.1 核心功能 + +下图为在终端中输入命令来控制日志的输出及过滤器的设置,更加直观的展示了 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) +- 移植文档:[`\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 中) + +下图过程为通过控制台输出日志,并将输出的日志存储到 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) +- 移植文档:[`\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) + +### 2.6.2 File Log(将日志保存到文件中) + +通过 FIle 插件,可以将日志自动保存至文件中。每个文件可以设定大小,超过规定大小后,自动新建新的文件来存储日志。 + +# 3. 文档 + +具体内容参考[`\docs\zh\`](https://github.com/armink/EasyLogger/tree/master/docs/zh)下的文件。务必保证在 **阅读文档** 后再移植使用。 + +# 4. 后期 + +- [x] Flash存储:在[EasyFlash](https://github.com/armink/EasyFlash)中增加日志存储、读取功能,让EasyLogger与其无缝对接。使日志可以更加容易的存储在 **非文件系统** 中,并具有历史日志检索的功能; +- [ ] 配置文件:文件系统下的配置文件; +- [x] 文件转档:文件系统下支持文件按容量转档,按时间区分; +- [ ] 日志助手:开发跨平台的日志助手,兼容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); +- [x] 异步输出:目前日志输出与用户代码之间是同步的方式,这种方式虽然软件简单,也不存在日志覆盖的问题。但在输出速度较低的平台下,会由于增加日志功能,而降低软件运行速度。所以后期会增加 **异步输出** 方式,关键字过滤也可以放到异步输出中去; +- [ ] Arduino:增加Arduino lib,并提供其Demo; + +# 5. 许可 + +MIT Copyright (c) armink.ztl@gmail.com diff --git a/example/example.c b/example/example.c new file mode 100644 index 0000000000000000000000000000000000000000..fd5c7d5cd39301bfa7ed0474e74ed5c73a5e534d --- /dev/null +++ b/example/example.c @@ -0,0 +1,39 @@ +#include "elog.h" +#include +#include + +static void test_elog(void); + +int easylogger_example(int argc, char *argv[]) +{ + if ((elog_init() == ELOG_NO_ERR)) + { + /* set enabled format */ + elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL & ~ELOG_FMT_P_INFO); + elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO)); + elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO)); + /* start EasyLogger */ + elog_start(); + } + test_elog(); + + return 0; +} +#include "aos/cli.h" +ALIOS_CLI_CMD_REGISTER(easylogger_example, easylogger, example); + +/** + * Elog demo + */ +static void test_elog(void) +{ + log_a("Hello EasyLogger!"); + log_e("Hello EasyLogger!"); + log_w("Hello EasyLogger!"); + log_i("Hello EasyLogger!"); + log_d("Hello EasyLogger!"); + log_v("Hello EasyLogger!"); +} diff --git a/include/elog.h b/include/elog.h new file mode 100644 index 0000000000000000000000000000000000000000..994b6eba9021a694865b4359fa6c02bf5e3a4a44 --- /dev/null +++ b/include/elog.h @@ -0,0 +1,279 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2019, 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: It is an head file for this library. You can see all be called functions. + * Created on: 2015-04-28 + */ + +#ifndef __ELOG_H__ +#define __ELOG_H__ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* output log's level */ +#define ELOG_LVL_ASSERT 0 +#define ELOG_LVL_ERROR 1 +#define ELOG_LVL_WARN 2 +#define ELOG_LVL_INFO 3 +#define ELOG_LVL_DEBUG 4 +#define ELOG_LVL_VERBOSE 5 + +/* the output silent level and all level for filter setting */ +#define ELOG_FILTER_LVL_SILENT ELOG_LVL_ASSERT +#define ELOG_FILTER_LVL_ALL ELOG_LVL_VERBOSE + +/* output log's level total number */ +#define ELOG_LVL_TOTAL_NUM 6 + +/* EasyLogger software version number */ +#define ELOG_SW_VERSION "2.2.99" + +/* EasyLogger assert for developer. */ +#ifdef ELOG_ASSERT_ENABLE + #define ELOG_ASSERT(EXPR) \ + if (!(EXPR)) \ + { \ + if (elog_assert_hook == NULL) { \ + elog_a("elog", "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \ + while (1); \ + } else { \ + elog_assert_hook(#EXPR, __FUNCTION__, __LINE__); \ + } \ + } +#else + #define ELOG_ASSERT(EXPR) ((void)0); +#endif + +#ifndef ELOG_OUTPUT_ENABLE + #define elog_raw(...) + #define elog_assert(tag, ...) + #define elog_error(tag, ...) + #define elog_warn(tag, ...) + #define elog_info(tag, ...) + #define elog_debug(tag, ...) + #define elog_verbose(tag, ...) +#else /* ELOG_OUTPUT_ENABLE */ + #define elog_raw(...) elog_raw_output(__VA_ARGS__) + #if ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT + #define elog_assert(tag, ...) \ + elog_output(ELOG_LVL_ASSERT, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_assert(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR + #define elog_error(tag, ...) \ + elog_output(ELOG_LVL_ERROR, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_error(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_WARN + #define elog_warn(tag, ...) \ + elog_output(ELOG_LVL_WARN, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_warn(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_WARN */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_INFO + #define elog_info(tag, ...) \ + elog_output(ELOG_LVL_INFO, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_info(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_INFO */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG + #define elog_debug(tag, ...) \ + elog_output(ELOG_LVL_DEBUG, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_debug(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG */ + + #if ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE + #define elog_verbose(tag, ...) \ + elog_output(ELOG_LVL_VERBOSE, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_verbose(tag, ...) + #endif /* ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE */ +#endif /* ELOG_OUTPUT_ENABLE */ + +/* all formats index */ +typedef enum { + ELOG_FMT_LVL = 1 << 0, /**< level */ + ELOG_FMT_TAG = 1 << 1, /**< tag */ + ELOG_FMT_TIME = 1 << 2, /**< current time */ + ELOG_FMT_P_INFO = 1 << 3, /**< process info */ + ELOG_FMT_T_INFO = 1 << 4, /**< thread info */ + ELOG_FMT_DIR = 1 << 5, /**< file directory and name */ + ELOG_FMT_FUNC = 1 << 6, /**< function name */ + ELOG_FMT_LINE = 1 << 7, /**< line number */ +} ElogFmtIndex; + +/* macro definition for all formats */ +#define ELOG_FMT_ALL (ELOG_FMT_LVL|ELOG_FMT_TAG|ELOG_FMT_TIME|ELOG_FMT_P_INFO|ELOG_FMT_T_INFO| \ + ELOG_FMT_DIR|ELOG_FMT_FUNC|ELOG_FMT_LINE) + +/* output log's tag filter */ +typedef struct { + uint8_t level; + char tag[ELOG_FILTER_TAG_MAX_LEN + 1]; + bool tag_use_flag; /**< false : tag is no used true: tag is used */ +} ElogTagLvlFilter, *ElogTagLvlFilter_t; + +/* output log's filter */ +typedef struct { + uint8_t level; + char tag[ELOG_FILTER_TAG_MAX_LEN + 1]; + char keyword[ELOG_FILTER_KW_MAX_LEN + 1]; + ElogTagLvlFilter tag_lvl[ELOG_FILTER_TAG_LVL_MAX_NUM]; +} ElogFilter, *ElogFilter_t; + +/* easy logger */ +typedef struct { + ElogFilter filter; + size_t enabled_fmt_set[ELOG_LVL_TOTAL_NUM]; + bool init_ok; + bool output_enabled; + bool output_lock_enabled; + bool output_is_locked_before_enable; + bool output_is_locked_before_disable; + +#ifdef ELOG_COLOR_ENABLE + bool text_color_enabled; +#endif + +}EasyLogger, *EasyLogger_t; + +/* EasyLogger error code */ +typedef enum { + ELOG_NO_ERR, +} ElogErrCode; + +/* elog.c */ +ElogErrCode elog_init(void); +void elog_deinit(void); +void elog_start(void); +void elog_stop(void); +void elog_set_output_enabled(bool enabled); +bool elog_get_output_enabled(void); +void elog_set_text_color_enabled(bool enabled); +bool elog_get_text_color_enabled(void); +void elog_set_fmt(uint8_t level, size_t set); +void elog_set_filter(uint8_t level, const char *tag, const char *keyword); +void elog_set_filter_lvl(uint8_t level); +void elog_set_filter_tag(const char *tag); +void elog_set_filter_kw(const char *keyword); +void elog_set_filter_tag_lvl(const char *tag, uint8_t level); +uint8_t elog_get_filter_tag_lvl(const char *tag); +void elog_raw_output(const char *format, ...); +void elog_output(uint8_t level, const char *tag, const char *file, const char *func, + const long line, const char *format, ...); +void elog_output_lock_enabled(bool enabled); +extern void (*elog_assert_hook)(const char* expr, const char* func, size_t line); +void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_t line)); +int8_t elog_find_lvl(const char *log); +const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len); +void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size); + +#define elog_a(tag, ...) elog_assert(tag, __VA_ARGS__) +#define elog_e(tag, ...) elog_error(tag, __VA_ARGS__) +#define elog_w(tag, ...) elog_warn(tag, __VA_ARGS__) +#define elog_i(tag, ...) elog_info(tag, __VA_ARGS__) +#define elog_d(tag, ...) elog_debug(tag, __VA_ARGS__) +#define elog_v(tag, ...) elog_verbose(tag, __VA_ARGS__) + +/** + * log API short definition + * NOTE: The `LOG_TAG` and `LOG_LVL` must defined before including the when you want to use log_x API. + */ +#if !defined(LOG_TAG) + #define LOG_TAG "NO_TAG" +#endif +#if !defined(LOG_LVL) + #define LOG_LVL ELOG_LVL_VERBOSE +#endif +#if LOG_LVL >= ELOG_LVL_ASSERT + #define log_a(...) elog_a(LOG_TAG, __VA_ARGS__) +#else + #define log_a(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_ERROR + #define log_e(...) elog_e(LOG_TAG, __VA_ARGS__) +#else + #define log_e(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_WARN + #define log_w(...) elog_w(LOG_TAG, __VA_ARGS__) +#else + #define log_w(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_INFO + #define log_i(...) elog_i(LOG_TAG, __VA_ARGS__) +#else + #define log_i(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_DEBUG + #define log_d(...) elog_d(LOG_TAG, __VA_ARGS__) +#else + #define log_d(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_VERBOSE + #define log_v(...) elog_v(LOG_TAG, __VA_ARGS__) +#else + #define log_v(...) ((void)0); +#endif + +/* assert API short definition */ +#if !defined(assert) + #define assert ELOG_ASSERT +#endif + +/* elog_buf.c */ +void elog_buf_enabled(bool enabled); +void elog_flush(void); + +/* elog_async.c */ +void elog_async_enabled(bool enabled); +size_t elog_async_get_log(char *log, size_t size); +size_t elog_async_get_line_log(char *log, size_t size); + +/* elog_utils.c */ +size_t elog_strcpy(size_t cur_len, char *dst, const char *src); +size_t elog_cpyln(char *line, const char *log, size_t len); +void *elog_memcpy(void *dst, const void *src, size_t count); + +#ifdef __cplusplus +} +#endif + +#endif /* __ELOG_H__ */ diff --git a/include/elog_cfg.h b/include/elog_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..bbdc7c2758f61e00dd6d723fc13f49f3264f2ad2 --- /dev/null +++ b/include/elog_cfg.h @@ -0,0 +1,79 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-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: It is the configure head file for this library. + * Created on: 2015-07-30 + */ + +#ifndef _ELOG_CFG_H_ +#define _ELOG_CFG_H_ +/*---------------------------------------------------------------------------*/ +/* enable log output. */ +#define ELOG_OUTPUT_ENABLE +/* 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 +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 1024 +/* output line number max length */ +#define ELOG_LINE_NUM_MAX_LEN 5 +/* output filter's tag max length */ +#define ELOG_FILTER_TAG_MAX_LEN 30 +/* output filter's keyword max length */ +#define ELOG_FILTER_KW_MAX_LEN 16 +/* output filter's tag level max num */ +#define ELOG_FILTER_TAG_LVL_MAX_NUM 5 +/* output newline sign */ +#define ELOG_NEWLINE_SIGN "\n" +/*---------------------------------------------------------------------------*/ +/* enable log color */ +#define ELOG_COLOR_ENABLE +/* 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 +/* the highest output level for async mode, other level will sync output */ +#define ELOG_ASYNC_OUTPUT_LVL ELOG_LVL_ASSERT +/* buffer size for asynchronous output mode */ +#define ELOG_ASYNC_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) +/* each asynchronous output's log which must end with newline sign */ +#define ELOG_ASYNC_LINE_OUTPUT +/* asynchronous output mode using POSIX pthread implementation */ +// #define ELOG_ASYNC_OUTPUT_USING_PTHREAD +/*---------------------------------------------------------------------------*/ +/* enable buffered output mode */ +#define ELOG_BUF_OUTPUT_ENABLE +/* buffer size for buffered output mode */ +#define ELOG_BUF_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) + +#define USING_POSIX_API 1 + +#endif /* _ELOG_CFG_H_ */ diff --git a/package.yaml b/package.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a8bd9c1b4ce039d2894a8dbb23be96cd2d429090 --- /dev/null +++ b/package.yaml @@ -0,0 +1,61 @@ + +## 第一部分: 基础信息 +name: EasyLogger # <必选项> 包名称 (符合C语言变量命名规则),长度少于等于64字节 +version: develop # <必选项> 组件版本号 +description: EasyLogger rtthread 适配 # <必选项> 建议至少20字以上 +type: common # <必选项> 组件类型,为:solution, chip, board, common, sdk +tag: 核心模块 # <可选项> 组件分类,缺省值: '' +keywords: # <可选项> 标签,会影响到组件被搜索的效果,合理的标签很重要 + - base +license: Apache license v2.0 # <可选项> 源代码的许可证,要确保所有代码、文件的许可证不冲突。如:MIT,Apache license v2.0,BSD + +## 第二部分:依赖信息 +# 指定该组件依赖的组件及版本 +# sdk_chip: # <可选项> 该组件依赖sdk组件,合理的依赖才能保证组件能编译、使用 +# - sdk_chip_csky_dummy: v7.4.0 +# - sdk_chip_riscv_dummy: v7.4.0 +depends: # <可选项> 该组件依赖其他的组件,合理的依赖才能保证组件能编译、使用 + # - devices: develop + +## 第四部分:编译连接信息 +# build_config: # <可选项> 编译配置项 +# include: # <可选项> 编译时,影响编译器的-I 参数 ,全局有效 +# - src # include 只能是该软件包下的目录,不能使用外部目录 +# internal_include: # <可选项> 编译时,影响编译器的-I 参数 ,组件内有效 +# - include +# cflag: '' # <可选项> C 编译器所需要要的编译参数 +# cxxflag: '' # <可选项> CXX 编译器所需要要的编译参数 +# asmflag: '' # <可选项> 汇编器所需要要参数 +# define: # <可选项> 宏定义, 增加编译器的-D 选项,如: +# XXX: 1 # -DXXX=1 +# AAA: 1 # -DAAA +# STR: "abc" # -DSTR="abc" +# libs: # 该组件中支持的二进制静态库,如:libxxx.a, libyyy.a +# - xxx # -lxxx +# - yyy # -lyyy +# libpath: # 指定静态库所在的路径(相对于该组件路径) +# - libs # -Llibs +build_config: + cflag: -O1 -g -Wall + include: + - include + +# source_file: # <可选项> 指定参与编译的源代码文件,支持通配符,采用相对路径 + # - src/*.c # 例:组件 src 目录下所有的扩展名为 c 的源代码文件 +source_file: + - example/*.c + - port/*.c + - src/*.c +## 第五部分:配置信息 +# def_config: # 组件的可配置项 +# CONFIG_DEBUG: y +# CONFIG_PARAM_NOT_CHECK: y +# CONFIG_CLI: y +def_config: + +## 第六部分:安装信息 +# install: +# - dest: include/ # 安装的目的路径 dest是相对路径,通常是相对于YoC SDK 安装目录 +# source: # 安装源列表 +# - src/*.h # 支持通配符,相对路径 + diff --git a/port/elog_port.c b/port/elog_port.c new file mode 100644 index 0000000000000000000000000000000000000000..ef518746a30f45b0c59f28709bf98b59255b148d --- /dev/null +++ b/port/elog_port.c @@ -0,0 +1,237 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015, 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: Portable interface for each platform. + * Created on: 2015-04-28 + */ +#include + +#if USING_POSIX_API + #include "pthread.h" + #include "semaphore.h" + #include + + #define printf aos_cli_printf + static sem_t output_lock; +#else + #define printf rt_kprintf + static struct rt_semaphore output_lock; +#endif + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE + #if USING_POSIX_API + static sem_t output_notice; + #else + static struct rt_semaphore output_notice; + #endif + static void *async_output(void *arg); +#endif + +/** + * EasyLogger port initialize + * + * @return result + */ +ElogErrCode elog_port_init(void) +{ + ElogErrCode result = ELOG_NO_ERR; + +#if USING_POSIX_API + sem_init(&output_lock, 0, 1); +#else + /* add your code here */ + rt_sem_init(&output_lock, "elog lock", 1, RT_IPC_FLAG_PRIO); +#endif + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE +#if USING_POSIX_API + void *status = NULL; + pthread_t async_thread; + + sem_init(&output_notice, 0, 0); + result = pthread_create(&async_thread, NULL, async_output, NULL); +#else + rt_thread_t async_thread = NULL; + + rt_sem_init(&output_notice, "elog async", 0, RT_IPC_FLAG_PRIO); + + async_thread = rt_thread_create("elog_async", async_output, NULL, 4096, RT_THREAD_PRIORITY_MAX - 1, 10); + if (async_thread) + { + rt_thread_startup(async_thread); + } +#endif /*USING_POSIX_API*/ +#endif /*ELOG_ASYNC_OUTPUT_ENABLE*/ + + return result; +} + +/** + * EasyLogger port deinitialize + * + */ +void elog_port_deinit(void) +{ + + /* add your code here */ +} + +/** + * output log port interface + * + * @param log output of log + * @param size log size + */ +void elog_port_output(const char *log, size_t size) +{ + + /* add your code here */ + /* output to terminal */ + printf("%.*s\r", size, log); +} + +/** + * output lock + */ +void elog_port_output_lock(void) +{ +#if USING_POSIX_API + sem_wait(&output_lock); +#else + /* add your code here */ + rt_sem_take(&output_lock, RT_WAITING_FOREVER); +#endif +} + +/** + * output unlock + */ +void elog_port_output_unlock(void) +{ +#if USING_POSIX_API + sem_post(&output_lock); +#else + /* add your code here */ + rt_sem_release(&output_lock); +#endif +} + +/** + * get current time interface + * + * @return current time + */ +const char *elog_port_get_time(void) +{ + /* add your code here */ + static char cur_system_time[16] = {0}; + uint32_t tick = 0u; + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + tick = ts.tv_sec * 100 * 1000 + ts.tv_nsec / (1000 * 1000); + snprintf(cur_system_time, 16, "tick:%010d", (uint32_t)((tick + 100 - 1) / 100)); + return cur_system_time; +} + +/** + * get current process name interface + * + * @return current process name + */ +const char *elog_port_get_p_info(void) +{ + /* add your code here */ + return ""; +} + +/** + * get current thread name interface + * + * @return current thread name + */ +const char *elog_port_get_t_info(void) +{ + /* add your code here */ +#if USING_POSIX_API +#define THREAD_NAME_LEN 16 + static char thread_name[USING_POSIX_API]; + int result = pthread_getname_np(pthread_self(), thread_name, USING_POSIX_API); + if (result != 0) + { + return ""; + } + return thread_name; +#else + return rt_thread_self()->parent.name; +#endif +} + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE +void elog_async_output_notice(void) +{ +#if USING_POSIX_API + sem_post(&output_notice); +#else + rt_sem_release(&output_notice); +#endif +} + +static void *async_output(void *arg) +{ + size_t get_log_size = 0; + static char poll_get_buf[ELOG_LINE_BUF_SIZE - 4]; + + while (true) + { +#if USING_POSIX_API + sem_wait(&output_notice); +#else + /* waiting log */ + rt_sem_take(&output_notice, RT_WAITING_FOREVER); +#endif + /* polling gets and outputs the log */ + while (true) + { + +#ifdef ELOG_ASYNC_LINE_OUTPUT + get_log_size = elog_async_get_line_log(poll_get_buf, sizeof(poll_get_buf)); +#else + get_log_size = elog_async_get_log(poll_get_buf, sizeof(poll_get_buf)); +#endif + + if (get_log_size) + { + elog_port_output(poll_get_buf, get_log_size); + } + else + { + break; + } + } + } + + return 0; +} +#endif \ No newline at end of file diff --git a/src/elog.c b/src/elog.c new file mode 100644 index 0000000000000000000000000000000000000000..421a110233140ecad4b02e125d213e5b66da5473 --- /dev/null +++ b/src/elog.c @@ -0,0 +1,925 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2018, 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: Initialize function and other general function. + * Created on: 2015-04-28 + */ + +#define LOG_TAG "elog" + +#include +#include +#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 + +/* output filter's tag level max num */ +#ifndef ELOG_FILTER_TAG_LVL_MAX_NUM +#define ELOG_FILTER_TAG_LVL_MAX_NUM 4 +#endif + +#ifdef ELOG_COLOR_ENABLE +/** + * CSI(Control Sequence Introducer/Initiator) sign + * more information on https://en.wikipedia.org/wiki/ANSI_escape_code + */ +#define CSI_START "\033[" +#define CSI_END "\033[0m" +/* output log front color */ +#define F_BLACK "30;" +#define F_RED "31;" +#define F_GREEN "32;" +#define F_YELLOW "33;" +#define F_BLUE "34;" +#define F_MAGENTA "35;" +#define F_CYAN "36;" +#define F_WHITE "37;" +/* output log background color */ +#define B_NULL +#define B_BLACK "40;" +#define B_RED "41;" +#define B_GREEN "42;" +#define B_YELLOW "43;" +#define B_BLUE "44;" +#define B_MAGENTA "45;" +#define B_CYAN "46;" +#define B_WHITE "47;" +/* output log fonts style */ +#define S_BOLD "1m" +#define S_UNDERLINE "4m" +#define S_BLINK "5m" +#define S_NORMAL "22m" +/* output log default color definition: [front color] + [background color] + [show style] */ +#ifndef ELOG_COLOR_ASSERT +#define ELOG_COLOR_ASSERT (F_MAGENTA B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_ERROR +#define ELOG_COLOR_ERROR (F_RED B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_WARN +#define ELOG_COLOR_WARN (F_YELLOW B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_INFO +#define ELOG_COLOR_INFO (F_CYAN B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_DEBUG +#define ELOG_COLOR_DEBUG (F_GREEN B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_VERBOSE +#define ELOG_COLOR_VERBOSE (F_BLUE B_NULL S_NORMAL) +#endif +#endif /* ELOG_COLOR_ENABLE */ + +/* EasyLogger object */ +static EasyLogger elog; +/* every line log's buffer */ +static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 }; +/* level output info */ +static const char *level_output_info[] = { + [ELOG_LVL_ASSERT] = "A/", + [ELOG_LVL_ERROR] = "E/", + [ELOG_LVL_WARN] = "W/", + [ELOG_LVL_INFO] = "I/", + [ELOG_LVL_DEBUG] = "D/", + [ELOG_LVL_VERBOSE] = "V/", +}; + +#ifdef ELOG_COLOR_ENABLE +/* color output info */ +static const char *color_output_info[] = { + [ELOG_LVL_ASSERT] = ELOG_COLOR_ASSERT, + [ELOG_LVL_ERROR] = ELOG_COLOR_ERROR, + [ELOG_LVL_WARN] = ELOG_COLOR_WARN, + [ELOG_LVL_INFO] = ELOG_COLOR_INFO, + [ELOG_LVL_DEBUG] = ELOG_COLOR_DEBUG, + [ELOG_LVL_VERBOSE] = ELOG_COLOR_VERBOSE, +}; +#endif /* ELOG_COLOR_ENABLE */ + +static bool get_fmt_enabled(uint8_t level, size_t set); +static void elog_set_filter_tag_lvl_default(void); + +/* 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; + + if (elog.init_ok == true) { + return result; + } + + /* 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 */ + elog.output_is_locked_before_enable = false; + elog.output_is_locked_before_disable = false; + +#ifdef ELOG_COLOR_ENABLE + /* enable text color by default */ + elog_set_text_color_enabled(true); +#endif + + /* set level is ELOG_LVL_VERBOSE */ + elog_set_filter_lvl(ELOG_LVL_VERBOSE); + + /* set tag_level to default val */ + elog_set_filter_tag_lvl_default(); + + elog.init_ok = true; + + return result; +} + +/** + * EasyLogger deinitialize. + * + */ +void elog_deinit(void) { + extern ElogErrCode elog_port_deinit(void); + extern ElogErrCode elog_async_deinit(void); + + if (!elog.init_ok) { + return ; + } + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE + elog_async_deinit(); +#endif + + /* port deinitialize */ + elog_port_deinit(); + + elog.init_ok = false; +} + + +/** + * EasyLogger start after initialize. + */ +void elog_start(void) { + if (!elog.init_ok) { + return ; + } + + /* enable output */ + elog_set_output_enabled(true); + +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + elog_async_enabled(true); +#elif defined(ELOG_BUF_OUTPUT_ENABLE) + elog_buf_enabled(true); +#endif + + /* show version */ + log_i("EasyLogger V%s is initialize success.", ELOG_SW_VERSION); +} + +/** + * EasyLogger stop after initialize. + */ +void elog_stop(void) { + if (!elog.init_ok) { + return ; + } + + /* disable output */ + elog_set_output_enabled(false); + +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + elog_async_enabled(false); +#elif defined(ELOG_BUF_OUTPUT_ENABLE) + elog_buf_enabled(false); +#endif + + /* show version */ + log_i("EasyLogger V%s is deinitialize success.", ELOG_SW_VERSION); +} + + +/** + * set output enable or disable + * + * @param enabled TRUE: enable FALSE: disable + */ +void elog_set_output_enabled(bool enabled) { + ELOG_ASSERT((enabled == false) || (enabled == true)); + + elog.output_enabled = enabled; +} + +#ifdef ELOG_COLOR_ENABLE +/** + * set log text color enable or disable + * + * @param enabled TRUE: enable FALSE:disable + */ +void elog_set_text_color_enabled(bool enabled) { + ELOG_ASSERT((enabled == false) || (enabled == true)); + + elog.text_color_enabled = enabled; +} + +/** + * get log text color enable status + * + * @return enable or disable + */ +bool elog_get_text_color_enabled(void) { + return elog.text_color_enabled; +} +#endif /* ELOG_COLOR_ENABLE */ + +/** + * get output is enable or disable + * + * @return enable or disable + */ +bool elog_get_output_enabled(void) { + return elog.output_enabled; +} + +/** + * set log output format. only enable or disable + * + * @param level level + * @param set format set + */ +void elog_set_fmt(uint8_t level, size_t set) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + elog.enabled_fmt_set[level] = set; +} + +/** + * set log filter all parameter + * + * @param level level + * @param tag tag + * @param keyword keyword + */ +void elog_set_filter(uint8_t level, const char *tag, const char *keyword) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + elog_set_filter_lvl(level); + elog_set_filter_tag(tag); + elog_set_filter_kw(keyword); +} + +/** + * set log filter's level + * + * @param level level + */ +void elog_set_filter_lvl(uint8_t level) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + elog.filter.level = level; +} + +/** + * set log filter's tag + * + * @param tag tag + */ +void elog_set_filter_tag(const char *tag) { + strncpy(elog.filter.tag, tag, ELOG_FILTER_TAG_MAX_LEN); +} + +/** + * set log filter's keyword + * + * @param keyword keyword + */ +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; + } +} + +/** + * set log filter's tag level val to default + */ +static void elog_set_filter_tag_lvl_default(void) +{ + uint8_t i = 0; + + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1); + elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT; + elog.filter.tag_lvl[i].tag_use_flag = false; + } +} + +/** + * Set the filter's level by different tag. + * The log on this tag which level is less than it will stop output. + * + * example: + * // the example tag log enter silent mode + * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_SILENT); + * // the example tag log which level is less than INFO level will stop output + * elog_set_filter_tag_lvl("example", ELOG_LVL_INFO); + * // remove example tag's level filter, all level log will resume output + * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_ALL); + * + * @param tag log tag + * @param level The filter level. When the level is ELOG_FILTER_LVL_SILENT, the log enter silent mode. + * When the level is ELOG_FILTER_LVL_ALL, it will remove this tag's level filer. + * Then all level log will resume output. + * + */ +void elog_set_filter_tag_lvl(const char *tag, uint8_t level) +{ + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + ELOG_ASSERT(tag != ((void *)0)); + uint8_t i = 0; + + if (!elog.init_ok) { + return; + } + + elog_output_lock(); + /* find the tag in arr */ + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + if (elog.filter.tag_lvl[i].tag_use_flag == true && + !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){ + break; + } + } + + if (i < ELOG_FILTER_TAG_LVL_MAX_NUM){ + /* find OK */ + if (level == ELOG_FILTER_LVL_ALL){ + /* remove current tag's level filter when input level is the lowest level */ + elog.filter.tag_lvl[i].tag_use_flag = false; + memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1); + elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT; + } else{ + elog.filter.tag_lvl[i].level = level; + } + } else{ + /* only add the new tag's level filer when level is not ELOG_FILTER_LVL_ALL */ + if (level != ELOG_FILTER_LVL_ALL){ + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + if (elog.filter.tag_lvl[i].tag_use_flag == false){ + strncpy(elog.filter.tag_lvl[i].tag, tag, ELOG_FILTER_TAG_MAX_LEN); + elog.filter.tag_lvl[i].level = level; + elog.filter.tag_lvl[i].tag_use_flag = true; + break; + } + } + } + } + elog_output_unlock(); +} + +/** + * get the level on tag's level filer + * + * @param tag tag + * + * @return It will return the lowest level when tag was not found. + * Other level will return when tag was found. + */ +uint8_t elog_get_filter_tag_lvl(const char *tag) +{ + ELOG_ASSERT(tag != ((void *)0)); + uint8_t i = 0; + uint8_t level = ELOG_FILTER_LVL_ALL; + + if (!elog.init_ok) { + return level; + } + + elog_output_lock(); + /* find the tag in arr */ + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + if (elog.filter.tag_lvl[i].tag_use_flag == true && + !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){ + level = elog.filter.tag_lvl[i].level; + break; + } + } + elog_output_unlock(); + + return level; +} + +/** + * output RAW format log + * + * @param format output format + * @param ... args + */ +void elog_raw_output(const char *format, ...) { + va_list args; + size_t log_len = 0; + int fmt_result; + + /* check output enabled */ + if (!elog.output_enabled) { + return; + } + + /* args point to the first variable parameter */ + va_start(args, format); + + /* lock output */ + elog_output_lock(); + + /* package log data to buffer */ + fmt_result = vsnprintf(log_buf, ELOG_LINE_BUF_SIZE, format, args); + + /* output converted log */ + if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { + log_len = fmt_result; + } else { + log_len = ELOG_LINE_BUF_SIZE; + } + /* output log */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(uint8_t level, const char *log, size_t size); + /* raw log will using assert level */ + elog_async_output(ELOG_LVL_ASSERT, log_buf, log_len); +#elif defined(ELOG_BUF_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_output_unlock(); + + va_end(args); +} + +/** + * output the log + * + * @param level level + * @param tag tag + * @param file file name + * @param func function name + * @param line line number + * @param format output format + * @param ... args + * + */ +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 }; + va_list args; + int fmt_result; + + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + /* check output enabled */ + if (!elog.output_enabled) { + return; + } + /* level filter */ + if (level > elog.filter.level || level > elog_get_filter_tag_lvl(tag)) { + return; + } else if (!strstr(tag, elog.filter.tag)) { /* tag filter */ + return; + } + /* args point to the first variable parameter */ + va_start(args, format); + /* lock output */ + elog_output_lock(); + +#ifdef ELOG_COLOR_ENABLE + /* add CSI start sign and color info */ + if (elog.text_color_enabled) { + log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START); + log_len += elog_strcpy(log_len, log_buf + log_len, color_output_info[level]); + } +#endif + + /* package level info */ + if (get_fmt_enabled(level, ELOG_FMT_LVL)) { + log_len += elog_strcpy(log_len, log_buf + log_len, level_output_info[level]); + } + /* package tag info */ + if (get_fmt_enabled(level, ELOG_FMT_TAG)) { + log_len += elog_strcpy(log_len, log_buf + log_len, tag); + /* if the tag length is less than 50% ELOG_FILTER_TAG_MAX_LEN, then fill space */ + if (tag_len <= ELOG_FILTER_TAG_MAX_LEN / 2) { + memset(tag_sapce, ' ', ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len); + log_len += elog_strcpy(log_len, log_buf + log_len, tag_sapce); + } + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + /* package time, process and thread info */ + if (get_fmt_enabled(level, ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, "["); + /* package time info */ + if (get_fmt_enabled(level, ELOG_FMT_TIME)) { + log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_time()); + if (get_fmt_enabled(level, ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + /* package process info */ + if (get_fmt_enabled(level, ELOG_FMT_P_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_p_info()); + if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + /* package thread info */ + if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_t_info()); + } + log_len += elog_strcpy(log_len, log_buf + log_len, "] "); + } + /* package file directory and name, function name and line number info */ + if (get_fmt_enabled(level, ELOG_FMT_DIR | ELOG_FMT_FUNC | ELOG_FMT_LINE)) { + log_len += elog_strcpy(log_len, log_buf + log_len, "("); + /* package file info */ + if (get_fmt_enabled(level, ELOG_FMT_DIR)) { + log_len += elog_strcpy(log_len, log_buf + log_len, file); + if (get_fmt_enabled(level, ELOG_FMT_FUNC)) { + log_len += elog_strcpy(log_len, log_buf + log_len, ":"); + } else if (get_fmt_enabled(level, ELOG_FMT_LINE)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + /* package line info */ + if (get_fmt_enabled(level, ELOG_FMT_LINE)) { + snprintf(line_num, ELOG_LINE_NUM_MAX_LEN, "%ld", line); + log_len += elog_strcpy(log_len, log_buf + log_len, line_num); + if (get_fmt_enabled(level, ELOG_FMT_FUNC)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + /* package func info */ + if (get_fmt_enabled(level, ELOG_FMT_FUNC)) { + log_len += elog_strcpy(log_len, log_buf + log_len, func); + + } + 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_LINE_BUF_SIZE - log_len, format, args); + + va_end(args); + /* calculate log length */ + if ((log_len + fmt_result <= ELOG_LINE_BUF_SIZE) && (fmt_result > -1)) { + log_len += fmt_result; + } else { + /* using max length */ + log_len = ELOG_LINE_BUF_SIZE; + } + /* overflow check and reserve some space for CSI end sign and newline sign */ +#ifdef ELOG_COLOR_ENABLE + if (log_len + (sizeof(CSI_END) - 1) + newline_len > ELOG_LINE_BUF_SIZE) { + /* using max length */ + log_len = ELOG_LINE_BUF_SIZE; + /* reserve some space for CSI end sign */ + log_len -= (sizeof(CSI_END) - 1); +#else + if (log_len + newline_len > ELOG_LINE_BUF_SIZE) { + /* using max length */ + log_len = ELOG_LINE_BUF_SIZE; +#endif /* ELOG_COLOR_ENABLE */ + /* reserve some space for newline sign */ + log_len -= newline_len; + } + /* keyword filter */ + if (elog.filter.keyword[0] != '\0') { + /* add string end sign */ + log_buf[log_len] = '\0'; + /* find the keyword */ + if (!strstr(log_buf, elog.filter.keyword)) { + /* unlock output */ + elog_output_unlock(); + return; + } + } + +#ifdef ELOG_COLOR_ENABLE + /* add CSI end sign */ + if (elog.text_color_enabled) { + log_len += elog_strcpy(log_len, log_buf + log_len, CSI_END); + } +#endif + + /* package newline sign */ + log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); + /* output log */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(uint8_t level, const char *log, size_t size); + elog_async_output(level, log_buf, log_len); +#elif defined(ELOG_BUF_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_output_unlock(); +} + +/** + * get format enabled + * + * @param level level + * @param set format set + * + * @return enable or disable + */ +static bool get_fmt_enabled(uint8_t level, size_t set) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + if (elog.enabled_fmt_set[level] & set) { + return true; + } else { + return false; + } +} + +/** + * enable or disable logger output lock + * @note disable this lock is not recommended except you want output system exception log + * + * @param enabled true: enable false: disable + */ +void elog_output_lock_enabled(bool enabled) { + elog.output_lock_enabled = enabled; + /* it will re-lock or re-unlock before output lock enable */ + if (elog.output_lock_enabled) { + if (!elog.output_is_locked_before_disable && elog.output_is_locked_before_enable) { + /* the output lock is unlocked before disable, and the lock will unlocking after enable */ + elog_port_output_lock(); + } else if (elog.output_is_locked_before_disable && !elog.output_is_locked_before_enable) { + /* the output lock is locked before disable, and the lock will locking after enable */ + elog_port_output_unlock(); + } + } +} + +/** + * Set a hook function to EasyLogger assert. It will run when the expression is false. + * + * @param hook the hook function + */ +void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_t line)) { + elog_assert_hook = hook; +} + +/** + * find the log level + * @note make sure the log level is output on each format + * + * @param log log buffer + * + * @return log level, found failed will return -1 + */ +int8_t elog_find_lvl(const char *log) { + ELOG_ASSERT(log); + /* make sure the log level is output on each format */ + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_ASSERT] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_ERROR] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_WARN] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_INFO] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_DEBUG] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_VERBOSE] & ELOG_FMT_LVL); + +#ifdef ELOG_COLOR_ENABLE + uint8_t i; + size_t csi_start_len = strlen(CSI_START); + for(i = 0; i < ELOG_LVL_TOTAL_NUM; i ++) { + if (!strncmp(color_output_info[i], log + csi_start_len, strlen(color_output_info[i]))) { + return i; + } + } + /* found failed */ + return -1; +#else + switch (log[0]) { + case 'A': return ELOG_LVL_ASSERT; + case 'E': return ELOG_LVL_ERROR; + case 'W': return ELOG_LVL_WARN; + case 'I': return ELOG_LVL_INFO; + case 'D': return ELOG_LVL_DEBUG; + case 'V': return ELOG_LVL_VERBOSE; + default: return -1; + } +#endif +} + +/** + * find the log tag + * @note make sure the log tag is output on each format + * @note the tag don't have space in it + * + * @param log log buffer + * @param lvl log level, you can get it by @see elog_find_lvl + * @param tag_len found tag length + * + * @return log tag, found failed will return NULL + */ +const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len) { + const char *tag = NULL, *tag_end = NULL; + + ELOG_ASSERT(log); + ELOG_ASSERT(tag_len); + ELOG_ASSERT(lvl < ELOG_LVL_TOTAL_NUM); + /* make sure the log tag is output on each format */ + ELOG_ASSERT(elog.enabled_fmt_set[lvl] & ELOG_FMT_TAG); + +#ifdef ELOG_COLOR_ENABLE + tag = log + strlen(CSI_START) + strlen(color_output_info[lvl]) + strlen(level_output_info[lvl]); +#else + tag = log + strlen(level_output_info[lvl]); +#endif + /* find the first space after tag */ + if ((tag_end = memchr(tag, ' ', ELOG_FILTER_TAG_MAX_LEN)) != NULL) { + *tag_len = tag_end - tag; + } else { + tag = NULL; + } + + return tag; +} + +/** + * dump the hex format data to log + * + * @param name name for hex object, it will show on log header + * @param width hex number for every line, such as: 16, 32 + * @param buf hex buffer + * @param size buffer size + */ +void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size) +{ +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') + + uint16_t i, j; + uint16_t log_len = 0; + const uint8_t *buf_p = buf; + char dump_string[8] = {0}; + int fmt_result; + + if (!elog.output_enabled) { + return; + } + + /* level filter */ + if (ELOG_LVL_DEBUG > elog.filter.level) { + return; + } else if (!strstr(name, elog.filter.tag)) { /* tag filter */ + return; + } + + /* lock output */ + elog_output_lock(); + + for (i = 0; i < size; i += width) { + /* package header */ + fmt_result = snprintf(log_buf, ELOG_LINE_BUF_SIZE, "D/HEX %s: %04X-%04X: ", name, i, i + width - 1); + /* calculate log length */ + if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { + log_len = fmt_result; + } else { + log_len = ELOG_LINE_BUF_SIZE; + } + /* dump hex */ + for (j = 0; j < width; j++) { + if (i + j < size) { + snprintf(dump_string, sizeof(dump_string), "%02X ", buf_p[i + j]); + } else { + strncpy(dump_string, " ", sizeof(dump_string)); + } + log_len += elog_strcpy(log_len, log_buf + log_len, dump_string); + if ((j + 1) % 8 == 0) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + /* dump char for hex */ + for (j = 0; j < width; j++) { + if (i + j < size) { + snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf_p[i + j]) ? buf_p[i + j] : '.'); + log_len += elog_strcpy(log_len, log_buf + log_len, dump_string); + } + } + /* overflow check and reserve some space for newline sign */ + if (log_len + strlen(ELOG_NEWLINE_SIGN) > ELOG_LINE_BUF_SIZE) { + log_len = ELOG_LINE_BUF_SIZE - strlen(ELOG_NEWLINE_SIGN); + } + /* package newline sign */ + log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); + /* do log output */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(uint8_t level, const char *log, size_t size); + elog_async_output(ELOG_LVL_DEBUG, log_buf, log_len); +#elif defined(ELOG_BUF_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_output_unlock(); +} diff --git a/src/elog_async.c b/src/elog_async.c new file mode 100644 index 0000000000000000000000000000000000000000..95db74ba7c4ff87c4904d52099eddb20968da3e5 --- /dev/null +++ b/src/elog_async.c @@ -0,0 +1,388 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2016-2017, 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 + +#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_LINE_OUTPUT +#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE +#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE (ELOG_ASYNC_OUTPUT_BUF_SIZE - 4) +#endif +#else +#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE +#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE (ELOG_LINE_BUF_SIZE - 4) +#endif +#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 /* ELOG_ASYNC_OUTPUT_ENABLE */ + +/* the highest output level for async mode, other level will sync output */ +#ifdef ELOG_ASYNC_OUTPUT_LVL +#define OUTPUT_LVL ELOG_ASYNC_OUTPUT_LVL +#else +#define OUTPUT_LVL ELOG_LVL_ASSERT +#endif /* ELOG_ASYNC_OUTPUT_LVL */ + +/* buffer size for asynchronous output mode */ +#ifdef ELOG_ASYNC_OUTPUT_BUF_SIZE +#define OUTPUT_BUF_SIZE ELOG_ASYNC_OUTPUT_BUF_SIZE +#else +#define OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) +#endif /* ELOG_ASYNC_OUTPUT_BUF_SIZE */ + +/* Initialize OK flag */ +static bool init_ok = false; +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD +/* thread running flag */ +static bool thread_running = false; +#endif +/* asynchronous output mode enabled flag */ +static bool is_enabled = false; +/* asynchronous output mode's ring buffer */ +static char log_buf[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 OUTPUT_BUF_SIZE - (read_index - write_index); + } else if (buf_is_full) { + return OUTPUT_BUF_SIZE; + } else { + return 0; + } + } +} + +/** + * asynchronous output ring buffer remain space + * + * @return remain space + */ +static size_t async_get_buf_space(void) { + return 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 < OUTPUT_BUF_SIZE) { + memcpy(log_buf + write_index, log, size); + write_index += size; + } else { + memcpy(log_buf + write_index, log, OUTPUT_BUF_SIZE - write_index); + memcpy(log_buf, log + OUTPUT_BUF_SIZE - write_index, + size - (OUTPUT_BUF_SIZE - write_index)); + write_index += size - OUTPUT_BUF_SIZE; + } + + buf_is_empty = false; + +__exit: + + return size; +} + +#ifdef ELOG_ASYNC_LINE_OUTPUT +/** + * Get line log from asynchronous output ring buffer. + * It will copy all log when the newline sign isn't find. + * + * @param log get line log buffer + * @param size line log size + * + * @return get line log size, the log size is less than ring buffer used size + */ +size_t elog_async_get_line_log(char *log, size_t size) { + size_t used = 0, cpy_log_size = 0; + /* lock output */ + elog_output_lock(); + used = elog_async_get_buf_used(); + + /* no log */ + if (!used || !size) { + goto __exit; + } + /* less log */ + if (used <= size) { + size = used; + } + + if (read_index + size < OUTPUT_BUF_SIZE) { + cpy_log_size = elog_cpyln(log, log_buf + read_index, size); + read_index += cpy_log_size; + } else { + cpy_log_size = elog_cpyln(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index); + if (cpy_log_size == OUTPUT_BUF_SIZE - read_index) { + cpy_log_size += elog_cpyln(log + cpy_log_size, log_buf, size - cpy_log_size); + read_index += cpy_log_size - OUTPUT_BUF_SIZE; + } else { + read_index += cpy_log_size; + } + } + + if (used == cpy_log_size) { + buf_is_empty = true; + } + + if (cpy_log_size) { + buf_is_full = false; + } + +__exit: + /* lock output */ + elog_output_unlock(); + return cpy_log_size; +} +#else +/** + * 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) { + size = 0; + goto __exit; + } + /* less log */ + if (used <= size) { + size = used; + buf_is_empty = true; + } + + if (read_index + size < OUTPUT_BUF_SIZE) { + memcpy(log, log_buf + read_index, size); + read_index += size; + } else { + memcpy(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index); + memcpy(log + OUTPUT_BUF_SIZE - read_index, log_buf, + size - (OUTPUT_BUF_SIZE - read_index)); + read_index += size - OUTPUT_BUF_SIZE; + } + + buf_is_full = false; + +__exit: + /* lock output */ + elog_output_unlock(); + return size; +} +#endif /* ELOG_ASYNC_LINE_OUTPUT */ + +void elog_async_output(uint8_t level, 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; + + if (is_enabled) { + if (level >= OUTPUT_LVL) { + put_size = async_put_log(log, size); + /* notify output log thread */ + if (put_size > 0) { + elog_async_output_notice(); + } + } else { + elog_port_output(log, size); + } + } else { + elog_port_output(log, size); + } +} + +#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]; + + while(thread_running) { + /* waiting log */ + sem_wait(&output_notice); + /* polling gets and outputs the log */ + while(true) { + +#ifdef ELOG_ASYNC_LINE_OUTPUT + get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE); +#else + get_log_size = elog_async_get_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE); +#endif + + if (get_log_size) { + elog_port_output(poll_get_buf, get_log_size); + } else { + break; + } + } + } + return NULL; +} +#endif + +/** + * enable or disable asynchronous output mode + * the log will be output directly when mode is disabled + * + * @param enabled true: enabled, false: disabled + */ +void elog_async_enabled(bool enabled) { + is_enabled = enabled; +} + +/** + * 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); + + thread_running = true; + + 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; +} + +/** + * asynchronous output mode deinitialize + * + */ +void elog_async_deinit(void) { + if (!init_ok) { + return ; + } + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD + thread_running = false; + + elog_async_output_notice(); + + pthread_join(async_output_thread, NULL); + + sem_destroy(&output_notice); +#endif + + init_ok = false; +} + + + +#endif /* ELOG_ASYNC_OUTPUT_ENABLE */ diff --git a/src/elog_buf.c b/src/elog_buf.c new file mode 100644 index 0000000000000000000000000000000000000000..c4bd9e719f8f3595ed823bb2920b805e1bfe62e9 --- /dev/null +++ b/src/elog_buf.c @@ -0,0 +1,105 @@ +/* + * 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; +/* buffered output mode enabled flag */ +static bool is_enabled = false; + +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; + + if (!is_enabled) { + elog_port_output(log, size); + return; + } + + 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; + /* output log */ + elog_port_output(log_buf, ELOG_BUF_OUTPUT_BUF_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) { + if (buf_write_size == 0) + return; + /* 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(); +} + +/** + * enable or disable buffered output mode + * the log will be output directly when mode is disabled + * + * @param enabled true: enabled, false: disabled + */ +void elog_buf_enabled(bool enabled) { + is_enabled = enabled; +} +#endif /* ELOG_BUF_OUTPUT_ENABLE */ diff --git a/src/elog_utils.c b/src/elog_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..165d855d2c8e8470b3543380ca6064c0799a8a44 --- /dev/null +++ b/src/elog_utils.c @@ -0,0 +1,103 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2018, 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: Some utils for this library. + * Created on: 2015-04-28 + */ + +#include +#include + +/** + * another copy string function + * + * @param cur_len current copied log length, max size is ELOG_LINE_BUF_SIZE + * @param dst destination + * @param src source + * + * @return copied length + */ +size_t elog_strcpy(size_t cur_len, char *dst, const char *src) { + const char *src_old = src; + + assert(dst); + assert(src); + + while (*src != 0) { + /* make sure destination has enough space */ + if (cur_len++ < ELOG_LINE_BUF_SIZE) { + *dst++ = *src++; + } else { + break; + } + } + return src - src_old; +} + +/** + * Copy line log split by newline sign. It will copy all log when the newline sign isn't find. + * + * @param line line log buffer + * @param log origin log buffer + * @param len origin log buffer length + * + * @return copy size + */ +size_t elog_cpyln(char *line, const char *log, size_t len) { + size_t newline_len = strlen(ELOG_NEWLINE_SIGN), copy_size = 0; + + assert(line); + assert(log); + + while (len--) { + *line++ = *log++; + copy_size++; + if (copy_size >= newline_len && !strncmp(log - newline_len, ELOG_NEWLINE_SIGN, newline_len)) { + break; + } + } + return copy_size; +} + +/** + * This function will copy memory content from source address to destination + * address. + * + * @param dst the address of destination memory + * @param src the address of source memory + * @param count the copied length + * + * @return the address of destination memory + */ +void *elog_memcpy(void *dst, const void *src, size_t count) { + char *tmp = (char *) dst, *s = (char *) src; + + assert(dst); + assert(src); + + while (count--) + *tmp++ = *s++; + + return dst; +}