分享一个小巧的嵌入式日志模块(附代码)

Posted 嵌入式大杂烩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分享一个小巧的嵌入式日志模块(附代码)相关的知识,希望对你有一定的参考价值。

👇 星标「嵌入式大杂烩」,一起进步!👇

在产品研发的过程中,经常需要借助打印信息来帮助调试和后期维护。所以拥有一个完善的日志模块是至关重要的。

如下是从经手的项目中整理出来的日志模块及使用示例,以备后续项目开发时使用。

log.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <pthread.h>
#include "log.h"
 
/*存储日志的文件名*/
 
static unsigned char g_ucLogFileName[MAX_LOG_FILE_NUM][STR_COMM_SIZE] = 0;
 
/*指明是g_ucLogFileName中的哪个文件*/
static unsigned char g_ucLogFileNo = 0;
 
/*输出日志位置标记,0-输出到终端,1-输出到日志文件*/
unsigned long g_ulPrintLogPlaceFlag = 0;
/*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
unsigned long g_ulPrintDebugLogFlag = 0;
 
/*日志文件大小*/
static unsigned long g_ulLogFileSize = 0;
 
/*日志文件句柄*/
static FILE* pFile = NULL;
 
/*日志存储互斥锁*/
static pthread_mutex_t g_stSaveLogMutexLock;
 
/*日志模块初始化标记*/
static unsigned long g_ulLogInitFlag = 0;
 
 
void LOG_SetPrintLogPlaceFlag(unsigned long flag)

 g_ulPrintLogPlaceFlag = flag;

 
void LOG_SetPrintDebugLogFlag(unsigned long flag)

 g_ulPrintDebugLogFlag = flag;

 
 
/*****************************************************************
** 函数名: get_file_size
** 输 入: char *path
** 输 出:
** 功能描述:获取指令文件大小
** 返回值: long
****************************************************************/
static long get_file_size(const char *path)

 long filesize = -1;
 struct stat statbuff;
 
 if(stat(path, &statbuff) < 0)
     return filesize;
 
 else
     filesize = statbuff.st_size;
 
 return filesize;

 
/*****************************************************************
** 函数名: unsigned long LOG_PrintLogTime
** 输 入:  unsigned long ulBufLen 存储时间的空间长度
** 输 出:unsigned char *ucTime  存储时间
** 功能描述:日志输出
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_PrintLogTime(unsigned char *ucTime, unsigned long ulBufLen)

    struct tm* pstTmSec;
    struct timeval stTmMsec;
 
 if(NULL == ucTime)
 
  return -1;
 
 gettimeofday(&stTmMsec, NULL);
 pstTmSec = localtime(&stTmMsec.tv_sec);
 snprintf((char *)ucTime, ulBufLen - 1, "%04d-%02d-%02d %02d:%02d:%02d %03ldms",
            pstTmSec->tm_year + 1900, pstTmSec->tm_mon + 1, pstTmSec->tm_mday, pstTmSec->tm_hour, 
            pstTmSec->tm_min, pstTmSec->tm_sec, stTmMsec.tv_usec / 1000);
 
 return 0;

 
/*****************************************************************
** 函数名:  unsigned char *LOG_LogTypeToStr
** 输 入:   unsigned char ucType  日志类型
   unsigned long ulBufLen 存储日志类型字符串空间的长度
** 输 出:unsigned char *pucTypeString 根据日志类型将其转换成相应的字符串
** 功能描述:根据日志类型转换成相应的字符串
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_LogTypeToStr(unsigned char ucType, unsigned char *pucTypeString, unsigned long ulBufLen)

 if(NULL == pucTypeString)
 
  return -1;
 
 /*防止发生越界*/
 ulBufLen -= 1;
 
 switch(ucType)
 
  case LOG_DEBUG:
  
   strncpy((char *)pucTypeString, "DEBUG", ulBufLen);
   break;
  
  case LOG_ERROR:
  
   strncpy((char *)pucTypeString, "ERROR", ulBufLen);
   break;
  
  case LOG_WARNING:
  
   strncpy((char *)pucTypeString, "WARNING", ulBufLen);
   break;
  
  case LOG_ACTION:
  
   strncpy((char *)pucTypeString, "ACTION", ulBufLen);
   break;
  
  case LOG_SYSTEM:
  
   strncpy((char *)pucTypeString, "SYSTEM", ulBufLen);
   break;
  
  default:
  
   strncpy((char *)pucTypeString, "UNKNOWN", ulBufLen);
   break;
  
 
 return 0;

 
