• 动手捣鼓一个log打印调试模块


    平时写代码时,总是需要用到打印调试输出,之前每次都是直接使用 printf 函数直接打印的,但是越来越发现很不方便。比如有时我想屏蔽打印调试信息时,总不能回去一点点的把原来的打印代码删除了吧。有时我需要分级别打印时,直接使用 printf 函数打印也不好操作。

    在网上我发现了一个打印调试的日志库 EasyLogger ,这个 log 库的功能非常强大,可以满足各种需求了。到时我写文章记录下使用心得。

    但是,我其实又不用这么强大的功能,虽然 EasyLogger 移植使用起来也不难,但自己所需要的功能也不用那么多。于是就自己捣鼓一下 log 的调试输出代码。

    log打印调试代码

    我大概需要的功能就是:

    • 可以打开、关闭调试输出信息
    • 输出信息有过滤机制,比如只打印报错、警告信息,过滤掉一些打印调试信息。输出过滤机制可以使用分级打印,这种方法很常用的,比如 linux 内核的打印信息也是有分级打印的。
    • 做的漂亮点,可以根据输出信息级别,打印信息显示的颜色不一样。

    我自己参考了 RT-Thread 写了这部分代码。

    其中核心代码就是这部分分级打印代码:

    在这里插入图片描述

    dbg_log_line 宏定义如下:

    在这里插入图片描述

    这个宏的第一行代码就是首先打印输出 log 信息的前缀以及设置打印信息的显示颜色,打印的前缀我们可以根据需要自己设置,一般都是设置打印出文件名、行数等等。接着就是打印我们真正的 log 信息,最后一行接着打印换行。

    完整的代码如下:

    #ifndef __LOG_H__
    #define __LOG_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include 
    
    /* 日志输出总开关 */
    #define DBG_ENABLE
    
    /* 颜色输出开关 */
    #define DBG_COLOR
    
    /* 日志打印级别 */
    #define DBG_ERROR           0
    #define DBG_WARNING         1
    #define DBG_INFO            2
    #define DBG_LOG             3
    
    /* 设置日志打印级别,级别越高输出的日志信息越多 */
    #define DBG_LEVEL         DBG_LOG
    
    
    #ifdef DBG_ENABLE
    
    /*
     * The color for terminal (foreground)
     * BLACK    30
     * RED      31
     * GREEN    32
     * YELLOW   33
     * BLUE     34
     * PURPLE   35
     * CYAN     36
     * WHITE    37
     */
    #ifdef DBG_COLOR
    #define _DBG_COLOR(n)        printf("\033["#n"m")
    #define _DBG_LOG_HDR(lvl_name, color_n)                    \
        printf("\033["#color_n"m[" lvl_name "/" "<%s:%d>" "] ", __FILE__, __LINE__)
    #define _DBG_LOG_X_END                                     \
        printf("\033[0m\n")
    #else
    #define _DBG_COLOR(n)
    #define _DBG_LOG_HDR(lvl_name, color_n)                    \
        printf("[" lvl_name "/" "%s:%d" "] ", __FILE__, __LINE__)
    #define _DBG_LOG_X_END                                     \
        printf("\n")
    #endif /* DBG_COLOR */
    
    
    #define dbg_log_line(lvl, color_n, fmt, ...)                \
        do                                                      \
        {                                                       \
            _DBG_LOG_HDR(lvl, color_n);                         \
            printf(fmt, ##__VA_ARGS__);                     	\
            _DBG_LOG_X_END;                                     \
        }                                                       \
        while (0)
    
    #else
    #define dbg_log_line(lvl, color_n, fmt, ...)
    #endif /* DBG_ENABLE */
    
    
    /* debug */
    #if (DBG_LEVEL >= DBG_LOG)
    #define LOG_D(fmt, ...)      dbg_log_line("D", 0, fmt, ##__VA_ARGS__)
    #else
    #define LOG_D(...)
    #endif
    
    /* info */
    #if (DBG_LEVEL >= DBG_INFO)
    #define LOG_I(fmt, ...)      dbg_log_line("I", 32, fmt, ##__VA_ARGS__)
    #else
    #define LOG_I(...)
    #endif
    
    /* warn */
    #if (DBG_LEVEL >= DBG_WARNING)
    #define LOG_W(fmt, ...)      dbg_log_line("W", 33, fmt, ##__VA_ARGS__)
    #else
    #define LOG_W(...)
    #endif
    
    /* error */
    #if (DBG_LEVEL >= DBG_ERROR)
    #define LOG_E(fmt, ...)      dbg_log_line("E", 31, fmt, ##__VA_ARGS__)
    #else
    #define LOG_E(...)
    #endif
    
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* __LOG_H__ */
    
    • 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

    在 ubuntu 下测试 log 打印

    由于输出的信息颜色,有些终端可能不支持,所以在 ubuntu 下测试。

    测试代码其实很简单,就是直接打印信息就行。

    #include 
    #include "log.h"
    
    int main(void)
    {
    	LOG_D("***********************log test start***********************");
    
    	LOG_D("hello world...");
    	LOG_I("hello world...");
    	LOG_W("hello world...");
    	LOG_E("hello world...");
    	
    	LOG_D("***********************log test end***********************");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    打印输出效果如下:

    在这里插入图片描述

    可以看到有不同的颜色效果。而且,如果是不支持颜色显示的话,可以关闭颜色显示功能。

    在 STM32 上测试 log 打印

    重定向 printf

    MCU 上测试的话,需要有串口的支持。我使用的测试开发板是 STM32F407ZGT6 芯片,关于串口的初始化相关的代码,我直接使用 STM32CubeMX 生成了,这里不多讲。如果你使用的是其他芯片,自己添加初始化串口相关的代码。

    初始化完串口之后,我们要想使用 printf 打印函数,需要重定向这个函数是向串口打印输出信息的。

    我们只要在工程代码中,重新实现 fputc 这个函数即可。我使用 HAL 库编写的代码如下:

    int fputc(int ch, FILE* f)
    {
    	char a = '\r';
    	
        __HAL_UNLOCK(&huart1);
    	
    	if (ch == '\n')
    	{
    		HAL_UART_Transmit(&huart1, (uint8_t *)&a, 1, 1);
    	}
    	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);
    	
    	return ch;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在 MDK 中配合使用 MicroLIB 库

    重新实现了 fputc 函数之后,我们还需要配合使用 MDK 提供的 MicroLIB ,这个库其实就是标准的 C 库高度优化而来的,占用资源更少。

    在配置串口配置如下:

    在这里插入图片描述

    在配置框中勾上 Use MicroLib 。一定要选择这个,不选择的话,会导致输出不成功的。

    不勾选 MicroLIB 库的方法

    如果我们不勾选 Use MicroLib 也想正常打印输出调试信息的话,那就要我们多添加一些代码了。

    因为 printf 函数使用了半主机模式,所以直接使用这个函数会导致程序无法运行的,我们需要提前高速编译器不要使用半主机模式。

    不勾选 Use MicroLib 库需要添加的完整代码如下:

    #if 1
    /* 告知连接器不从C库链接使用半主机的函数 */
    #pragma import(__use_no_semihosting)
    
    /* 标准库需要的支持数据类型 */
    struct __FILE
    {
        int handle;
    };
     
    FILE __stdout;
    
    /**
     * @brief 定义_sys_exit()以避免使用半主机模式
     * @param void
     * @return  void
     */
    void _sys_exit(int x)
    {
        x = x;
    }
    
    int fputc(int ch, FILE* f)
    {
    	char a = '\r';
    	
        __HAL_UNLOCK(&huart1);
    	
    	if (ch == '\n')
    	{
    		HAL_UART_Transmit(&huart1, (uint8_t *)&a, 1, 1);
    	}
    	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);
    	
    	return ch;
    }
    #endif
    
    • 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

    这样添加了上述代码之后,我们就算不够选 Use MicroLib 库也可以正常打印输出日志信息了。

    log模块打印测试效果

    在 main 函数中,我们添加在 ubuntu 下测试的那几行打印测试代码,然后打开 MobaXterm 终端软件,可以看到如下打印效果:

    在这里插入图片描述

    可以看到有不同颜色的打印效果,如果不想打印某些信息的级别,修改打印日志输出级别的宏定义就行。

    注意:如果是使用常用的串口助手测试工具的话,可能不支持输出信息带颜色的,而且反而会看到一些不需要的颜色编码,这个时候我们可以把颜色输出的宏定义关闭就可以不用输出颜色了。

  • 相关阅读:
    react使用脚手架搭建
    HTTP 发展史
    SparkStreaming—入门概述
    分类算法系列④:朴素贝叶斯算法
    线性代数学习笔记10-1:奇异值分解SVD(从四个子空间角度理解)
    【Maven】SpringBoot多模块项目利用reversion占位符,进行版本管理.打包时版本号不能识别问题
    Hadoop----------大数据单机伪集群搭建(这一篇就够了)
    sqlite3 drop table 大数据库,耗时很长(相关搜索:数据库)
    ARM架构 --- MMU与协处理器CP15
    Midjourney中文版来了,这里有一份保姆级上手指南!
  • 原文地址:https://blog.csdn.net/luobeihai/article/details/126613432