Easylogger与RTT结合使用 Easylogger在FreeRTOS下实现异步输出
目录
RTT和Easylogger结合使用
RTT和Easylogger的关系
结合使用操作步骤:
错误解决:
问题根源分析
FreeRTOS 下的elog“锁”实现:(同/异步都一样)
修改 elog_port.c 文件
FreeRTOS下实现异步输出日志
elog_cfg.h
elog_async.c
elog_port.c
main.c 初始化
异步输出问题:无法一次性连续发送多条(>11条)日志
问题说明
ds 的解决方法:
1. 提高异步输出任务优先级
2. 优化异步任务处理逻辑
3. 优化默认任务的日志输出
4. 添加缓冲区监控功能
RTT和Easylogger结合使用
RTT和Easylogger的关系
RTT 是 “调试数据传输工具”,EasyLogger 是 “嵌入式日志管理库”,二者可协同工作,而非竞争或从属关系。
维度 | RTT(Real Time Transfer) | EasyLogger |
核心定位 | SEGGER 推出的调试数据实时传输技术(依附 J-Link) | 国产开源的嵌入式日志管理库(纯软件组件) |
核心功能 | 解决 “目标板→PC” 的高效数据传输: - 把目标板的日志 / 调试信息,低开销、实时传到 PC; - 支持双向传输(PC 也能发指令给目标板); - 无需占用 UART/SPI 等外设资源。 | 解决 “日志本身的管理”: - 日志生成(分级:Debug/Info/Warn/Error); - 日志过滤(按级别 / 模块关闭不需要的日志); - 日志格式化(添加时间戳、模块名、级别); - 支持日志输出到不同 “目的地”(RTT、UART、Flash 等)。 |
依赖条件 | 必须配合 J-Link 调试器使用(硬件依赖) | 无硬件依赖,只需嵌入式系统(RT-Thread/FreeRTOS/ 裸机等)支持,直接移植代码即可。 |
实际开发中,两者常搭配使用,形成 “日志管理→高效传输” 的完整链路:
-
目标板上,通过EasyLogger生成规范化日志(比如带时间戳的 Error 级日志);
-
不直接用 UART 传输(速度慢、占外设),而是调用 RTT 的 API,将 EasyLogger 生成的日志数据,通过RTT传输到 PC;
-
PC 端用 “J-Link RTT Viewer” 工具接收数据,实时查看 EasyLogger 格式化后的日志。
简单总结:
EasyLogger 负责 “把日志做规范、做灵活”,RTT 负责 “把规范的日志快速传到 PC”,二者结合能兼顾日志的 “易用性” 和 “传输效率”,是嵌入式开发中很常见的日志方案。
结合使用操作步骤:
首先把RTT和Easylogger的相关文件移植进来,路径记得添加
重写elog_port.c文件,先包含头文件#include "SEGGER_RTT.h",编写初始化、输出、获取时间等函数
输出时间戳:
/*** get current time interface** @return current time*/
const char *elog_port_get_time(void) {/* add your code here */static char time_str[20] = { 0 };// get timesnprintf(time_str, sizeof(time_str), "%d", HAL_GetTick());return time_str;
}
main函数中包含elog.h头文件,调用elog的初始化函数,调用api就能在rtt的viewner上看见打印信息
错误解决:
问题根源分析
(1) 现象描述
启用这两个宏后日志不显示:说明日志被缓存到内存(异步队列或缓冲区),但未触发实际输出到 RTT。
禁用后日志正常:EasyLogger 退化为同步直接输出模式,每条日志立即调用 elog_port_output 写入 RTT。
(2) 具体原因
-
未初始化异步任务:
若启用了 ELOG_ASYNC_OUTPUT_ENABLE,必须调用 elog_async_start() 启动后台任务,否则日志堆积在队列中无法处理。
-
未刷新缓冲区:
若启用了 ELOG_BUF_OUTPUT_ENABLE,需在适当位置调用 elog_flush() 强制刷新缓冲区,否则日志可能驻留内存未输出。
-
RTT 输出函数未适配异步/缓冲模式:
在异步或缓冲模式下,EasyLogger 的输出最终仍需要通过 elog_port_output 写入 RTT。若该函数未正确处理缓冲数据(如未传递正确指针或长度),会导致 RTT 接收错误。
在启用 ELOG_ASYNC_OUTPUT_ENABLE 时,确保在 elog_init() 后启动异步任务:
void main() {elog_init();elog_async_start(); // 必须调用!启动异步任务处理日志队列// ... 其他初始化
}
检查缓冲区刷新
如果启用了 ELOG_BUF_OUTPUT_ENABLE,在需要立即输出日志时手动刷新:
log_i("tag", "message");
elog_flush(); // 强制刷新缓冲区到输出设备
FreeRTOS 下的elog“锁”实现:(同/异步都一样)
修改 elog_port.c
文件
通过使用 FreeRTOS 的互斥锁来保护输出函数:
/** 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 each platform.* Created on: 2015-04-28*/#include <elog.h>
#include "stdio.h"
#include "SEGGER_RTT.h"/* FreeRTOS add begin */
#include "FreeRTOS.h"
#include "semphr.h"
#include "stm32f4xx_hal.h"//#define OutputSemaphore // open lock or not#ifdef OutputSemaphore
/* FreeRTOS 互斥锁用于保护输出 */
static SemaphoreHandle_t xOutputMutex = NULL;
/* FreeRTOS add end */
#endif/*** EasyLogger port initialize** @return result*/
ElogErrCode elog_port_init(void) {ElogErrCode result = ELOG_NO_ERR;/* add your code here */
#ifdef OutputSemaphore/* create xOutputMutex to protect output */xOutputMutex = xSemaphoreCreateMutex();if (xOutputMutex == NULL) {/* xOutputMutex create failed */result = ELOG_NO_ERR;}
#endifreturn result;
}/*** EasyLogger port deinitialize**/
void elog_port_deinit(void) {/* add your code here */
#ifdef OutputSemaphore/* delete xOutputMutex */if (xOutputMutex != NULL) {vSemaphoreDelete(xOutputMutex);xOutputMutex = NULL;}
#endif
}/*** output log port interface** @param log output of log* @param size log size*/
void elog_port_output(const char *log, size_t size) {/* add your code here */// SEGGER_RTT_Write(0,log,size);printf("%.*s",size,log);
}/*** output lock*/
void elog_port_output_lock(void) {/* add your code here */
#ifdef OutputSemaphoreif (xOutputMutex != NULL) {/* wait xOutputMutex forever */xSemaphoreTake(xOutputMutex, portMAX_DELAY);}
#endif
}/*** output unlock*/
void elog_port_output_unlock(void) {/* add your code here */
#ifdef OutputSemaphoreif (xOutputMutex != NULL) {/* release xOutputMutex */xSemaphoreGive(xOutputMutex);}
#endif
}/*** get current time interface** @return current time*/
const char *elog_port_get_time(void) {/* add your code here */static char time_str[20] = { 0 };// get timesnprintf(time_str, sizeof(time_str), "%d", HAL_GetTick());return time_str;
}/*** get current process name interface** @return current process name*/
const char *elog_port_get_p_info(void) {/* add your code here */return "";
}/*** get current thread name interface** @return current thread name*/
const char *elog_port_get_t_info(void) {/* add your code here */return "";
}
FreeRTOS下实现异步输出日志
下面文件中修改与添加的代码部分都加了注释/* FreeRTOS add */ 或者 /* FreeRTOS modify */
elog_cfg.h
在 elog_cfg.h 中打开异步输出使能,关闭 pthread 支持。
/* enable asynchronous output mode */
#define ELOG_ASYNC_OUTPUT_ENABLE
/* the highest output level for async mode, other level will sync output */
#define ELOG_ASYNC_OUTPUT_LVL ELOG_LVL_ASSERT
/* buffer size for asynchronous output mode */
#define ELOG_ASYNC_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10)
/* each asynchronous output's log which must end with newline sign */
#define ELOG_ASYNC_LINE_OUTPUT
/* 注释掉或删除 pthread 支持 */
// #define ELOG_ASYNC_OUTPUT_USING_PTHREAD
elog_async.c
/** This file is part of the EasyLogger Library.** Copyright (c) 2016-2017, 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: Logs asynchronous output.* Created on: 2016-11-06*/#include <elog.h>
#include <string.h>
/* FreeRTOS add begin*/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
/* FreeRTOS add end*/#ifdef ELOG_ASYNC_OUTPUT_ENABLE#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
/* thread default stack size */
#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE
#if PTHREAD_STACK_MIN > 4*1024
#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE PTHREAD_STACK_MIN
#else
#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE (1*1024)
#endif
/* thread default priority */
#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY
#define ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY (sched_get_priority_max(SCHED_RR) - 1)
#endif
/* output thread poll get log buffer size */
#ifndef ELOG_ASYNC_LINE_OUTPUT
#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE
#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE (ELOG_ASYNC_OUTPUT_BUF_SIZE - 4)
#endif
#else
#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE
#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE (ELOG_LINE_BUF_SIZE - 4)
#endif
#endif
#endif /* ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE *//* asynchronous output log notice */
static sem_t output_notice;
/* asynchronous output pthread thread */
static pthread_t async_output_thread;
#endif /* ELOG_ASYNC_OUTPUT_USING_PTHREAD *//* the highest output level for async mode, other level will sync output */
#ifdef ELOG_ASYNC_OUTPUT_LVL
#define OUTPUT_LVL ELOG_ASYNC_OUTPUT_LVL
#else
#define OUTPUT_LVL ELOG_LVL_ASSERT
#endif /* ELOG_ASYNC_OUTPUT_LVL *//* buffer size for asynchronous output mode */
#ifdef ELOG_ASYNC_OUTPUT_BUF_SIZE
#define OUTPUT_BUF_SIZE ELOG_ASYNC_OUTPUT_BUF_SIZE
#else
#define OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10)
#endif /* ELOG_ASYNC_OUTPUT_BUF_SIZE *//* Initialize OK flag */
static bool init_ok = false;
#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
/* thread running flag */
static bool thread_running = false;
#endif
/* FreeRTOS add begin */
/* FreeRTOS task running flag */
static bool task_running = false;
/* FreeRTOS add end */
/* asynchronous output mode enabled flag */
static bool is_enabled = false;
/* asynchronous output mode's ring buffer */
static char log_buf[OUTPUT_BUF_SIZE] = { 0 };
/* log ring buffer write index */
static size_t write_index = 0;
/* log ring buffer read index */
static size_t read_index = 0;
/* log ring buffer full flag */
static bool buf_is_full = false;
/* log ring buffer empty flag */
static bool buf_is_empty = true;/* FreeRTOS add begin*/
/* FreeRTOS task handle and synchronization objects */
static TaskHandle_t async_output_task_handle = NULL;
static SemaphoreHandle_t output_notice_sem = NULL;
static SemaphoreHandle_t async_lock_mutex = NULL;
/* FreeRTOS add end*/extern void elog_port_output(const char *log, size_t size);
extern void elog_output_lock(void);
extern void elog_output_unlock(void);/*** asynchronous output ring buffer used size** @return used size*/
static size_t elog_async_get_buf_used(void) {if (write_index > read_index) {return write_index - read_index;} else {if (!buf_is_full && !buf_is_empty) {return OUTPUT_BUF_SIZE - (read_index - write_index);} else if (buf_is_full) {return OUTPUT_BUF_SIZE;} else {return 0;}}
}/*** asynchronous output ring buffer remain space** @return remain space*/
static size_t async_get_buf_space(void) {return OUTPUT_BUF_SIZE - elog_async_get_buf_used();
}/*** put log to asynchronous output ring buffer** @param log put log buffer* @param size log size** @return put log size, the log which beyond ring buffer space will be dropped*/
static size_t async_put_log(const char *log, size_t size) {size_t space = 0;space = async_get_buf_space();/* no space */if (!space) {size = 0;goto __exit;}/* drop some log */if (space <= size) {size = space;buf_is_full = true;}if (write_index + size < OUTPUT_BUF_SIZE) {memcpy(log_buf + write_index, log, size);write_index += size;} else {memcpy(log_buf + write_index, log, OUTPUT_BUF_SIZE - write_index);memcpy(log_buf, log + OUTPUT_BUF_SIZE - write_index,size - (OUTPUT_BUF_SIZE - write_index));write_index += size - OUTPUT_BUF_SIZE;}buf_is_empty = false;__exit:return size;
}#ifdef ELOG_ASYNC_LINE_OUTPUT
/*** Get line log from asynchronous output ring buffer.* It will copy all log when the newline sign isn't find.** @param log get line log buffer* @param size line log size** @return get line log size, the log size is less than ring buffer used size*/
size_t elog_async_get_line_log(char *log, size_t size) {size_t used = 0, cpy_log_size = 0;/* lock output *//* FreeRTOS modify */// elog_output_lock();if (async_lock_mutex != NULL) {xSemaphoreTake(async_lock_mutex, portMAX_DELAY);}/* FreeRTOS modify */used = elog_async_get_buf_used();/* no log */if (!used || !size) {goto __exit;}/* less log */if (used <= size) {size = used;}if (read_index + size < OUTPUT_BUF_SIZE) {cpy_log_size = elog_cpyln(log, log_buf + read_index, size);read_index += cpy_log_size;} else {cpy_log_size = elog_cpyln(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index);if (cpy_log_size == OUTPUT_BUF_SIZE - read_index) {cpy_log_size += elog_cpyln(log + cpy_log_size, log_buf, size - cpy_log_size);read_index += cpy_log_size - OUTPUT_BUF_SIZE;} else {read_index += cpy_log_size;}}if (used == cpy_log_size) {buf_is_empty = true;}if (cpy_log_size) {buf_is_full = false;}__exit:/* lock output *//* FreeRTOS modify */// elog_output_unlock();if (async_lock_mutex != NULL) {xSemaphoreGive(async_lock_mutex);}/* FreeRTOS modify */return cpy_log_size;
}
#else
/*** get log from asynchronous output ring buffer** @param log get log buffer* @param size log size** @return get log size, the log size is less than ring buffer used size*/
size_t elog_async_get_log(char *log, size_t size) {size_t used = 0;/* lock output *//* FreeRTOS modify */// elog_output_lock();if (async_lock_mutex != NULL) {xSemaphoreTake(async_lock_mutex, portMAX_DELAY);}/* FreeRTOS modify */used = elog_async_get_buf_used();/* no log */if (!used || !size) {size = 0;goto __exit;}/* less log */if (used <= size) {size = used;buf_is_empty = true;}if (read_index + size < OUTPUT_BUF_SIZE) {memcpy(log, log_buf + read_index, size);read_index += size;} else {memcpy(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index);memcpy(log + OUTPUT_BUF_SIZE - read_index, log_buf,size - (OUTPUT_BUF_SIZE - read_index));read_index += size - OUTPUT_BUF_SIZE;}buf_is_full = false;__exit:/* lock output *//* FreeRTOS modify */// elog_output_unlock();if (async_lock_mutex != NULL) {xSemaphoreGive(async_lock_mutex);}/* FreeRTOS modify */return size;
}
#endif /* ELOG_ASYNC_LINE_OUTPUT */void elog_async_output(uint8_t level, const char *log, size_t size) {/* this function must be implement by user when ELOG_ASYNC_OUTPUT_USING_PTHREAD is not defined */// extern void elog_async_output_notice(void);size_t put_size;if (is_enabled) {if (level >= OUTPUT_LVL) {put_size = async_put_log(log, size);/* notify output log thread *//* FreeRTOS modify */// if (put_size > 0) {// elog_async_output_notice();// }if (put_size > 0 && output_notice_sem != NULL) {xSemaphoreGive(output_notice_sem);}/* FreeRTOS modify */}else {elog_port_output(log, size);}} else {elog_port_output(log, size);}
}/*** FreeRTOS 异步输出任务*/
static void async_output_task(void* arg) {size_t get_log_size = 0;static char poll_get_buf[ELOG_LINE_BUF_SIZE];while (task_running) {/* waiting log */if (xSemaphoreTake(output_notice_sem, portMAX_DELAY) == pdTRUE) {/* polling gets and outputs the log */while (true) {
#ifdef ELOG_ASYNC_LINE_OUTPUTget_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#elseget_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endifif (get_log_size) {elog_port_output(poll_get_buf, get_log_size);}else {break;}}}}/* 删除自己 */vTaskDelete(NULL);
}#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD
void elog_async_output_notice(void) {sem_post(&output_notice);
}static void *async_output(void *arg) {size_t get_log_size = 0;static char poll_get_buf[ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE];while(thread_running) {/* waiting log */sem_wait(&output_notice);/* polling gets and outputs the log */while(true) {#ifdef ELOG_ASYNC_LINE_OUTPUTget_log_size = elog_async_get_line_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE);
#elseget_log_size = elog_async_get_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE);
#endifif (get_log_size) {elog_port_output(poll_get_buf, get_log_size);} else {break;}}}return NULL;
}
#endif/*** enable or disable asynchronous output mode* the log will be output directly when mode is disabled** @param enabled true: enabled, false: disabled*/
void elog_async_enabled(bool enabled) {is_enabled = enabled;
}/*** asynchronous output mode initialize** @return result*/
ElogErrCode elog_async_init(void) {ElogErrCode result = ELOG_NO_ERR;if (init_ok) {return result;}#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREADpthread_attr_t thread_attr;struct sched_param thread_sched_param;sem_init(&output_notice, 0, 0);thread_running = true;pthread_attr_init(&thread_attr);//pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);pthread_attr_setstacksize(&thread_attr, ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE);pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);thread_sched_param.sched_priority = ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY;pthread_attr_setschedparam(&thread_attr, &thread_sched_param);pthread_create(&async_output_thread, &thread_attr, async_output, NULL);pthread_attr_destroy(&thread_attr);
#endif/* FreeRTOS add begin *//* 创建信号量 */output_notice_sem = xSemaphoreCreateCounting(10, 0);if (output_notice_sem == NULL) {return ELOG_NO_ERR;}/* 创建互斥锁 */async_lock_mutex = xSemaphoreCreateMutex();if (async_lock_mutex == NULL) {vSemaphoreDelete(output_notice_sem);return ELOG_NO_ERR;}task_running = true;/* 创建异步输出任务 */if (xTaskCreate(async_output_task, "elog_async", configMINIMAL_STACK_SIZE + 512,NULL, tskIDLE_PRIORITY + 2, &async_output_task_handle) != pdPASS) {vSemaphoreDelete(output_notice_sem);vSemaphoreDelete(async_lock_mutex);task_running = false;return ELOG_NO_ERR;}/* FreeRTOS add end */init_ok = true;return result;
}/*** asynchronous output mode deinitialize**/
void elog_async_deinit(void) {if (!init_ok) {return ;}#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREADthread_running = false;elog_async_output_notice();pthread_join(async_output_thread, NULL);sem_destroy(&output_notice);
#endif/* FreeRTOS add begin */task_running = false;/* 通知任务退出 */if (output_notice_sem != NULL) {xSemaphoreGive(output_notice_sem);}/* 等待任务结束 */if (async_output_task_handle != NULL) {vTaskDelete(async_output_task_handle);async_output_task_handle = NULL;}/* 删除信号量和互斥锁 */if (output_notice_sem != NULL) {vSemaphoreDelete(output_notice_sem);output_notice_sem = NULL;}if (async_lock_mutex != NULL) {vSemaphoreDelete(async_lock_mutex);async_lock_mutex = NULL;}/* FreeRTOS add end */init_ok = false;
}#endif /* ELOG_ASYNC_OUTPUT_ENABLE */
elog_port.c
/** 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 each platform.* Created on: 2015-04-28*/#include <elog.h>
#include "stdio.h"
#include "SEGGER_RTT.h"
/* FreeRTOS add begin */
#include "FreeRTOS.h"
#include "semphr.h"
#include "stm32f4xx_hal.h"//#define OutputSemaphore // open lock or not#ifdef OutputSemaphore
/* FreeRTOS 互斥锁用于输出同步 */
static SemaphoreHandle_t output_mutex = NULL;
#endif
/* FreeRTOS add end *//*** EasyLogger port initialize** @return result*/
ElogErrCode elog_port_init(void) {ElogErrCode result = ELOG_NO_ERR;/* add your code here */
#ifdef OutputSemaphore/* 创建互斥锁用于输出同步 */output_mutex = xSemaphoreCreateMutex();if (output_mutex == NULL) {return ELOG_NO_ERR;}
#endifreturn result;
}/*** EasyLogger port deinitialize**/
void elog_port_deinit(void) {/* add your code here */
#ifdef OutputSemaphore/* 删除互斥锁 */if (output_mutex != NULL) {vSemaphoreDelete(output_mutex);output_mutex = NULL;}
#endif
}/*** output log port interface** @param log output of log* @param size log size*/
void elog_port_output(const char *log, size_t size) {/* add your code here */SEGGER_RTT_Write(0,log,size);// printf("%.*s",size,log);
}/*** output lock*/
void elog_port_output_lock(void) {/* add your code here */
#ifdef OutputSemaphore/* 获取互斥锁 */if (output_mutex != NULL) {xSemaphoreTake(output_mutex, portMAX_DELAY);}
#endif
}/*** output unlock*/
void elog_port_output_unlock(void) {/* add your code here */
#ifdef OutputSemaphore/* 释放互斥锁 */if (output_mutex != NULL) {xSemaphoreGive(output_mutex);}
#endif
}/*** get current time interface** @return current time*/
const char *elog_port_get_time(void) {/* add your code here */static char time_str[20] = { 0 };// get timesnprintf(time_str, sizeof(time_str), "%d", HAL_GetTick());return time_str;
}/*** get current process name interface** @return current process name*/
const char *elog_port_get_p_info(void) {/* add your code here */return "";
}/*** get current thread name interface** @return current thread name*/
const char *elog_port_get_t_info(void) {/* add your code here *//* 返回当前任务名称 */TaskHandle_t current_task = xTaskGetCurrentTaskHandle();if (current_task != NULL) {return pcTaskGetName(current_task);}return "unknown";
}
main.c 初始化
// ... 其他代码 .../* USER CODE BEGIN PFP */
// 重定向printf
#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__*//*******************************************************************@brief Retargets the C library printf function to the USART.*@param None*@retval None******************************************************************/PUTCHAR_PROTOTYPE{// HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);SEGGER_RTT_PutChar(0,ch);return ch;}/* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void app_elog_init(void)
{elog_init();elog_set_fmt(ELOG_LVL_ASSERT,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);elog_set_fmt(ELOG_LVL_DEBUG,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);elog_set_fmt(ELOG_LVL_ERROR,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);elog_set_fmt(ELOG_LVL_INFO,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);elog_set_fmt(ELOG_LVL_WARN,ELOG_FMT_TIME | ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_DIR | ELOG_FMT_LINE | ELOG_FMT_FUNC);elog_start();
}/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{// ... 硬件初始化代码 .../* 初始化 FreeRTOS */// .../* 初始化 EasyLogger */app_elog_init();/* 初始化 RTT */SEGGER_RTT_Init();/* 创建任务线程 */// .../* 启动调度器 */// ...// ... 其他代码 ...
}
异步输出问题:无法一次性连续发送多条(>11条)日志
问题说明
目前这种异步输出实现不能一次性连续发送多条(>11条)日志,如果连续发送多条,就会出现丢日志的情况:
目前可以通过穿插 osDelay(3) 在第十一条和第十二条之间来解决这个问题:
ds 的解决方法:
-
任务优先级问题
异步输出任务的优先级可能低于默认任务,导致异步任务无法及时处理日志。
-
信号量竞争
在异步模式下,每次写入日志后会发送信号量通知异步任务。如果日志生成速度过快,可能会导致信号量多次发送,但异步任务只能处理一次。
-
输出速度瓶颈
如果输出接口(如串口)速度较慢,而日志生成速度快,会导致积压。
1. 提高异步输出任务优先级
在 elog_async.c
中提高异步任务的优先级:
/* 创建异步输出任务 */
if (xTaskCreate(async_output_task, "elog_async", configMINIMAL_STACK_SIZE + 1024, NULL, tskIDLE_PRIORITY + 4, &async_output_task_handle) != pdPASS) { // 优先级从+2提高到+4vSemaphoreDelete(output_notice_sem);vSemaphoreDelete(async_lock_mutex);task_running = false;return ELOG_NO_MEM;
}
2. 优化异步任务处理逻辑
修改 elog_async.c
中的异步任务处理逻辑,确保它能处理所有积压的日志:
/*** FreeRTOS 异步输出任务*/
static void async_output_task(void *arg) {size_t get_log_size = 0;static char poll_get_buf[ELOG_LINE_BUF_SIZE];TickType_t last_wake_time = xTaskGetTickCount();const TickType_t frequency = pdMS_TO_TICKS(10); // 每10ms检查一次while(task_running) {/* 定期检查,确保不会错过信号量 */if (xSemaphoreTake(output_notice_sem, frequency) == pdTRUE) {/* 处理所有积压的日志 */do {
#ifdef ELOG_ASYNC_LINE_OUTPUTget_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#elseget_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endifif (get_log_size) {elog_port_output(poll_get_buf, get_log_size);}} while (get_log_size > 0);} else {/* 即使没有信号量,也检查一次是否有积压的日志 */
#ifdef ELOG_ASYNC_LINE_OUTPUTget_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#elseget_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endifif (get_log_size) {elog_port_output(poll_get_buf, get_log_size);}}/* 让出CPU给其他任务 */vTaskDelayUntil(&last_wake_time, frequency);}/* 删除自己 */vTaskDelete(NULL);
}
3. 优化默认任务的日志输出
在默认任务中,减少一次性输出的日志数量,或增加延迟:
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN 5 */osDelay(500);log_d("DefaultTask is alive\r\n");key_press_status_t key_result = KEY_NOT_PRESSED;for (;;){log_d("APP task is alive");// 分批输出日志,避免一次性输出太多for(int i = 1; i <= 5; i++) {log_a("hello%d", i);log_i("hello%d", i+5);log_w("hello%d", i+10);log_e("hello%d", i+15);log_d("hello%d", i+20);osDelay(1); // 每次输出后稍微延迟}// ... 其他代码 ...osDelay(100);}/* USER CODE END 5 */
}
4. 添加缓冲区监控功能
在 elog_async.c
中添加监控功能,以便调试缓冲区使用情况:
// 添加一个函数来获取缓冲区使用情况
size_t elog_async_get_buf_usage(void) {return elog_async_get_buf_used();
}size_t elog_async_get_buf_capacity(void) {return OUTPUT_BUF_SIZE;
}// 在异步任务中添加调试输出
static void async_output_task(void *arg) {size_t get_log_size = 0;static char poll_get_buf[ELOG_LINE_BUF_SIZE];TickType_t last_wake_time = xTaskGetTickCount();const TickType_t frequency = pdMS_TO_TICKS(10);uint32_t loop_count = 0;while(task_running) {/* 定期检查,确保不会错过信号量 */if (xSemaphoreTake(output_notice_sem, frequency) == pdTRUE) {/* 处理所有积压的日志 */do {
#ifdef ELOG_ASYNC_LINE_OUTPUTget_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#elseget_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endifif (get_log_size) {elog_port_output(poll_get_buf, get_log_size);}} while (get_log_size > 0);} else {/* 即使没有信号量,也检查一次是否有积压的日志 */
#ifdef ELOG_ASYNC_LINE_OUTPUTget_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#elseget_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endifif (get_log_size) {elog_port_output(poll_get_buf, get_log_size);}}// 每100次循环输出一次缓冲区使用情况if (++loop_count % 100 == 0) {size_t used = elog_async_get_buf_usage();size_t total = elog_async_get_buf_capacity();log_d("Async buffer: %d/%d bytes (%.1f%%)", used, total, (float)used/total*100);}/* 让出CPU给其他任务 */vTaskDelayUntil(&last_wake_time, frequency);}/* 删除自己 */vTaskDelete(NULL);
}