Easylogger与RTT结合使用 & Easylogger在FreeRTOS下实现异步输出
RTT与EasyLogger是嵌入式开发中常用的两种工具,二者协同工作形成高效日志解决方案。RTT作为SEGGER推出的调试数据传输工具,负责通过J-Link实现目标板与PC间的高效双向通信;EasyLogger则是国产开源的嵌入式日志管理库,专注于日志生成、过滤和格式化。实际应用中,EasyLogger将规范化处理后的日志通过RTT接口传输至PC端,由J-Link RTT Viewer接收显示。
目录
FreeRTOS 下的elog“锁”实现:(同/异步都一样)
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 time
snprintf(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;
}
#endif
return 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 OutputSemaphore
if (xOutputMutex != NULL) {
/* wait xOutputMutex forever */
xSemaphoreTake(xOutputMutex, portMAX_DELAY);
}
#endif
}
/**
* output unlock
*/
void elog_port_output_unlock(void) {
/* add your code here */
#ifdef OutputSemaphore
if (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 time
snprintf(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_OUTPUT
get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
if (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_OUTPUT
get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE);
#else
get_log_size = elog_async_get_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE);
#endif
if (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_PTHREAD
pthread_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_PTHREAD
thread_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;
}
#endif
return 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 time
snprintf(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提高到+4
vSemaphoreDelete(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_OUTPUT
get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
if (get_log_size) {
elog_port_output(poll_get_buf, get_log_size);
}
} while (get_log_size > 0);
} else {
/* 即使没有信号量,也检查一次是否有积压的日志 */
#ifdef ELOG_ASYNC_LINE_OUTPUT
get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
if (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_OUTPUT
get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
if (get_log_size) {
elog_port_output(poll_get_buf, get_log_size);
}
} while (get_log_size > 0);
} else {
/* 即使没有信号量,也检查一次是否有积压的日志 */
#ifdef ELOG_ASYNC_LINE_OUTPUT
get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#else
get_log_size = elog_async_get_log(poll_get_buf, ELOG_LINE_BUF_SIZE);
#endif
if (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);
}更多推荐
所有评论(0)