• 【嵌入式开源库】EasyLogger的使用, 一款轻量级且高性能的日志库


    简介

    EasyLogger 是一款超轻量级 、高性能的 C 日志库,非常适合对资源敏感的软件项目,例如:IoT 产品、可穿戴设备、智能家居等等。相比 log4c、zlog 这些知名的 C 日志库,EasyLogger 的功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件形式进行动态扩展。

    本章使用环境:

    正点原子stm32407探索者开发板、工程模板:HAL库 - 实验4 串口通信实验

    下载

    easylogger项目托管在Github,下载地址: https://github.com/armink/EasyLogger
    在这里插入图片描述

    也可以通过git工具进行项目克隆(保证自己电脑配好git环境)

    git clone https://github.com/armink/EasyLogger.git
    
    • 1

    移植

    在这里插入图片描述
    将下载的源码拖直接拖动到工程中,然后添加.C文件到keil工程中

    port/elog_port.c:elog移植接口文件;
    src/elog.c:elog核心功能源码;
    src/elog_utils.c:elog所用到的一些c库工具函数实现;
    src/elog_buf.c(可选添加):elog缓冲输出模式源码;
    src/elog_async.c(可选添加):elog异步输出模式源码;
    在这里插入图片描述
    勾选支持C99模式
    这里是引用

    在这里插入图片描述
    然后编译工程遇到错误

    …\easylogger\src\elog_async.c(35): error: #5: cannot open source input file “pthread.h”: No such file or directory

    然后我们点击错误到达错误的位置,注释掉这两个宏定义开关然后再次编译,然后我们需要配置一下easylogger需要用到的接口函数;在这里插入图片描述
    我们可以打开easylogger源代码下的demo文件夹里面的stm32模板工程,复制他的port文件到我们的工程中来,然后修改头文件包含即可
    在这里插入图片描述

    /*
     * 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 non-os stm32f10x.
     * Created on: 2015-04-28
     */
    
    #include "elog.h"
    #include 
    #include 
    
    /**
     * EasyLogger port initialize
     *
     * @return result
     */
    ElogErrCode elog_port_init(void) {
        ElogErrCode result = ELOG_NO_ERR;
    
        return result;
    }
    
    /**
     * EasyLogger port deinit
     */
    void elog_port_deinit(void)
    {
        
    }
    
    /**
     * output log port interface
     *
     * @param log output of log
     * @param size log size
     */
    void elog_port_output(const char *log, size_t size) {
        /* output to terminal */
        printf("%.*s", size, log);
        //TODO output to flash
    }
    
    /**
     * output lock
     */
    void elog_port_output_lock(void) {
        __disable_irq();
    }
    
    /**
     * output unlock
     */
    void elog_port_output_unlock(void) {
        __enable_irq();
    }
    
    /**
     * get current time interface
     *
     * @return current time
     */
    const char *elog_port_get_time(void) {
        return "10:08:12";
    }
    
    /**
     * get current process name interface
     *
     * @return current process name
     */
    const char *elog_port_get_p_info(void) {
        return "pid:1008";
    }
    
    /**
     * get current thread name interface
     *
     * @return current thread name
     */
    const char *elog_port_get_t_info(void) {
        return "tid:24";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    到这里工程移植就完成了

    使用

    更加详细的使用接口可用参考源码目录下的docs的参考文档,以下内容均取之与该项目文档;

    初始化

    初始化的 EasyLogger 的核心功能,初始化后才可以使用下面的API。

    ElogErrCode elog_init(void)
    
    • 1

    启动

    注意:在初始化完成后,必须调用启动方法,日志才会被输出。

    void elog_start(void)
    
    • 1

    输出日志

    所有日志的级别关系大小如下:

    级别 标识 描述
    0    [A]  断言(Assert)
    1    [E]  错误(Error)
    2    [W]  警告(Warn)
    3    [I]  信息(Info)
    4    [D]  调试(Debug)
    5    [V]  详细(Verbose)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    输出基本日志

    所有级别的日志输出方法如下,每种级别都有两种简化方式,用户可以自行选择。

    #define elog_assert(tag, ...) 
    #define elog_a(tag, ...) //简化方式1,每次需填写 LOG_TAG
    #define log_a(...)       //简化方式2,LOG_TAG 已经在文件顶部定义,使用前无需填写 LOG_TAG
    
    #define elog_error(tag, ...)
    #define elog_e(tag, ...)
    #define log_e(...)
    
    #define elog_warn(tag, ...)
    #define elog_w(tag, ...)
    #define log_w(...)
    
    #define elog_info(tag, ...)
    #define elog_i(tag, ...)
    #define log_i(...)
    
    #define elog_debug(tag, ...)
    #define elog_d(tag, ...)
    #define log_d(...)
    
    #define elog_verbose(tag, ...)
    #define elog_v(tag, ...)
    #define log_v(...)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    参数描述
    tag日志标签
    不定参格式,与printf入参一致,放入将要输出日志

    技巧一 :对于每个源代码文件,可以在引用 elog.h 上方,根据模块的不同功能,定义不同的日志标签,如下所示,这样既可直接使用 log_x 这类无需输入标签的简化方式 API 。

    //WiFi 协议处理(位于 /wifi/proto.c 源代码文件)
    #define LOG_TAG    "wifi.proto"
    
    #include 
    
    log_e("我是 wifi.proto 日志");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //WiFi 数据打包处理(位于 /wifi/package.c 源代码文件)
    #define LOG_TAG    "wifi.package"
    
    #include 
    
    log_w("我是 wifi.package 日志");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //CAN 命令解析(位于 /can/disp.c 源代码文件)
    #define LOG_TAG    "can.disp"
    
    #include 
    
    log_w("我是 can.disp 日志");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    技巧二 :为了实现按照模块、子模块作用域来限制日志输出级别的功能,可以按照下面的方式,在模块的头文件中定义以下宏定义:

    /**
     * Log default configuration for EasyLogger.
     * NOTE: Must defined before including the 
     */
    #if !defined(LOG_TAG)
        #define LOG_TAG                    "xx"
    #endif
    #undef LOG_LVL
    #if defined(XX_LOG_LVL)
        #define LOG_LVL                    XX_LOG_LVL
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    XX 是模块名称的缩写,该段内容务必定义在 elog.h 之前,否则失效;这样做的 好处 是,如果模块内的源文件没有定义 TAG ,则会自动引用该段内容中的定义的 TAG 。同时可以在 头文件中可以配置 XX_LOG_LVL ,这样只会输出比这个优先级高或相等级别的日志。当然 XX_LOG_LVL 这个宏也可以不定义,此时会输出全部级别的日志,定义为 ASSERT 级别,就只剩断言信息了。
    此时我们就能够实现 源文件->子模块->模块->EasyLogger全局 对于其中任何环节的日志配置及控制。调试时想要查看其中任何环节的日志,或者调整其中的某个环节日志级别,都会非常轻松,极大的提高了调试的灵活性及效率。

    使能/失能日志输出
    void elog_set_output_enabled(bool enabled)
    
    • 1
    使能/失能日志输出锁

    默认为使能状态,当系统或MCU进入异常后,需要输出异常日志时,就必须失能日志输出锁,来保证异常日志能够被正常输出。

    void elog_output_lock_enabled(bool enabled)
    
    • 1
    参数描述
    enabledtrue: 使能,false: 失能

    日志格式及样式

    设置日志格式

    每种级别可对应一种日志输出格式,日志的输出内容位置顺序固定,只可定义开启或关闭某子内容。可设置的日志子内容包括:级别、标签、时间、进程信息、线程信息、文件路径、行号、方法名。

    注:默认为 RAW格式

    void elog_set_fmt(uint8_t level, size_t set)
    
    • 1
    参数描述
    level级别
    set格式集合

    例子:

    /* 断言:输出所有内容 */
    elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
    /* 错误:输出级别、标签和时间 */
    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_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    使能/失能日志颜色

    日志颜色功能是将各个级别日志按照颜色进行区分,默认颜色功能是关闭的。

    void elog_set_text_color_enabled(bool enabled)
    
    • 1
    参数描述
    enabledtrue: 使能,false: 失能
    颜色修改

    每个级别的日志均有默认颜色。如果想修改,请先查看在 elog.c 的头部定义的各种颜色及字体风格,这里以修改 VERBOSE 级别日志来举例:

    首先选择前景色为白色,再选择背景色为黑色,最后字体风格为粗体

    那么最终的配置如下:

    #define ELOG_COLOR_VERBOSE         (F_WHITE B_BLACK S_BOLD)
    
    • 1
    • 操作方法:增加并修改ELOG_COLOR_VERBOSE宏对应值即可,其他级别日志颜色的修改以此类推
      在这里插入图片描述

    配置文件说明

    elog_cfg.h

    // 开启输出使能
    #define ELOG_OUTPUT_ENABLE
    /*设置静态输出日志级别。范围:从ELOG_LVL_ASSERT到ELOG-LVL-VERBOSE*/
    #define ELOG_OUTPUT_LVL                          ELOG_LVL_VERBOSE
    // 开启断言检测
    #define ELOG_ASSERT_ENABLE
    /*每行日志的缓冲区大小*/
    #define ELOG_LINE_BUF_SIZE                       1024
    /*输出行号最大长度*/
    #define ELOG_LINE_NUM_MAX_LEN                    5
    /* output filter's tag max length */
    #define ELOG_FILTER_TAG_MAX_LEN                  30
    /*输出过滤器的标记最大长度*/
    #define ELOG_FILTER_KW_MAX_LEN                   16
    /*输出过滤器的标记级别最大值*/
    #define ELOG_FILTER_TAG_LVL_MAX_NUM              5
    // 换行符定义
    #define ELOG_NEWLINE_SIGN                        "\r\n"
    /*---------------------------------------------------------------------------*/
    // 开启日志颜色出书
    #define ELOG_COLOR_ENABLE
    /*如果需要,请将某些级别日志更改为非默认颜色*/
    #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)
    /*---------------------------------------------------------------------------*/
    // 开启异步输出模式
    // #define ELOG_ASYNC_OUTPUT_ENABLE
    /*异步模式的最高输出级别,其他级别将同步输出*/
    #define ELOG_ASYNC_OUTPUT_LVL                    ELOG_LVL_ASSERT
    /*异步输出模式的缓冲区大小*/
    #define ELOG_ASYNC_OUTPUT_BUF_SIZE               (ELOG_LINE_BUF_SIZE * 10)
    /*每个异步输出的日志必须以换行符结尾*/
    #define ELOG_ASYNC_LINE_OUTPUT
    /*使用POSIX pthread实现的异步输出模式*/
    #define ELOG_ASYNC_OUTPUT_USING_PTHREAD
    /*---------------------------------------------------------------------------*/
    // 启用缓冲输出模式
    // #define ELOG_BUF_OUTPUT_ENABLE
    /*缓冲输出模式的缓冲区大小 :如果是在逻辑程序上使用该方法这里就会出现输出10行串口才开始打印*/
    #define ELOG_BUF_OUTPUT_BUF_SIZE                 (ELOG_LINE_BUF_SIZE * 10)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    实际使用案例

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    
    //开头添加
    #include 
    #include "elog.h"
    
    #define LOG_TAG    "main"
    
    int main(void)
    {
    	u16 times=0;
        HAL_Init();                    	//初始化HAL库    
        Stm32_Clock_Init(336,8,2,7);  	//设置时钟,168Mhz
    	delay_init(168);               	//初始化延时函数
    	uart_init(115200);              //初始化USART
    	LED_Init();						//初始化LED	
        /* 初始化elog */
        elog_init();
        
        elog_set_text_color_enabled(true);
    
        /* 设置每个级别的日志输出格式 */
        //输出所有内容
        elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
        //输出日志级别信息和日志TAG
        elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG);
        elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG);
        elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG);
        //除了时间、进程信息、线程信息之外,其余全部输出
        elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO));
        //输出所有内容
        elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL);
    
        /* 启动elog */
        elog_start();
    	
        while(1)
        {			
            times++;
            if(times%30==0)
            {
                LED0=!LED0;//闪烁LED,提示系统正在运行.
                printf("hello\r\n");
                log_a("Hello EasyLogger!");
                log_e("Hello EasyLogger!");
                log_w("Hello EasyLogger!");
                log_i("Hello EasyLogger!");
                log_d("Hello EasyLogger!");
                log_v("Hello EasyLogger!");
    
            }
            delay_ms(10);   
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    在这里插入图片描述
    在这里插入图片描述
    关于该库的实现底层原理可以参考我之前写的这一篇文章C语言使用宏定义实现等级调试输出PRINT_LEVEL

  • 相关阅读:
    Vue2速成手册(原创不易,转载请注明出处)
    Android native层实现MediaCodec编码H264/HEVC
    Mac 多版本jdk安装与切换
    数据结构(二)顺序表和链表
    设计模式之代理模式
    关于自己DIY配置电脑
    两个妙招教你怎么拍照识别植物,增长见识
    Nginx 学习
    概率论的学习和整理--番外10:两女孩问题,3种题目文本和对应解答
    美团外卖优惠券小程序 美团优惠券微信小程序 自带流量主模式 带教程
  • 原文地址:https://blog.csdn.net/qq_43581670/article/details/126249911