打印
[应用相关]

不会吧,难道还没有人使用过日志库,今天你就看到了

[复制链接]
67|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 深藏功名丿小志 于 2025-3-15 11:15 编辑

#有奖活动# #申请原创#

今天介绍 的是使用AT32移植ulog日志库,源码在附件,可以直接下载放在工程中使用哦...

1、ulog日志库简介

在嵌入式系统开发中,日志记录是不可或缺的功能,它能帮助开发者记录关键信息,便于调试和故障排查。然而,嵌入式系统通常资源受限,如内存小、处理能力有限等,传统的日志库难以满足其需求。uLog 应运而生,它是一款专为嵌入式微控制器或资源受限系统设计的轻量级结构化日志库,为开发者提供了一种高效、灵活且低资源开销的日志记录解决方案。

uLog 以其简洁的结构和强大的功能脱颖而出。它仅包含一个头文件和一个源文件,用纯 C 编写,易于集成到各种嵌入式项目中。它提供了多种日志级别,如 CRITICAL、ERROR、INFO 等,方便开发者根据实际需求灵活控制日志输出。此外,uLog 支持多种用户定义的输出方式,例如控制台、日志文件等,能够满足不同场景下的日志记录需求。

uLog 的资源开销极低,代码量小,运行时占用的内存和 CPU 资源少,这使得它非常适合在资源受限的嵌入式系统中使用。开发者还可以通过宏在编译时控制日志功能的开启与关闭,进一步优化资源利用。uLog 的依赖极少,仅需几个标准库头文件,这使得它在各种开发环境中都能轻松部署。

借助 uLog,开发者可以方便地在嵌入式系统中记录日志,从而显著提升系统的可维护性和调试效率。无论是简单的调试信息,还是关键的错误记录,uLog 都能以高效、灵活的方式满足需求,成为嵌入式开发中不可或缺的工具之一。

2、ulog等级介绍

2.1 ULOG_TRACE_LEVEL

等级值:通常为最低等级,如定义为 100。
用途:用于记录非常详细的调试信息,通常包含程序执行的每一个步骤和变量的详细状态。在开发和调试阶段,开发者可以使用这个等级来深入了解程序的运行流程,但在生产环境中一般会关闭该等级的日志输出,因为它会产生大量的信息。

2.2 ULOG_DEBUG_LEVEL

等级值:比 ULOG_TRACE_LEVEL 高。
用途:用于记录调试信息,主要记录程序中的关键变量、函数调用等信息,帮助开发者定位问题。与 ULOG_TRACE_LEVEL 相比,它的信息输出量相对较少,更侧重于关键调试信息的记录。

2.3 ULOG_INFO_LEVEL

等级值:比 ULOG_DEBUG_LEVEL 高。
用途:用于记录程序的一般运行信息,如程序启动、重要操作的开始和结束等。这些信息对于了解程序的正常运行状态非常有用,通常在开发和生产环境中都会保留该等级的日志输出。

2.4 ULOG_WARNING_LEVEL

等级值:比 ULOG_INFO_LEVEL 高。
用途:用于记录可能会导致问题的警告信息,如程序中出现了一些不符合预期的情况,但还没有导致严重的错误。开发者需要关注这些警告信息,以便及时发现潜在的问题。

2.5 ULOG_ERROR_LEVEL

等级值:比 ULOG_WARNING_LEVEL 高。
用途:用于记录程序中出现的错误信息,这些错误可能会影响程序的正常运行,但不会导致程序崩溃。例如,文件读取失败、网络连接中断等错误都可以使用该等级的日志进行记录。

2.6 ULOG_CRITICAL_LEVEL
等级值:比 ULOG_ERROR_LEVEL 高。
用途:用于记录严重的错误信息,这些错误可能会导致程序崩溃或系统不稳定。例如,内存泄漏、硬件故障等严重问题都应该使用该等级的日志进行记录。

2.7 ULOG_ALWAYS_LEVEL

等级值:最高等级。
用途:用于记录无论在任何情况下都需要输出的信息,如系统的关键状态、重要的安全信息等。该等级的日志信息通常不会被过滤,会一直输出。

3、ulog接口介绍

3.1 ULOG_INIT()

定义:#define ULOG_INIT() ulog_init()
功能:该宏用于初始化 ulog 日志系统。调用这个宏就相当于调用 ulog_init 函数,一般在使用 ulog 记录日志之前,需要先调用该宏进行初始化操作,以确保日志系统的正常工作。

3.2 ULOG_SUBSCRIBE(a, b)

定义:#define ULOG_SUBSCRIBE(a, b) ulog_subscribe(a, b)
功能:用于订阅日志处理函数。参数 a 是用户自定义的日志处理函数,其类型为 ulog_function_t;参数 b 是日志级别阈值,类型为 ulog_level_t。当有日志消息的级别达到或超过该阈值时,会调用对应的日志处理函数 a 来处理该消息。

3.3 ULOG_UNSUBSCRIBE(a)

定义:#define ULOG_UNSUBSCRIBE(a) ulog_unsubscribe(a)
功能:用于取消订阅指定的日志处理函数。参数 a 是之前通过 ULOG_SUBSCRIBE 订阅的日志处理函数。调用该宏后,该日志处理函数将不再接收日志消息。

