当前位置: 首页 > news >正文

ARM环境日志系统的简单设计思路

在ARM系统开发中(各种架构的嵌入式系统开发也同理),日志系统是至关重要的调试和诊断工具。 一个设计良好的日志系统能够帮助开发者快速定位问题、分析系统行为并监控运行状态。核心需求如下:

1. 多级别日志:支持不同重要程度的日志级别(DEBUG、INFO、WARN、ERROR等)2. 低资源占用:内存占用小,CPU开销低  3. 实时性:不影响主程序实时性能  4. 多种输出方式:支持串口、文件系统、网络等多种输出  5. 时间戳:提供精确的时间信息  6. 线程/任务安全:多任务环境下安全使用  7. 低功耗模式:在节能模式下仍能正常工作

架构设计采用分层架构设计,接口层:提供日志API,处理层:格式化、过滤、分级处理,输出层:控制日志输出目的地。

核心要点:一个分等级的ARM日志系统,包含日志级别管理、格式化输出、时间戳等功能。

日志级别:DEBUG、INFO、WARN、ERROR、FATAL
输出控制:可设置当前日志级别,过滤低级别日志
格式规范:包含时间戳、级别、文件、行号等信息
性能考虑:使用宏定义避免低级别日志的函数调用开销

数据结构

typedef enum 
{LOG_LEVEL_DEBUG = 0,LOG_LEVEL_INFO,LOG_LEVEL_WARNING,LOG_LEVEL_ERROR,LOG_LEVEL_CRITICAL,LOG_LEVEL_NONE // 禁用所有日志
} log_level_t;typedef struct 
{uint32_t timestamp;log_level_t level;uint16_t line_number;const char *filename;const char *function;char message[LOG_MAX_MESSAGE_LENGTH];
} log_entry_t;typedef struct 
{void (*output_func)(const log_entry_t*);log_level_t current_level;bool enabled;uint32_t dropped_count; // 丢弃的日志计数
} logger_t;

核心API

// 日志系统初始化
void log_init(log_level_t default_level, void (*output_func)(const log_entry_t*));// 设置日志级别
void log_set_level(log_level_t level);// 核心日志函数
void log_write(log_level_t level, const char* filename, uint16_t line,const char* function,const char* format, ...);

宏简化调用

#define LOG_DEBUG(format, ...) \log_write(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__)#define LOG_INFO(format, ...) \log_write(LOG_LEVEL_INFO, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__)#define LOG_WARN(format, ...) \log_write(LOG_LEVEL_WARNING, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__)#define LOG_ERROR(format, ...) \log_write(LOG_LEVEL_ERROR, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__)

环形缓冲区实现