/*****************************************************************
** 函数名: unsigned long LOG_OpenLogFile
** 输 入:  void
** 输 出:void
** 功能描述:打开日志文件
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_OpenLogFile(void)

 char *path = (char*)g_ucLogFileName[g_ucLogFileNo];
 char *flag = NULL;
 int len = 0;
 
 /*判断文件是否已经打开*/
 if(NULL != pFile)
 
  LOG_PRINT("[ACTION] file opened!");
  return 0;
 
 /*判断文件名是否有定义*/
 if(NULL == path)
 
  LOG_PRINT("[ERROR] file name is NULL.");
  return -1;
 
 
 /*判断文件是否存在*/
 if (!access(path, 0))
 
  /*获取文件大小*/
  if (0 > (len = get_file_size(path)))
  
   LOG_PRINT("[ERROR] get file size failed!");
   return -1;
  
 
 flag = (len > 0 && len < g_ulLogFileSize) ? "a" : "w";
 
 /*打开文件*/
 pFile = fopen(path, flag);
 if(NULL == pFile)
 
  LOG_PRINT("[ERROR] open file failed!");
  return -1;
 
 LOG_PRINT("[DEBUG] open file name = %s", path);
 return 0;

 
/*****************************************************************
** 函数名: LOG_PrintLog
** 输 入:  unsigned char *ucLogInfo  需要打印或者存储的日志信息
   unsigned char ucType 日志类型
** 输 出:void
** 功能描述:日志输出
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_PrintLog(unsigned char ucType, unsigned char *pucLogInfo)

 unsigned long ulResult = 0;
 unsigned long ulFileLen = 0;
 unsigned char ucTime[STR_COMM_SIZE] = 0;
 unsigned char ucLogTypeStr[STR_COMM_SIZE] = 0;
 unsigned char ucLogInfo[STR_MAX_SIZE] = 0;
 
 if(NULL == pucLogInfo)
 
  return -1;
 
 
 /*将日志类型转换成字符串*/
 ulResult = LOG_LogTypeToStr(ucType, ucLogTypeStr, sizeof(ucLogTypeStr));
 /*获取生成日志的时间*/
 ulResult += LOG_PrintLogTime(ucTime, sizeof(ucTime));
 if(0 != ulResult)
 
  return -1;
 
 snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, "[%s] [%s] %s", ucTime, ucLogTypeStr, pucLogInfo);
 /*判断是否打印调试日志*/
 if(PRINT_LOG_TO_TERM == g_ulPrintLogPlaceFlag)
 
  printf("%s", ucLogInfo);
  return 0;
 
 /*加锁保护文件操作*/
 pthread_mutex_lock(&g_stSaveLogMutexLock);
 /*打开日志文件*/
 (void)LOG_OpenLogFile();
 if(NULL != pFile)
 
  fputs((char *)ucLogInfo, pFile);
  ulFileLen = ftell(pFile);
  LOG_PRINT("file len = %ld", ulFileLen);
  if(ulFileLen >= g_ulLogFileSize)
  
   fclose(pFile);
   pFile = NULL;
   g_ucLogFileNo = (g_ucLogFileNo + 1) % MAX_LOG_FILE_NUM;
  
 
 pthread_mutex_unlock(&g_stSaveLogMutexLock);
 return 0;

 
/*****************************************************************
** 函数名: LOG_Init
** 输 入:  const unsigned char* ucLogFileName  用来保存日志的文件名
   unsigned long ulFileSize 存储日志的文件大小
** 输 出:void
** 功能描述:日志打印初始化
** 返回值:unsigned long
****************************************************************/
unsigned long LOG_Init(const unsigned char* ucLogFileName, unsigned long ulFileSize)

 unsigned int i = 0;
 /*判断参数的合法性*/
 if((NULL == ucLogFileName) || !(ulFileSize > 0))
 
  return -1;
 
 /*判断是否将日志输出到日志文件*/
 if((PRINT_LOG_TO_FILE != g_ulPrintLogPlaceFlag) || (0 != g_ulLogInitFlag))
 
  printf("g_ulPrintLogPlaceFlag = %ld g_ulLogInitFlag = %ld\\n",g_ulPrintLogPlaceFlag,g_ulLogInitFlag);
  LOG_PRINT("print log to termination!!");
  return 0;
 
 
 /*记录日志模块已经被初始化(防止改模块被重复初始化)*/
 g_ulLogInitFlag = 1;
 
 /*生成存储日志的文件名*/
 for(i = 0; i < NUMBER(g_ucLogFileName); i++)
 
  snprintf((char *)g_ucLogFileName[i], sizeof(g_ucLogFileName[i]) - 1, "%s_%02d", ucLogFileName, i);
  LOG_PRINT("Log File: %s", g_ucLogFileName[i]);
  printf("Log File: %s\\n", g_ucLogFileName[i]);
 
 /*设置日志文件大小*/
 g_ulLogFileSize = ulFileSize;
 pthread_mutex_init(&g_stSaveLogMutexLock, NULL);
 
 return 0;

 
/*****************************************************************
** 函数名: LOG_Destroy
** 输 入:  void
** 输 出:void
** 功能描述:日志打印资源释放
** 返回值:unsigned long
****************************************************************/
void LOG_Destroy(void)

 if(pFile != NULL)
 
  fclose(pFile);
  pFile = NULL;
 
 pthread_mutex_destroy(&g_stSaveLogMutexLock);
 return;

