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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【嵌入式开源库】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日志并进行多维度分析。
相关文章
|
3月前
|
前端开发 C语言 开发者
领导被我的花式console.log吸引了!直接写入公司公共库!
【8月更文挑战第23天】领导被我的花式console.log吸引了!直接写入公司公共库!
40 2
领导被我的花式console.log吸引了!直接写入公司公共库!
|
2月前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
177 1
|
2月前
|
存储 运维 监控
超级好用的C++实用库之日志类
超级好用的C++实用库之日志类
32 0
|
3月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
3月前
|
存储 JSON 前端开发
一文搞懂 Go 1.21 的日志标准库 - slog
一文搞懂 Go 1.21 的日志标准库 - slog
82 2
|
3月前
|
JSON Go API
一文搞懂 Golang 高性能日志库 - Zap
一文搞懂 Golang 高性能日志库 - Zap
155 2
|
21天前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
134 3
|
3月前
|
Kubernetes Ubuntu Windows
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
118 3
|
21天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1585 14
|
16天前
|
Python
log日志学习
【10月更文挑战第9天】 python处理log打印模块log的使用和介绍
19 0