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

简介: 【嵌入式开源库】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

移植

将下载的源码拖直接拖动到工程中,然后添加.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, <armink.ztl@gmail.com>
 *
 * 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 <stdio.h>
#include <sys.h>
/**
 * 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";
}

到这里工程移植就完成了

使用

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

初始化

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

ElogErrCode elog_init(void)

启动

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

void elog_start(void)

输出日志

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

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

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

#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(...)
参数 描述
tag 日志标签
不定参格式,与printf入参一致,放入将要输出日志

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

//WiFi 协议处理(位于 /wifi/proto.c 源代码文件)
#define LOG_TAG    "wifi.proto"
#include <elog.h>
log_e("我是 wifi.proto 日志");
//WiFi 数据打包处理(位于 /wifi/package.c 源代码文件)
#define LOG_TAG    "wifi.package"
#include <elog.h>
log_w("我是 wifi.package 日志");
//CAN 命令解析(位于 /can/disp.c 源代码文件)
#define LOG_TAG    "can.disp"
#include <elog.h>
log_w("我是 can.disp 日志");

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

/**
 * Log default configuration for EasyLogger.
 * NOTE: Must defined before including the <elog.h>
 */
#if !defined(LOG_TAG)
    #define LOG_TAG                    "xx"
#endif
#undef LOG_LVL
#if defined(XX_LOG_LVL)
    #define LOG_LVL                    XX_LOG_LVL
#endif

XX 是模块名称的缩写,该段内容务必定义在 elog.h 之前,否则失效;这样做的 好处 是,如果模块内的源文件没有定义 TAG ,则会自动引用该段内容中的定义的 TAG 。同时可以在 头文件中可以配置 XX_LOG_LVL ,这样只会输出比这个优先级高或相等级别的日志。当然 XX_LOG_LVL 这个宏也可以不定义,此时会输出全部级别的日志,定义为 ASSERT 级别,就只剩断言信息了。

此时我们就能够实现 源文件->子模块->模块->EasyLogger全局 对于其中任何环节的日志配置及控制。调试时想要查看其中任何环节的日志,或者调整其中的某个环节日志级别,都会非常轻松,极大的提高了调试的灵活性及效率。

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

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

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

日志格式及样式

设置日志格式

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

注:默认为 RAW格式

void elog_set_fmt(uint8_t level, size_t set)
参数 描述
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);
使能/失能日志颜色

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

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

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

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

那么最终的配置如下:

#define ELOG_COLOR_VERBOSE         (F_WHITE B_BLACK S_BOLD)
  • 操作方法:增加并修改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)

实际使用案例

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
//开头添加
#include <stdio.h>
#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);   
    }
}

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


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
5天前
|
Oracle 关系型数据库 MySQL
实时计算 Flink版产品使用合集之是否支持从库归档日志捕获数据
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
11天前
|
监控 安全 API
orhanobut/logger - 强大的Android日志打印库
orhanobut/logger - 强大的Android日志打印库
27 1
|
12天前
|
运维 监控 Go
Golang深入浅出之-Go语言中的日志记录:log与logrus库
【4月更文挑战第27天】本文比较了Go语言中标准库`log`与第三方库`logrus`的日志功能。`log`简单但不支持日志级别配置和多样化格式,而`logrus`提供更丰富的功能,如日志级别控制、自定义格式和钩子。文章指出了使用`logrus`时可能遇到的问题,如全局logger滥用、日志级别设置不当和过度依赖字段,并给出了避免错误的建议,强调理解日志级别、合理利用结构化日志、模块化日志管理和定期审查日志配置的重要性。通过这些实践,开发者能提高应用监控和故障排查能力。
95 1
|
12天前
|
SQL 调度 Swift
【深入浅出】阿里自研开源搜索引擎Havenask日志查询
本次分享内容为Havenask的日志查询,文章包含了具体查询步骤和举例、实操演示,希望可以帮助大家更好的使用Havenask。
54999 0
|
12天前
|
C++
glog --- C++日志库
glog --- C++日志库
|
10天前
|
关系型数据库 MySQL 数据库
mysql数据库bin-log日志管理
mysql数据库bin-log日志管理
|
11天前
|
存储 关系型数据库 数据库
关系型数据库文件方式存储LOG FILE(日志文件)
【5月更文挑战第11天】关系型数据库文件方式存储LOG FILE(日志文件)
69 1
|
11天前
|
运维 监控 安全
Java一分钟之-Log4j与日志记录的重要性
【5月更文挑战第16天】Log4j是Java常用的日志框架,用于灵活地记录程序状态和调试问题。通过设置日志级别和过滤器,可避免日志输出混乱。为防止日志文件过大,可配置滚动策略。关注日志安全性,如Log4j 2.x的CVE-2021-44228漏洞,及时更新至安全版本。合理使用日志能提升故障排查和系统监控效率。
70 0
|
12天前
|
C++
JNI Log 日志输出
JNI Log 日志输出
55 1
|
12天前
|
存储 运维 大数据
聊聊日志硬扫描,阿里 Log Scan 的设计与实践
泛日志(Log/Trace/Metric)是大数据的重要组成,伴随着每一年业务峰值的新脉冲,日志数据量在快速增长。同时,业务数字化运营、软件可观测性等浪潮又在对日志的存储、计算提出更高的要求。
262 6

热门文章

最新文章