log.h

#ifndef _LOG_H_
#define _LOG_H_
#include <string.h>
 
/*通用字符串存储大小定义*/
#define STR_COMM_SIZE 128
#define STR_MAX_SIZE 1024
 
#define MAX_LOG_FILE_NUM    (3)
 
#define NUMBER(type) sizeof(type)/sizeof(type[0])
 
#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)
 
/*日志类型*/
enum

    LOG_DEBUG = 0,/*调试日志*/
    LOG_ERROR,/*错误日志*/
    LOG_WARNING,/*告警日志*/
    LOG_ACTION,/*运行日志*/
    LOG_SYSTEM,/*系统日志*/
    BUTTOM
;
 
/*将日志输出到终端*/
#define PRINT_LOG_TO_TERM (0)
/*将日志输出到文件中*/
#define PRINT_LOG_TO_FILE (1)
 
/*调试日志宏定义*/
#define DEBUG_PRINT 0
#define LOG_PRINT(fmt, ...) do\\
 if(DEBUG_PRINT)\\
 \\
  printf(fmt"  [line:%d] [%s]\\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\\
 \\
while(0);
 
/*错误日志打印(在日志打印模块还未启动时使用)*/
#define LOG_ERR(fmt, ...) do\\
 printf("[ERROR]  "fmt"  [line:%d] [%s]\\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\\
while(0);
 
 
 
/*存储日志标记. 0-不存储日志, 1-存储日志*/
extern unsigned long g_ulPrintLogPlaceFlag;
 
/*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
extern unsigned long g_ulPrintDebugLogFlag;
 
unsigned long LOG_PrintLog(unsigned char ucType, unsigned char *pucLogInfo);
 
 
/*日志打印宏定义*/
#define LOG_INFO(type, fmt, ...) do\\
 if(PRINT_LOG_TO_TERM == g_ulPrintLogPlaceFlag) \\
  \\
  if((0 == g_ulPrintDebugLogFlag) && (LOG_DEBUG == type)) \\
  \\
   break;\\
  \\
     unsigned char ucLogInfo[STR_MAX_SIZE] = 0; \\
  snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, fmt"  [%s] [line:%d] [%s]\\n", ##__VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__); \\
  LOG_PrintLog(type, ucLogInfo); \\
  \\
 else \\
  \\
  unsigned char ucLogInfo[STR_MAX_SIZE] = 0; \\
  snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, fmt"  [%s] [line:%d] [%s]\\n", ##__VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__); \\
  LOG_PrintLog(type, ucLogInfo); \\
  \\
while(0)
 
 
/*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
extern void LOG_SetPrintDebugLogFlag(unsigned long flag);
/*存储日志标记. 0-不存储日志, 1-存储日志*/
extern void LOG_SetPrintLogPlaceFlag(unsigned long flag);
 
extern unsigned long LOG_Init(const unsigned char* ucLogFileName, unsigned long ulFileSize);
extern void LOG_Destroy(void);
 
#endif //_LOG_H_

main.c

#include <stdio.h>
#include "log.h"
 
int test_func1()

 LOG_INFO(LOG_DEBUG, "%s", "hello world!!"); 
 return 0;

 
int main(int argc, char *argv[])

 LOG_SetPrintDebugLogFlag(1);//打印调试信息
 //LOG_SetPrintLogPlaceFlag(1);//保存打印信息到文件
 LOG_Init("info", 8000);
 
 LOG_INFO(LOG_DEBUG, "%s", "Init log!!");
 
 test_func1();
 
 LOG_INFO(LOG_DEBUG, "%s", "Destroy log!!");
 LOG_Destroy();

执行结果:

由执行结果可以看出一条打印信息包含了时间、日志类型、内容、文件名、行号、函数名等信息,这样就能够方便快速的定位问题,其使用方法也与printf函数类似,简单方便。

其次该日志模块还支持将日志存至文件,可以自行定义文件的大小及个数,日志循环覆盖。

原文链接:https://blog.csdn.net/fangye945a/article/details/85695764

本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

嵌入式中如何正确使用动态内存?


PC机与嵌入式设备通信协议设计的原则?


如何高效解析不定长度的协议帧?


以上是关于分享一个小巧的嵌入式日志模块(附代码)的主要内容,如果未能解决你的问题,请参考以下文章

分享一个简单且轻量级的日志库:log.c

分享我们用了不到200行代码实现的文件日志系统,极佳的IO性能和高并发支持,附压力测试数据

分享一份嵌入式软件工具清单!

分享一种灵活性很高的协议格式(附代码例子)

分享一种灵活性很高的协议格式(附代码例子)

单片机笔记如何编写一个清晰的串口日志输出DEBUG文件(附源码)