3.4 ULOG_LEVEL_NAME(a)

定义:#define ULOG_LEVEL_NAME(a) ulog_level_name(a)
功能:根据传入的日志级别 a(类型为 ulog_level_t),返回该日志级别的名称字符串。例如,传入 ULOG_INFO_LEVEL,则返回 "INFO"。

3.5 ULOG(...)

定义:#define ULOG(...) ulog_message(__VA_ARGS__)
功能:这是一个通用的日志记录宏,__VA_ARGS__ 表示可变参数。使用时,可以直接传入日志级别和格式化字符串等参数,相当于调用 ulog_message 函数。

3.6 ULOG_TRACE(...)  等

功能:用于记录 TRACE 级别的日志信息的宏接口。ULOG_TRACE 用于记录 TRACE 级别的日志,ULOG_DEBUG 用于记录 DEBUG 级别的日志,以此类推。使用时,只需传入相应的格式化字符串和参数,就会调用 ulog_message 函数并指定对应的日志级别。

4、AT32移植ulog步骤

* 获取 uLog 源码:从官方渠道或其他可靠来源获取 uLog 的源代码文件,通常包括头文件(.h)和源文件(.c)
* 添加文件到工程:将 uLog 的源文件和头文件添加到工程中。main模块包含模块 #include "ulog.h"
* 修改 uLog 配置文件:使能 ulog 头文件中的 ULOG_ENABLED,即将注释的宏打开即可。
* 适配底层接口:uLog 需要通过串口输出日志,需要根据芯片的串口驱动函数,实现 fputc 函数,将日志数据通过串口发送出去。
* 编写测试代码:在工程中编写测试代码,调用 uLog 的日志输出函数,验证 uLog 是否正常工作。例如,可以在主函数中使用ULOG_I("This is a test log.")输出一条信息日志。

适配底层接口,我们使用 printf 将日志消息输出到串口 USART1 中。固需要重写 "int fputc(int ch, FILE *f)"函数,这里"at32f403a_407_board.c"已经帮我们写好了,还怪贴心的,以为这块又要折腾一会了,哈哈,如下所示:

#if defined (__GNUC__) && !defined (__clang__)
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  retargets the c library printf function to the usart.
  * @param  none
  * @retval none
  */
PUTCHAR_PROTOTYPE
{
  while(usart_flag_get(PRINT_UART, USART_TDBE_FLAG) == RESET);
  usart_data_transmit(PRINT_UART, (uint16_t)ch);
  while(usart_flag_get(PRINT_UART, USART_TDC_FLAG) == RESET);
  return ch;
}

编写剩下的是编写测试函数,如下所示:

void my_console_logger(ulog_level_t severity, char *msg)
{
  printf("console: %s [%s]: %s\r\n",
         "time", // user defined function
         ulog_level_name(severity),
         msg);
}

void my_file_logger(ulog_level_t severity, char *msg)
{
  printf("file: %s [%s]: %s\r\n",
         "time", // user defined function
         ulog_level_name(severity),
         msg);
}

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  main function.
  * @param  none
  * @retval none
  */
int main(void)
{
  system_clock_config();
  at32_board_init();
  uart_print_init(115200);
  
  int arg = 42;

  ULOG_INIT();

  // log messages with a severity of WARNING or higher to the console.  The
  // user must supply a method for my_console_logger, e.g. along the lines
  // of what is shown above.
  ULOG_SUBSCRIBE(my_console_logger, ULOG_DEBUG_LEVEL);

  // log messages with a severity of DEBUG or higher to a file.  The user must
  // provide a method for my_file_logger (not shown here).
  ULOG_SUBSCRIBE(my_file_logger, ULOG_WARNING_LEVEL);

  ULOG_INFO("Info, arg=%d", arg);         // logs to file but not console
  ULOG_CRITICAL("Critical, arg=%d", arg); // logs to file and console

  // dynamically change the threshold for a specific logger
  ULOG_SUBSCRIBE(my_console_logger, ULOG_INFO_LEVEL);

  ULOG_INFO("Info, arg=%d", arg); // logs to file and console

  // remove a logger
  ULOG_UNSUBSCRIBE(my_file_logger);

  ULOG_INFO("Info, arg=%d", arg); // logs to console only
  
  while(1)
  {

  }
}

下面是工程部分,是在"printf"例程的基础上修改的。后面会把关键代码上传的。



将工程下载到目标板上,然后将串口连接到电脑端,按下复位按钮,让程序重新执行,查看串口的消息,如下所示:



下面是串口接收到的消息:



至此,移植还算成功,中间也出现了一些小插曲,主要是如何开启日志模块出现的问题,原来想的是在main模块中直接定义 ULOG_ENABLED 这个宏的,结果编译总是报错,主要还是宏的作用域的问题。


  

AT32_ULOG.rar

15.41 KB

工程源码

使用特权

评论回复
沙发
作业粉碎机| | 2025-3-15 13:18 | 只看该作者
会的,我还真的没有使用过日志库,我不看我不看

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

20

帖子

0

粉丝