1.日志的重要性
-
故障排查与调试:嵌入式系统通常运行在资源有限的环境中,故障排查和调试变得尤为复杂。日志系统可以记录系统在运行过程中的各种操作、状态和事件信息,方便开发人员追踪和定位问题所在。通过分析日志,可以快速找到故障源,并进行相应的修复和调试。
-
系统性能优化:嵌入式系统的资源有限,因此性能优化尤为关键。日志系统可以记录系统运行过程中的性能指标,如任务执行时间、资源利用率等。通过分析这些日志,可以发现系统性能瓶颈,进行性能优化和资源管理,提高系统的响应速度和资源利用效率。
-
资源监控与管理:嵌入式系统可能包含多个任务或模块并发运行,而每个任务和模块都可能占用不同的资源。日志系统可以记录各个任务和模块的资源使用情况,如内存分配情况、任务运行状态等。通过分析日志,可以进行资源的动态监控和管理,以确保嵌入式系统的稳定运行。
-
安全审计与故障恢复:嵌入式系统在一些关键领域,如工业控制、医疗设备等,安全性和可靠性要求很高。日志系统可以记录系统的操作行为、故障事件、安全事件等,方便进行安全审计和故障恢复。通过分析日志,可以快速发现安全漏洞和故障点,并采取相应的措施进行修复和恢复。
-
协助产品迭代和升级:通过收集和分析日志数据,可以了解用户的使用习惯、问题反馈和需求变化。这些信息可以用于产品的迭代和升级,提供更好的用户体验和功能优化。
2.代码设计
#ifndef _CJ_LOG_H_
#define _CJ_LOG_H_
#include <stdbool.h>
#define MAX_LOG_QUEUE_CNT 10
typedef enum _LogType
{
LT_DEBUG = 0,
LT_INFO = 1,
LT_WARNING = 2,
LT_ERROR = 3,
}LogType;
typedef struct _CjLog
{
unsigned int timestamp; //时间戳
LogType type; //类别
unsigned char para1; //参数1
unsigned char para2; //参数2
unsigned char para3; //参数3
unsigned char para4; //参数4
int checkSum; //校验和
}CjLog;
void clog_Init();
int clog_addlog(CjLog *log);
int clog_getLog(unsigned int startTime, unsigned int endTime, unsigned char *buf);
bool clog_getLog(unsigned int index, unsigned char *buf);
int clog_getCnt();
void clog_clear();
#endif
#include "cjlog.h"
#include "freertos/queue.h"
/* 创建一个日志队列 */
static QueueHandle_t log_queue;
static void clog_write_thread(void)
{
uart_event_t event;
CjLog receivedLog;
int len = 0;
printf("clog_write_thread start !\n");
while (1)
{
BaseType_t xStatus = xQueueReceive(log_queue, &receivedLog, portMAX_DELAY);
if (xStatus == pdPASS)
{
printf("Get a new log!\n");
/* 写日志函数(需校验checkSum) 具体根据实际平台去实现 */
if(flash_writeLog(receivedLog) == false)
printf("Write log failed!\n");
}
else
{
}
}
return;
}
void clog_Init()
{
log_queue = xQueueCreate(MAX_LOG_QUEUE_CNT, sizeof(CjLog));
/* 创建一个接收线程 */
xTaskCreate(clog_write_thread, "log_thread", 2048, NULL, configMAX_PRIORITIES, NULL);
}
void clog_addlog(CjLog *log)
{
xQueueSend(log_queue, &log, portMAX_DELAY);
}
int clog_getLog(unsigned int startTime, unsigned int endTime, unsigned char *buf)
{
if(endTime == 0)
endTime = 0xffffffff;
/* 读取当前日志数量 具体根据实际平台去实现 */
const int logCount = readLogCnt();
int cnt = 0;
//从最新的日志扫描至最旧的
for(int i = 0; i < logCount; i++)
{
/* 读取当前日志 也需要根据平台去实现 */
CjLog *log = flash_readLog(i);
if(log)
{
if(log->timestamp <= endTime && log->timestamp >= startTime)
{
*buf++ = (unsigned char)(log->timestamp >> 24);
*buf++ = (unsigned char)(log->timestamp >> 16);
*buf++ = (unsigned char)(log->timestamp >> 8);
*buf++ = (unsigned char)(log->timestamp);
*buf++ = (unsigned char)log->type;
*buf++ = log->para1;
*buf++ = log->para2;
*buf++ = log->para3;
*buf++ = log->para4;
cnt ++;
}
else
{
if(log->timestamp < startTime)
break;
}
}
else
break;
}
return cnt;
}
bool clog_getLog(unsigned int index, unsigned char *buf)
{
/* 读取当前日志数量 具体根据实际平台去实现 */
const int logCount = flash_readLogCnt();
if(index > logCount)
{
return false;
}
/* 读取当前日志 也需要根据平台去实现 */
CjLog *log = flash_readLog(index);
if(log)
{
*buf++ = (unsigned char)(log->timestamp >> 24);
*buf++ = (unsigned char)(log->timestamp >> 16);
*buf++ = (unsigned char)(log->timestamp >> 8);
*buf++ = (unsigned char)(log->timestamp);
*buf++ = (unsigned char)log->type;
*buf++ = log->para1;
*buf++ = log->para2;
*buf++ = log->para3;
*buf++ = log->para4;
return true;
}
else
return false;
}
int clog_getCnt()
{
/* 读取当前日志数量 具体根据实际平台去实现 */
return flash_readLogCnt();
}
void clog_clear()
{
/* 清空也需要根据平台去实现 */
flash_clearAllLog();
}
3.注意事项
1.与flash有关的函数的实现需要与实际情况结合。
2.flash读的时候要进行校验。
3.日志结构体可以根据实际的flash芯片设计大小,字节对齐等。
/* 4字节对齐 这里刚好是 4个 4字节 */
typedef struct _CjLog
{
unsigned int timestamp; //时间戳
LogType type; //类别
unsigned char para1; //参数1
unsigned char para2; //参数2
unsigned char para3; //参数3
unsigned char para4; //参数4
int checkSum; //校验和
}CjLog;