/*环形缓冲区实现 为避免日志输出阻塞主程序,实现环形缓冲区*/#define LOG_BUFFER_SIZE 1024typedef struct 
{log_entry_t entries[LOG_BUFFER_SIZE];uint32_t head;uint32_t tail;bool full;
} log_buffer_t;// 初始化缓冲区
void log_buffer_init(log_buffer_t* buffer);// 写入日志到缓冲区
bool log_buffer_put(log_buffer_t* buffer, const log_entry_t* entry);// 从缓冲区读取日志
bool log_buffer_get(log_buffer_t* buffer, log_entry_t* entry);/*输出处理线程 创建专用线程处理日志输出:*/
void log_output_task(void* argument) {log_entry_t entry;while(1) {if(log_buffer_get(&log_buffer, &entry)) {// 调用输出函数if(logger.output_func != NULL) {logger.output_func(&entry);}} else {// 缓冲区空,休眠等待osDelay(10);}}
}/*格式化输出函数*/
void log_format_default(const log_entry_t* entry, char* buffer, size_t size) {const char* level_str;switch(entry->level) {case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;case LOG_LEVEL_INFO: level_str = "INFO"; break;case LOG_LEVEL_WARNING: level_str = "WARN"; break;case LOG_LEVEL_ERROR: level_str = "ERROR"; break;case LOG_LEVEL_CRITICAL: level_str = "CRITICAL"; break;default: level_str = "UNKNOWN"; break;}snprintf(buffer, size, "[%08lu][%s] %s:%d (%s) - %s",entry->timestamp,level_str,entry->filename,entry->line_number,entry->function,entry->message);
}

基础实现的一般用例

可以根据具体的ARM平台需求进行调整,比如替换时间函数、输出函数等。

多级别控制:支持5个标准日志级别;

性能优化:使用宏定义避免低级别日志的函数调用开销;

丰富信息:包含时间戳、文件、行号等定位信息;

可扩展性:支持自定义输出处理器;

线程安全:可扩展为线程安全版本;

格式化支持:支持变参和标准printf格式化;

视觉优化:支持彩色输出(可选);

/*** ARM日志系统* 支持多级别日志输出,包含时间戳和代码位置信息*/#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>// 日志级别定义
typedef enum {LOG_LEVEL_DEBUG = 0,LOG_LEVEL_INFO,LOG_LEVEL_WARN,LOG_LEVEL_ERROR,LOG_LEVEL_FATAL,LOG_LEVEL_NONE  // 禁用所有日志
} log_level_t;// 日志系统配置
typedef struct {log_level_t current_level;int enable_timestamp;int enable_color;void (*output_handler)(const char*);
} log_config_t;// 全局配置
static log_config_t g_log_config = {.current_level = LOG_LEVEL_DEBUG,.enable_timestamp = 1,.enable_color = 1,.output_handler = NULL
};// 颜色定义 (ANSI)
#define COLOR_DEBUG   "\033[36m"  // 青色
#define COLOR_INFO    "\033[32m"  // 绿色
#define COLOR_WARN    "\033[33m"  // 黄色
#define COLOR_ERROR   "\033[31m"  // 红色
#define COLOR_FATAL   "\033[35m"  // 洋红
#define COLOR_RESET   "\033[0m"   // 重置// 级别字符串
static const char* level_strings[] = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};// 级别颜色
static const char* level_colors[] = {COLOR_DEBUG, COLOR_INFO, COLOR_WARN, COLOR_ERROR, COLOR_FATAL
};/*** 初始化日志系统*/
void log_init(log_level_t level, int enable_timestamp, int enable_color, void (*output_handler)(const char*)) {g_log_config.current_level = level;g_log_config.enable_timestamp = enable_timestamp;g_log_config.enable_color = enable_color;g_log_config.output_handler = output_handler;
}/*** 设置日志级别*/
void log_set_level(log_level_t level) {g_log_config.current_level = level;
}/*** 获取当前时间字符串*/
static void get_timestamp_string(char* buffer, int size) {time_t rawtime;struct tm* timeinfo;time(&rawtime);timeinfo = localtime(&rawtime);strftime(buffer, size, "%Y-%m-%d %H:%M:%S", timeinfo);
}/*** 默认输出处理函数*/
static void default_output_handler(const char* message) {printf("%s", message);
}/*** 核心日志函数*/
void log_output(log_level_t level, const char* file, int line, const char* format, ...) {// 级别检查if (level < g_log_config.current_level) {return;}char message[512];char timestamp[32];va_list args;// 获取时间戳if (g_log_config.enable_timestamp) {get_timestamp_string(timestamp, sizeof(timestamp));}// 构建日志前缀int prefix_len = 0;// 时间戳if (g_log_config.enable_timestamp) {prefix_len += snprintf(message + prefix_len, sizeof(message) - prefix_len, "[%s] ", timestamp);}// 级别(带颜色)if (g_log_config.enable_color) {prefix_len += snprintf(message + prefix_len, sizeof(message) - prefix_len, "%s%-5s%s ", level_colors[level], level_strings[level], COLOR_RESET);} else {prefix_len += snprintf(message + prefix_len, sizeof(message) - prefix_len, "%-5s ", level_strings[level]);}// 文件位置prefix_len += snprintf(message + prefix_len, sizeof(message) - prefix_len, "%s:%d: ", file, line);// 格式化日志内容va_start(args, format);vsnprintf(message + prefix_len, sizeof(message) - prefix_len, format, args);va_end(args);// 确保以换行结束int total_len = strlen(message);if (total_len < sizeof(message) - 2 && message[total_len - 1] != '\n') {strcat(message, "\n");}// 输出日志if (g_log_config.output_handler) {g_log_config.output_handler(message);} else {default_output_handler(message);}// FATAL级别退出程序if (level == LOG_LEVEL_FATAL) {exit(1);}
}// 日志宏定义 - 自动获取文件和行号信息
#define LOG_DEBUG(format, ...) \log_output(LOG_LEVEL_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)#define LOG_INFO(format, ...) \log_output(LOG_LEVEL_INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)#define LOG_WARN(format, ...) \log_output(LOG_LEVEL_WARN, __FILE__, __LINE__, format, ##__VA_ARGS__)#define LOG_ERROR(format, ...) \log_output(LOG_LEVEL_ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)#define LOG_FATAL(format, ...) \log_output(LOG_LEVEL_FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)// 断言宏
#define LOG_ASSERT(condition, format, ...) \do { \if (!(condition)) { \LOG_FATAL("Assertion failed: " format, ##__VA_ARGS__); \} \} while(0)// 示例用法
int main() {// 初始化日志系统log_init(LOG_LEVEL_DEBUG, 1, 1, NULL);LOG_DEBUG("这是一条调试信息");LOG_INFO("系统初始化完成");LOG_WARN("内存使用率较高: %d%%", 85);LOG_ERROR("打开文件失败: %s", "test.txt");// 改变日志级别log_set_level(LOG_LEVEL_WARN);LOG_INFO("这条信息不会被显示");  // 级别低于WARN,不会输出LOG_WARN("只有警告和更高级别的日志会显示");// 测试断言int value = 10;LOG_ASSERT(value == 10, "value should be 10, but got %d", value);return 0;
}

这个一般用例可以进行如下高级功能扩展

环形缓冲区日志(避免阻塞)

#include <pthread.h>#define LOG_BUFFER_SIZE 1024
#define LOG_QUEUE_SIZE 100typedef struct {char messages[LOG_QUEUE_SIZE][LOG_BUFFER_SIZE];int head;int tail;int count;pthread_mutex_t mutex;
} log_queue_t;static log_queue_t g_log_queue;void log_queue_init() {g_log_queue.head = 0;g_log_queue.tail = 0;g_log_queue.count = 0;pthread_mutex_init(&g_log_queue.mutex, NULL);
}void log_async_output(const char* message) {pthread_mutex_lock(&g_log_queue.mutex);if (g_log_queue.count < LOG_QUEUE_SIZE) {strncpy(g_log_queue.messages[g_log_queue.tail], message, LOG_BUFFER_SIZE - 1);g_log_queue.messages[g_log_queue.tail][LOG_BUFFER_SIZE - 1] = '\0';g_log_queue.tail = (g_log_queue.tail + 1) % LOG_QUEUE_SIZE;g_log_queue.count++;}pthread_mutex_unlock(&g_log_queue.mutex);
}

