• 组件-ulog


    目录

    1、ulog 简介

    1.1、特性

    1.2、架构

    1.3、配置选项

    1.3.1、配置项

     1.3.2、宏

    1.4、日志级别

    1.4.1、不使用ULOG_USING_SYSLOG

    1.4.2、使用ULOG_USING_SYSLOG

    1.4.3、静态级别与动态级别

    1.4.4、全局级别与模块级别

    1.4.5、设定日志的输出级别

    1.5、日志标签

    2、日志输出

    2.1、宏 LOG_X(...)

    2.2、ulog_x(LOG_TAG, __VA_ARGS__)

    2.3、LOG_RAW()和ulog_raw()

    2.4、LOG_HEX()和ulog_hex()

    2.5、接口函数

    2.5.1、ulog_raw()函数

    2.5.2、ulog_hexdump()函数

    2.5.3、ulog_output()函数

    2.6、内部函数

    2.6.1、output_lock()函数

    2.6.2、output_unlock()函数

    2.6.3、get_log_buf()函数

    2.6.4、do_output()函数

    2.6.5、ulog_output_to_all_backend()函数

    2.6.6、ulog_voutput()函数

    2.6.7、ulog_flush()函数

    3、设置日志格式

    3.1、函数

    3.1.1、ulog_head_formater()函数

    3.1.2、ulog_tail_formater()函数

    3.1.3、ulog_hex_formater()函数

    3.1.4、ulog_formater()函数

    4、动态过滤器

    4.1、接口函数

    4.1.1、ulog_tag_lvl_list_get()函数

    4.1.2、ulog_global_filter_lvl_set()函数

    4.1.3、ulog_global_filter_lvl_get()函数

    4.1.4、ulog_global_filter_tag_set()函数

    4.1.5、ulog_global_filter_tag_get()函数

    4.1.6、ulog_global_filter_kw_set()函数

    4.1.7、ulog_global_filter_kw_get()函数

    4.1.8、ulog_tag_lvl_filter_set()函数

     4.1.9、ulog_tag_lvl_filter_get()函数

    5、异步输出

    5.1、异步模式的优缺点

    5.1.1、优点

    5.1.2、缺点

    5.2、结构体

    5.2.1、rt_ulog结构体

    5.2.2、ulog_frame结构体

    5.3、函数

    5.3.1、ulog_async_output_enabled()函数

    5.3.2、ulog_async_output()函数

    5.3.3、ulog_async_waiting_log()函数

    5.3.4、async_output_thread_entry()函数

    5.3.5、ulog_async_init()函数

    6、中断输出

    7、后端

    7.1、ulog_backend结构体

    7.2、接口

    7.2.1、ulog_backend_register()函数

    7.2.2、ulog_backend_unregister()函数

    7.2.3、ulog_backend_set_filter()函数

    7.2.4、ulog_backend_find()函数

    7.2.5、ulog_be_lvl_filter_set()函数


    1、ulog 简介

    ulog是一个小巧、实用的日志组件(日志系统),为软件调试、维护过程中的问题追溯、性能分析、系统监控、故障预警等功能,提供参考依据。

    1.1、特性

    ulog 是一个非常简洁、易用的 C/C++ 日志组件,第一个字母 u 代表 μ,即微型的意思。它能做到最低ROM<1K, RAM<0.2K的资源占用。ulog 不仅有小巧体积,同样也有非常全面的功能,其设计理念参考的是另外一款 C/C++ 开源日志库:EasyLogger(简称 elog),并在功能和性能等方面做了非常多的改进。主要特性如下:

    1)日志输出的后端多样化,可支持例如:串口、网络,文件、闪存等后端形式。

    2)日志输出被设计为线程安全的方式,并支持异步输出模式。

    3)日志系统高可靠,在中断 ISR 、Hardfault等复杂环境下依旧可用。

    4)日志支持运行期 / 编译期设置输出级别。

    5)日志内容支持按关键词及标签方式进行全局过滤。

    6)API 和日志格式可兼容 linux syslog。

    7)支持以 hex 格式 dump 调试数据到日志中。

    8)兼容 rtdbg (RTT 早期的日志头文件)及 EasyLogger 的日志输出 API。

    1.2、架构

    分层说明
    前端该层作为离应用最近的一层,给用户提供了 syslog 及 LOG_X 两类 API 接口,方便用户在不同的场景中使用。
    核心中间核心层的主要工作是将上层传递过来的日志,按照不同的配置要求进行格式化与过滤然后生成日志帧,最终通过不同的输出模块,输出到最底层的后端设备上。
    后端接收到核心层发来的日志帧后,将日志输出到已经注册的日志后端设备上,例如:文件、控制台、日志服务器等等

    1.3、配置选项

    1.3.1、配置项

     1.3.2、宏

    RT_USING_ULOG使能ulog组件
    ULOG_USING_SYSLOG使能syslog
    ULOG_FILTER_TAG_MAX_LEN日志tag最大长度
    ULOG_FILTER_KW_MAX_LEN日志关键词最大长度
    ULOG_USING_FILTER使能过滤器
    ULOG_OUTPUT_LVL

    静态日志输出级别

    注:选择完成后,比设定级别低的日志将不会被编译到 ROM 中

    LOG_LVL

    模块静态日志输出级别

    注:需在文件.c文件的#include  的上方定义

    LOG_TAG

    模块标签

    注:需在文件.c文件的#include  的上方定义

    ULOG_USING_ISR_LOG

    使能中断 ISR 日志,即在 ISR 中也可以使用日志输出 API

    ULOG_ASSERT_ENABLE

    使能断言检查。

    注:关闭使能后,断言的日志将不会被编译到 ROM 中

    ULOG_LINE_BUF_SIZE

    日志的最大长度。

    注:由于 ulog 的日志 API 按行作为单位,所以这个长度也代表一行日志的最大长度

    异步日志
    ULOG_USING_ASYNC_OUTPUT

    使能异步日志输出模式。

    注:开启这个模式后,日志不会立刻输出到后端,而是先缓存起来,然后交给日志输出线程(例如:idle 线程)去输出

    ULOG_ASYNC_OUTPUT_BUF_SIZE异步日志的最大长度。
    ULOG_ASYNC_OUTPUT_STORE_LINES

    异步日志可储存的消息条数。

    注:默认为ULOG_ASYNC_OUTPUT_BUF_SIZE * 3 / 2 / 80

    ULOG_ASYNC_OUTPUT_BY_THREAD

    异步日志通过异步线程输出。

    注:也可以不使用异步线程,如通过idle线程进行输出。

    ULOG_ASYNC_OUTPUT_THREAD_STACK异步输出线程栈大小
    ULOG_ASYNC_OUTPUT_THREAD_PRIORITY异步输出线程优先级
    后端
    ULOG_BACKEND_USING_CONSOLE

    使能控制台作为后端。

    注:使能后,在非线程上下文环境中,日志可以输出到控制台串口上。

    ULOG_BACKEND_USING_FILE使能文件作为后端。
    日志格式
    ULOG_USING_COLOR                         使能颜色格式
    ULOG_OUTPUT_TIME

    使能时间格式

    注:默认为系统tick,定义宏ULOG_TIME_USING_TIMESTAMP后则为时间戳

    ULOG_TIME_USING_TIMESTAMP使能时间戳
    ULOG_OUTPUT_LEVEL使能输出级别格式
    ULOG_OUTPUT_THREAD_NAME使能输出线程名称格式

    1.4、日志级别

    1.4.1、不使用ULOG_USING_SYSLOG

    级别名称描述
    LOG_LVL_ASSERT断言发生无法处理、致命性的的错误,以至于系统无法继续运行的断言日志
    LOG_LVL_ERROR错误发生严重的、不可修复的错误时输出的日志属于错误级别日志
    LOG_LVL_WARNING警告出现一些不太重要的、具有可修复性的错误时,会输出这些警告日志
    LOG_LVL_INFO信息给本模块上层使用人员查看的重要提示信息日志,例如:初始化成功,当前工作状态等。该级别日志一般在量产时依旧保留
    LOG_LVL_DBG调试给本模块开发人员查看的调试日志,该级别日志一般在量产时关闭

    1.4.2、使用ULOG_USING_SYSLOG

    级别名称
    EMERG紧急
    ALERT警报
    CRIT重要
    ERR错误
    WARNING警告
    NOTICE通知
    INFO信息
    DEBUG调试

    1.4.3、静态级别与动态级别

    按照日志是否可以在运行阶段修改进行分类。可在运行阶段修改的称之为动态级别,只能在编译阶段修改的称之为静态级别。

    静态级别比静态级别低的日志将不会被编译到 ROM 中,最终也不会输出、显示出来。
    动态级别动态级别可以管控的是高于或等于静态级别的日志。在 ulog 运行时,比动态级别低的日志会被过滤掉。

    1.4.4、全局级别与模块级别

    按照作用域进行的分类。在 ulog 中每个文件(模块)也可以设定独立的日志级别。全局级别作用域大于模块级别(模块级别只能管控那些高于或等于全局级别的模块日志)。

    1.4.5、设定日志的输出级别

    1)全局静态日志级别:ULOG_OUTPUT_LVL宏。

    2)全局动态日志级别:使用 void ulog_global_filter_lvl_set(rt_uint32_t level) 函数来设定。

    3)模块静态日志级别:在模块(文件)内定义 LOG_LVL 宏,与日志标签宏 LOG_TAG 定义方式类似。

    4)模块动态日志级别:使用 int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level) 函数来设定。

    注:作用范围关系为,全局静态>全局动态>模块静态>模块动态。

    1.5、日志标签

    使用tag标签可以给每条日志进行分类。标签的定义是按照模块化的方式,例如:Wi-Fi 组件包括设备驱动(wifi_driver)、设备管理(wifi_mgnt)等模块,则 Wi-Fi 组件内部模块可以使用 wifi.driverwifi.mgnt 等作为标签,进行日志的分类输出。

    注:日志标签的作用域是当前源码文件,项目源代码通常也会按照模块进行文件分类。所以在定义标签时,可以指定模块名、子模块名作为标签名称,这样不仅在日志输出显示时清晰直观,也能方便后续按标签方式动态调整级别或过滤。

    每条日志的标签属性也可以被输出并显示出来,同时 ulog 还可以设置每个标签对应日志的输出级别。当前不重要模块的日志可以选择性关闭,不仅降低 ROM 资源,还能帮助开发者过滤无关日志。

    1. #define LOG_TAG "example" // 该模块对应的标签。不定义时,默认:NO_TAG
    2. #define LOG_LVL LOG_LVL_DBG // 该模块对应的日志输出级别。不定义时,默认:调试级别
    3. #include // 必须在 LOG_TAG 与 LOG_LVL 下面

    注:定义日志标签必须位于 #include  的上方,否则会使用默认的 NO_TAG(不推荐在头文件中定义这些宏)。

    2、日志输出

    ulog主要有两种日志输出宏 API,源代码中定义如下所示:

    1. #define LOG_E(...) ulog_e(LOG_TAG, __VA_ARGS__)
    2. #define LOG_W(...) ulog_w(LOG_TAG, __VA_ARGS__)
    3. #define LOG_I(...) ulog_i(LOG_TAG, __VA_ARGS__)
    4. #define LOG_D(...) ulog_d(LOG_TAG, __VA_ARGS__)
    5. #define LOG_RAW(...) ulog_raw(__VA_ARGS__)
    6. #define LOG_HEX(name, width, buf, size) ulog_hex(name, width, buf, size)

    2.1、宏 LOG_X(...)

    宏 LOG_X(...)的X对应的是不同级别的第一个字母大写。参数 ... 为日志内容,格式与 printf 一致。这种方式是首选,一方面因为其 API 格式简单,入参只有一个即日志信息。

    注:这个API适用于在一个文件中使用相同tag 输出

    2.2、ulog_x(LOG_TAG, __VA_ARGS__)

    ulog_x(LOG_TAG, __VA_ARGS__)宏的x 对应的是不同级别的简写。参数 LOG_TAG 为日志标签,参数 ... 为日志内容,格式与 printf 一致。

    注:这个 API 适用于在一个文件中使用不同 tag 输出日志的情况。

    2.3、LOG_RAW()和ulog_raw()

    LOG_X 及 ulog_x 这类 API 输出都是带格式日志;有些时候需要输出不带任何格式的日志时,可以使用 LOG_RAW 或 ulog_raw() 。

    2.4、LOG_HEX()和ulog_hex()

    以 16 进制 hex 格式 dump 数据到日志中可使用可以使用 LOG_HEX() 或 ulog_hex() 。

    2.5、接口函数

    2.5.1、ulog_raw()函数

    该接口调用get_log_buf()获取日志缓冲区并调用do_output()函数输出日志(级别LOG_LVL_DBG,标签为"")。

    void ulog_raw(const char *format, ...)

    2.5.2、ulog_hexdump()函数

    将十六进制格式数据转储到日志中。

    void ulog_hexdump(const char *tag, rt_size_t width, const rt_uint8_t *buf, rt_size_t size, ...)

    2.5.3、ulog_output()函数

    void ulog_output(rt_uint32_t level, const char *tag, rt_bool_t newline, const char *format, ...)

    2.6、内部函数

    2.6.1、output_lock()函数

    1)如果调度程序已启动并且位于线程上下文中,获取互斥量ulog.output_locker

    2)否则,若定义ULOG_USING_ISR_LOG则直接调用rt_hw_interrupt_disable()关中断

    static void output_lock(void)

    2.6.2、output_unlock()函数

    1)如果调度程序已启动并且位于线程上下文中,释放互斥量ulog.output_locker

    2)否则,若定义ULOG_USING_ISR_LOG则直接调用rt_hw_interrupt_enable()开中断

    static void output_unlock(void)

    2.6.3、get_log_buf()函数

    1)在线程上下文中,则返回ulog.log_buf_th

    2)在中断中,且定义ULOG_USING_ISR_LOG,则返回ulog.log_buf_isr。

    3)在中断中,且未定义ULOG_USING_ISR_LOG,则返回RT_NULL。

    static char *get_log_buf(void)

    2.6.4、do_output()函数

    1)如果定义ULOG_USING_ASYNC_OUTPUT,分配ulog_frame_t结构体,将其放入ulog.async_rbb

    2)如果未定义ULOG_USING_ASYNC_OUTPUT,在线程上下文环境,调用ulog_output_to_all_backend()函数输出日志

    3)如果未定义ULOG_USING_ASYNC_OUTPUT,在中断环境,如果定义ULOG_BACKEND_USING_CONSOLE,则调用ulog_console_backend_output(),因为不能确保所有后端都支持ISR上下文输出。

    static void do_output(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log_buf, rt_size_t log_len)

    2.6.5、ulog_output_to_all_backend()函数

    向所有后端输出日志。

    1)如果没有注册后端,则直接调用rt_kputs()输出日志

    2)遍历所有后端,查找符合条件的后端(level小于等于backend的out_level)

    3)向后端输出日志

            a、如果未定义ULOG_USING_COLOR或定义了ULOG_USING_SYSLOG,则直接调用backend的output()输出日志

            b、backend的filter成员存在,且调用filter()返回RT_TRUE时,如果backend的support_color为RT_TRUE或 is_raw,则直接调用backend的output()输出日志

            c、backend的filter成员存在,且调用filter()返回RT_TRUE时,如果backend不使能颜色时,如果log中存在颜色信息重新计算日志起始地址和日志大小(去除log中相应的颜色信息),再调用output()输出日志

    static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)

    2.6.6、ulog_voutput()函数

    1)全局过滤,tag级别过滤。

    2)调用ulog_formater()函数或ulog_hex_formater()函数或syslog_formater()函数设置日志格式

    3)如果定义关键词,且日志中匹配关键词,则不输出。

    4)调用do_output()函数输出日志

    1. void ulog_voutput(rt_uint32_t level, const char *tag, rt_bool_t newline, const rt_uint8_t *hex_buf, rt_size_t hex_size,
    2. rt_size_t hex_width, rt_base_t hex_addr, const char *format, va_list args)

    2.6.7、ulog_flush()函数

    清除所有后端日志。

    1)如果定义ULOG_USING_ASYNC_OUTPUT,则调用ulog_async_output()函数

    2)遍历日志后端,调用backend的flush()函数

    void ulog_flush(void)

    3、设置日志格式

    ulog支持带颜色的日志、时间信息(包括时间戳)、级别信息、标签信息、线程信息。

    3.1、函数

    3.1.1、ulog_head_formater()函数

    添加日志格式前缀,格式为:[颜色开始信息]+[时间信息]+[日志级别]+[标签信息]+[线程名称]+": "

    rt_size_t ulog_head_formater(char *log_buf, rt_uint32_t level, const char *tag)

    3.1.2、ulog_tail_formater()函数

    溢出检查并添加日志格式后缀,格式为:[ULOG_NEWLINE_SIGN宏定义的换行符]+[颜色结束信息]+'\0'

    rt_size_t ulog_tail_formater(char *log_buf, rt_size_t log_len, rt_bool_t newline, rt_uint32_t level)

    3.1.3、ulog_hex_formater()函数

    日志格式前缀+缓冲区地址信息+十六进制值+ascii值+日志格式后缀

    注:不可打印的ascii值则会输出'.'

    rt_size_t ulog_hex_formater(char *log_buf, const char *tag, const rt_uint8_t *buf, rt_size_t size, rt_size_t width, rt_base_t addr)

    3.1.4、ulog_formater()函数

    添加日志格式,调用ulog_head_formater()函数添加日志格式前缀,调用ulog_tail_formater()函数添加日志格式后缀。

    rt_size_t ulog_formater(char *log_buf, rt_uint32_t level, const char *tag, rt_bool_t newline,const char *format, va_list args)

    4、动态过滤器

    1. struct ulog_tag_lvl_filter
    2. {
    3. char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
    4. rt_uint32_t level;
    5. rt_slist_t list;
    6. };
    1. struct rt_ulog
    2. {
    3. ...
    4. #ifdef ULOG_USING_FILTER
    5. struct
    6. {
    7. /* all tag's level filter */
    8. rt_slist_t tag_lvl_list;
    9. /* global filter level, tag and keyword */
    10. rt_uint32_t level;
    11. char tag[ULOG_FILTER_TAG_MAX_LEN + 1];
    12. char keyword[ULOG_FILTER_KW_MAX_LEN + 1];
    13. } filter;
    14. #endif /* ULOG_USING_FILTER */
    15. };

    4.1、接口函数

    4.1.1、ulog_tag_lvl_list_get()函数

    该接口用于获取日志过滤器链表filter.tag_lvl_list。

    rt_slist_t *ulog_tag_lvl_list_get(void)

    4.1.2、ulog_global_filter_lvl_set()函数

    该接口用于设置日志全局过滤器级别filter.level。

    void ulog_global_filter_lvl_set(rt_uint32_t level)

    4.1.3、ulog_global_filter_lvl_get()函数

    该接口用于获取日志全局过滤器级别filter.level。

    rt_uint32_t ulog_global_filter_lvl_get(void)

    4.1.4、ulog_global_filter_tag_set()函数

    该接口用于设置日志全局过滤器标签filter.tag。

    void ulog_global_filter_tag_set(const char *tag)

    4.1.5、ulog_global_filter_tag_get()函数

    该接口用于获取日志全局过滤器标签filter.tag。

    const char *ulog_global_filter_tag_get(void)

    4.1.6、ulog_global_filter_kw_set()函数

    该接口用于设置日志全局过滤器关键词filter.keyword。

    void ulog_global_filter_kw_set(const char *keyword)

    4.1.7、ulog_global_filter_kw_get()函数

    该接口用于获取日志全局过滤器关键词filter.keyword。

    const char *ulog_global_filter_kw_get(void)

    4.1.8、ulog_tag_lvl_filter_set()函数

    通过不同的标签设置过滤器的级别(标签上小于此级别的日志将不输出)。

    1)日志过滤器中未存在对应tag,且级别不是LOG_FILTER_LVL_ALL,则分配ulog_tag_lvl_filter结构体,并将其加入过滤器中。

    2)日志过滤器中存在对应tag,且级别不是LOG_FILTER_LVL_ALL,则更新标签级别。

    3)日志过滤器中存在对应tag,且级别为LOG_FILTER_LVL_ALL,则从过滤器移除对应标签,并释放内存。

    int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level)

     4.1.9、ulog_tag_lvl_filter_get()函数

    当没有找到标签时,它将返回最低级别。当找到标签时,返回该标签的级别。

    rt_uint32_t ulog_tag_lvl_filter_get(const char *tag)

    5、异步输出

    在 ulog 中,默认的输出模式是同步模式,在很多场景下用户可能还需要异步模式。用户在调用日志输出 API 时,会将日志缓存到缓冲区中,会有专门负责日志输出的线程取出日志,然后输出到后端。

    异步模式和同步模式针对用户而言,在日志 API 使用上是没有差异的,因为 ulog 在底层处理上会有区分。两者的工作原理区别大致如下图所示:

    注:开启异步模式后,在代码上离得非常近的日志的时间信息几乎是相同的(几乎不占用调用者的时间)。但在同步模式下,日志使用用户线程来输出,由于日志输出要花一定时间,所以每条日志的时间会有一定的间隔。

    5.1、异步模式的优缺点

    5.1.1、优点

    1)日志输出时不会阻塞住当前线程,有些后端输出速率低,使用同步输出模式可能影响当前线程的时序,异步模式不存在该问题。
    2)每个使用日志的线程省略了后端输出的动作,所以这些线程的堆栈开销可能也会减少,从这个角度也可以降低整个系统的资源占用。
    3)同步模式下的中断日志只能输出到控制台后端,而异步模式下中断日志可以输出到所有后端去。

    5.1.2、缺点

    1)异步模式需要日志缓冲区。

    2)异步日志的输出需要有专门线程来完成,比如:idle 线程或者用户自定义的线程,用法上略显复杂。

    3)整体感觉异步模式资源占用会比同步模式要高。

    5.2、结构体

    5.2.1、rt_ulog结构体

    rt_ulog中异步输出相关部分如下:

    1. struct rt_ulog
    2. {
    3. ...
    4. #ifdef ULOG_USING_ASYNC_OUTPUT
    5. rt_bool_t async_enabled;
    6. rt_rbb_t async_rbb;
    7. /* ringbuffer for log_raw function only */
    8. struct rt_ringbuffer *async_rb;
    9. rt_thread_t async_th;
    10. struct rt_semaphore async_notice;
    11. #endif
    12. ...
    13. };
    异步输出相关成员说明
    async_enabled是否使能异步输出
    async_rbb异步输出环块缓冲区
    async_rb

    异步输出环行缓冲区

    注:只用于ulog_raw()函数

    async_th异步输出线程控制块
    async_notice异步输出信号量

    5.2.2、ulog_frame结构体

    异步消息输出通过将消息封装成ulog_frame结构体放入async_rbb异步输出环块缓冲区中。

    1. struct ulog_frame
    2. {
    3. /* magic word is 0x10 ('lo') */
    4. rt_uint32_t magic:8;
    5. rt_uint32_t is_raw:1;
    6. rt_uint32_t log_len:23;
    7. rt_uint32_t level;
    8. const char *log;
    9. const char *tag;
    10. };
    11. typedef struct ulog_frame *ulog_frame_t;

    5.3、函数

    5.3.1、ulog_async_output_enabled()函数

    此接口用于启用或禁用异步输出模式,当禁用异步输出模式时,日志将直接输出。

    void ulog_async_output_enabled(rt_bool_t enabled)

    5.3.2、ulog_async_output()函数

    异步输出日志到所有后端。

    1)遍历ulog.async_rbb取出数据,并调用ulog_output_to_all_backend()函数输出日志

    2)从ulog.async_rb取出数据,并调用ulog_output_to_all_backend()函数输出日志(级别为LOG_LVL_DBG,标签为"")

    void ulog_async_output(void)

    5.3.3、ulog_async_waiting_log()函数

    此接口用于等待获取异步输出日志。

    rt_err_t ulog_async_waiting_log(rt_int32_t time)

    5.3.4、async_output_thread_entry()函数

    1)循环调用ulog_async_output()和ulog_async_waiting_log()函数

    2)如果2秒(RT_TICK_PER_SECOND * 2)内没有日志输出,调用ulog_flush()刷新日志缓冲区

    static void async_output_thread_entry(void *param)

    5.3.5、ulog_async_init()函数

    该接口创建一个异步输出线程,并启动。

    线程参数说明
    线程名"ulog_async"
    入口函数async_output_thread_entry
    入口函数参数&ulog
    栈大小ULOG_ASYNC_OUTPUT_THREAD_STACK
    优先级ULOG_ASYNC_OUTPUT_THREAD_PRIORITY
    时间片 20
    int ulog_async_init(void)

    6、中断输出

    由于ulog 的异步模式具有缓存机制,注册进来的后端内部也可能具有缓存。如果系统出现了 hardfault 、断言等错误情况,但缓存中还有日志没有输出出来,这可能会导致日志丢失的问题,对于查找异常的原因会无从入手。

    1. struct rt_ulog
    2. {
    3. ...
    4. #ifdef ULOG_USING_ISR_LOG
    5. /* the ISR log's line buffer */
    6. rt_base_t output_locker_isr_lvl;
    7. char log_buf_isr[ULOG_LINE_BUF_SIZE + 1];
    8. #endif /* ULOG_USING_ISR_LOG */
    9. ...
    10. };

    很多时候需要在中断 ISR 中输出日志,但是中断 ISR 可能会打断正在进行日志输出的线程。要保证中断日志与线程日志互不干涉,就得针对于中断情况进行特殊处理。

    同步模式下如果线程此时正在输出日志时来了中断,此时如果中断里也有日志要输出,会直接输出到控制台上,不支持输出到其他后端;

    异步模式下如果发生上面的情况,中断里的日志会先放入缓冲区中,最终和线程日志一起交给日志输出线程来处理。

    7、后端

    7.1、ulog_backend结构体

    1. struct ulog_backend
    2. {
    3. char name[RT_NAME_MAX];
    4. rt_bool_t support_color;
    5. rt_uint32_t out_level;
    6. void (*init) (struct ulog_backend *backend);
    7. void (*output)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
    8. void (*flush) (struct ulog_backend *backend);
    9. void (*deinit)(struct ulog_backend *backend);
    10. /* The filter will be call before output. It will return TRUE when the filter condition is math. */
    11. rt_bool_t (*filter)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
    12. rt_slist_t list;
    13. };
    14. typedef struct ulog_backend *ulog_backend_t;
    15. typedef rt_bool_t (*ulog_backend_filter_t)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
    成员说明
    name后端名称
    support_color是否支持颜色
    out_level输出级别
    init初始化函数
    output输出函数
    flush刷新缓存函数
    deinit取消初始化函数
    filter

    过滤函数

    注:定义ULOG_USING_COLOR且未定义ULOG_USING_SYSLOG时,filter()将在输出之前被调用。当过滤条件为math时,它将返回TRUE。

    list后端链表节点

    7.2、接口

    7.2.1、ulog_backend_register()函数

    该接口用于注册一个日志后端。

    1)调用backend的init()函数

    2)设置backend的support_color和name、设置backend的out_level为LOG_FILTER_LVL_ALL

    3)将backend链进日志后端链表ulog.backend_list

    rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color)

    7.2.2、ulog_backend_unregister()函数

    该接口用于取消注册一个日志后端。

    1)调用backend的deinit()函数

    2)将backend从日志后端链表ulog.backend_list移除

    rt_err_t ulog_backend_unregister(ulog_backend_t backend)

    7.2.3、ulog_backend_set_filter()函数

    设置backend的filter成员。

    rt_err_t ulog_backend_set_filter(ulog_backend_t backend, ulog_backend_filter_t filter)

    7.2.4、ulog_backend_find()函数

    根据name从日志后端链表ulog.backend_list中查找对应的backend。

    ulog_backend_t ulog_backend_find(const char *name)

    7.2.5、ulog_be_lvl_filter_set()函数

    设置名称为be_name的backend的out_level成员为level。

    int ulog_be_lvl_filter_set(const char *be_name, rt_uint32_t level)

  • 相关阅读:
    vue-cli中总提示组件没有正确注册
    el-table表格进行排序 & 清除排序和清除排序箭头的高亮图标
    RocketMQ的底层架构模型
    【opencv图像处理】--1. 图像,视频,鼠标,trackbar控件简单使用
    第一届全国高校将计算机技能大赛知识点整理
    2022年6月 电子学会青少年软件编程 中小学生Python编程 等级考试一级真题答案解析(选择题)
    使用Harbor作为docker镜像仓库之安装运行Harbor
    Nginx搭建Rtmp流媒体服务,并使用Ffmpeg推流
    从零实现的Chrome扩展
    网络安全-漏洞与木马
  • 原文地址:https://blog.csdn.net/qq_37932504/article/details/127565200