文件输出支持

void log_to_file(const char* message) {static FILE* log_file = NULL;if (log_file == NULL) {log_file = fopen("system.log", "a");if (log_file == NULL) {return;}}fprintf(log_file, "%s", message);fflush(log_file);  // 确保立即写入
}

按级别过滤的宏优化版本

// 编译时级别检查 - 完全消除低级别日志的运行时开销
#if LOG_COMPILE_LEVEL <= LOG_LEVEL_DEBUG
#define LOG_DEBUG_OPT(format, ...) \log_output(LOG_LEVEL_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)
#else
#define LOG_DEBUG_OPT(format, ...) ((void)0)
#endif

用例测试

// 初始化示例
void system_init() {// 启用时间戳,禁用颜色,输出到文件log_init(LOG_LEVEL_INFO, 1, 0, log_to_file);LOG_INFO("=== 系统启动 ===");LOG_DEBUG("硬件初始化中...");  // 不会输出,因为级别是INFO
}void process_data(int data) {LOG_DEBUG("处理数据: %d", data);if (data < 0) {LOG_WARN("接收到异常数据: %d", data);}if (data == -1) {LOG_ERROR("数据格式错误");return;}// 关键检查LOG_ASSERT(data >= 0, "数据必须为非负数");
}

日志系统的优化

/*日志过滤 实现基于模块、级别和关键词的过滤:*/
typedef struct {const char* module_name;log_level_t min_level;
} log_filter_t;void log_add_filter(const char* module, log_level_t min_level);
bool log_check_filter(const char* filename, log_level_t level);

(异步日志处理,使用DMA或专用硬件实现异步串口输出,减少CPU占用。)

内存优化 •  使用静态内存分配避免碎片  •  优化字符串存储,避免重复  •  实现日志消息池复用内存

性能优化 •  减少字符串操作,使用memcpy代替strcpy  •  避免在关键路径中调用格式化函数  •  使用位操作代替除法和乘法  

功耗优化 •  实现日志批处理,减少设备唤醒次数  •  在低功耗模式下禁用非关键日志  •  动态调整日志级别基于电源状态

/*日志压缩 在存储到文件系统前进行简单压缩:*/
void log_compress_entry(const log_entry_t* entry, uint8_t* output, size_t* output_size);/*系统状态日志 自动记录关键系统状态:*/
void log_system_status(void) {LOG_INFO("System status - Heap free: %lu, CPU usage: %d%%", get_free_heap(), get_cpu_usage());
}

测试

/*单元测试 编写测试用例验证核心功能:*/
void test_log_level_filtering(void) {log_set_level(LOG_LEVEL_INFO);LOG_DEBUG("This should not appear"); // 应被过滤LOG_INFO("This should appear"); // 应显示// 验证实际输出
}/*性能测试 测量最坏情况下的执行时间和内存使用:*/
void test_log_performance(void) {uint32_t start_time = get_current_time();for(int i = 0; i < 1000; i++) {LOG_INFO("Performance test message %d", i);}uint32_t duration = get_current_time() - start_time;printf("1000 logs took %lu ms\n", duration);
}/*压力测试 测试缓冲区满时的行为:*/
void test_log_stress(void) {log_set_level(LOG_LEVEL_DEBUG);// 快速产生大量日志填满缓冲区for(int i = 0; i < LOG_BUFFER_SIZE * 2; i++) {LOG_DEBUG("Stress test message %d", i);}// 验证系统没有崩溃且记录了丢弃的日志数量
}

部署到RTOS

/*与RTOS集成 提供RTOS特定适配层:*/#ifdef USE_FREERTOS
#include "FreeRTOS.h"
#define LOG_MUTEX_TYPE SemaphoreHandle_t
#define LOG_MUTEX_CREATE() xSemaphoreCreateMutex()
#define LOG_MUTEX_LOCK(m) xSemaphoreTake(m, portMAX_DELAY)
#define LOG_MUTEX_UNLOCK(m) xSemaphoreGive(m)
#endif/*配置系统 提供编译时和运行时配置选项:*/
typedef struct {log_level_t default_level;size_t buffer_size;bool enable_timestamp;bool enable_filename;output_destination_t destination;
} log_config_t;void log_configure(const log_config_t* config);

生产环境考量

•  提供机制动态调整日志级别而不重启系统  •  实现日志远程传输和集中管理  •  添加日志旋转和自动清理功能

可以根据具体项目需求进行扩展和优化。 良好的日志系统不仅能加速调试过程,还能在产品部署后提供宝贵的运行洞察,是嵌入式系统不可或缺的组成部分。通过遵循分层设计原则、实现适当的抽象和提供灵活的配置选项,可以创建出既强大又高效的日志系统,满足从开发调试到生产监控的全生命周期需求。

http://www.dtcms.com/a/445882.html

相关文章:

  • 网站名称推荐大气聚财的公司名字
  • 【JVM】——实战篇
  • 那里有正规网站开发培训学校个人网站是怎么样的
  • 高端网站设计简介推荐外贸网站建设的公司
  • asp.net 网站安装工商核名在哪个网站
  • 基于websocket的多用户网页五子棋(五)
  • 【图像处理基石】什么是全景视觉?
  • 【Linux】线程同步和生产者消费者模型
  • 《嵌入式驱动(三):字符设备驱动开发》
  • 做的网站提示磁盘空间不足投票活动网站怎么做
  • 项目1:FFMPEG推流器讲解(二):FFMPEG输出模块初始化
  • 中级前端进阶方向 框架篇 三十四) 前端自动化测试 + 【步骤落地 + 了解】
  • 【开题答辩全过程】以 python杭州亚运会数据分析与可视化开题为例,包含答辩的问题和答案
  • 中国做外贸网站有哪些网站下雪的效果怎么做的
  • XSLT `<choose>` 元素详解
  • 汽车零部件英语词汇 | 3000 最常用单词系列
  • 深圳优秀网站建设价格网站视频开发平台
  • 菏泽最好的网站建设公司安徽建设工程信息网查
  • Video-of-Thought论文阅读
  • 做下载类型网站怎样划算做网站常用的技术有哪些
  • stp instance 0 cost 5000 概念及题目
  • KVM创建的虚拟机,虚拟机的网卡是如何生成的
  • 网站开发人员结构清新太和做网站
  • 【开题答辩全过程】以 SportsGo健身网站为例,包含答辩的问题和答案
  • Cobalt Strike
  • Java Servlet(三)--- 写一个简单的网站,表白墙程序,登录功能的实现
  • 达梦数据库(DM8)物理备份与还原
  • 【AI论文】OpenGPT-4o-Image:面向高级图像生成与编辑的综合性数据集
  • Pyenv 使用教程:安装与卸载
  • 告别PECL,拥抱PIE:像Composer一样管理PHP扩展