51Testing软件测试论坛

标题: Linux C 日志模块(附代码)给有用的人 [打印本页]

作者: 测试积点老人    时间: 2023-8-14 13:08
标题: Linux C 日志模块(附代码)给有用的人

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

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

log.c
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/time.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include <ctype.h>
  10. #include <pthread.h>
  11. #include "log.h"

  12. /*存储日志的文件名*/

  13. static unsigned char g_ucLogFileName[MAX_LOG_FILE_NUM][STR_COMM_SIZE] = {{0}};

  14. /*指明是g_ucLogFileName中的哪个文件*/
  15. static unsigned char g_ucLogFileNo = 0;

  16. /*输出日志位置标记,0-输出到终端,1-输出到日志文件*/
  17. unsigned long g_ulPrintLogPlaceFlag = 0;
  18. /*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
  19. unsigned long g_ulPrintDebugLogFlag = 0;

  20. /*日志文件大小*/
  21. static unsigned long g_ulLogFileSize = 0;

  22. /*日志文件句柄*/
  23. static FILE* pFile = NULL;

  24. /*日志存储互斥锁*/
  25. static pthread_mutex_t g_stSaveLogMutexLock;

  26. /*日志模块初始化标记*/
  27. static unsigned long g_ulLogInitFlag = 0;


  28. void LOG_SetPrintLogPlaceFlag(unsigned long flag)
  29. {
  30. g_ulPrintLogPlaceFlag = flag;
  31. }

  32. void LOG_SetPrintDebugLogFlag(unsigned long flag)
  33. {
  34. g_ulPrintDebugLogFlag = flag;
  35. }


  36. /*****************************************************************
  37. ** 函数名: get_file_size
  38. ** 输 入: char *path
  39. ** 输 出:
  40. ** 功能描述:获取指令文件大小
  41. ** 返回值: long
  42. ****************************************************************/
  43. static long get_file_size(const char *path)
  44. {
  45. long filesize = -1;
  46. struct stat statbuff;

  47. if(stat(path, &statbuff) < 0){
  48.      return filesize;
  49. }
  50. else{
  51.      filesize = statbuff.st_size;
  52. }
  53. return filesize;
  54. }

  55. /*****************************************************************
  56. ** 函数名: unsigned long LOG_PrintLogTime
  57. ** 输 入:  unsigned long ulBufLen 存储时间的空间长度
  58. ** 输 出:unsigned char *ucTime  存储时间
  59. ** 功能描述:日志输出
  60. ** 返回值:unsigned long
  61. ****************************************************************/
  62. unsigned long LOG_PrintLogTime(unsigned char *ucTime, unsigned long ulBufLen)
  63. {
  64.     struct tm* pstTmSec;
  65.     struct timeval stTmMsec;

  66. if(NULL == ucTime)
  67. {
  68.   return -1;
  69. }
  70. gettimeofday(&stTmMsec, NULL);
  71. pstTmSec = localtime(&stTmMsec.tv_sec);
  72. snprintf((char *)ucTime, ulBufLen - 1, "%04d-%02d-%02d %02d:%02d:%02d %03ldms",
  73.             pstTmSec->tm_year + 1900, pstTmSec->tm_mon + 1, pstTmSec->tm_mday, pstTmSec->tm_hour,
  74.             pstTmSec->tm_min, pstTmSec->tm_sec, stTmMsec.tv_usec / 1000);

  75. return 0;
  76. }

  77. /*****************************************************************
  78. ** 函数名:  unsigned char *LOG_LogTypeToStr
  79. ** 输 入:   unsigned char ucType  日志类型
  80.    unsigned long ulBufLen 存储日志类型字符串空间的长度
  81. ** 输 出:unsigned char *pucTypeString 根据日志类型将其转换成相应的字符串
  82. ** 功能描述:根据日志类型转换成相应的字符串
  83. ** 返回值:unsigned long
  84. ****************************************************************/
  85. unsigned long LOG_LogTypeToStr(unsigned char ucType, unsigned char *pucTypeString, unsigned long ulBufLen)
  86. {
  87. if(NULL == pucTypeString)
  88. {
  89.   return -1;
  90. }
  91. /*防止发生越界*/
  92. ulBufLen -= 1;

  93. switch(ucType)
  94. {
  95.   case LOG_DEBUG:
  96.   {
  97.    strncpy((char *)pucTypeString, "DEBUG", ulBufLen);
  98.    break;
  99.   }
  100.   case LOG_ERROR:
  101.   {
  102.    strncpy((char *)pucTypeString, "ERROR", ulBufLen);
  103.    break;
  104.   }
  105.   case LOG_WARNING:
  106.   {
  107.    strncpy((char *)pucTypeString, "WARNING", ulBufLen);
  108.    break;
  109.   }
  110.   case LOG_ACTION:
  111.   {
  112.    strncpy((char *)pucTypeString, "ACTION", ulBufLen);
  113.    break;
  114.   }
  115.   case LOG_SYSTEM:
  116.   {
  117.    strncpy((char *)pucTypeString, "SYSTEM", ulBufLen);
  118.    break;
  119.   }
  120.   default:
  121.   {
  122.    strncpy((char *)pucTypeString, "UNKNOWN", ulBufLen);
  123.    break;
  124.   }
  125. }
  126. return 0;
  127. }

  128. /*****************************************************************
  129. ** 函数名: unsigned long LOG_OpenLogFile
  130. ** 输 入:  void
  131. ** 输 出:void
  132. ** 功能描述:打开日志文件
  133. ** 返回值:unsigned long
  134. ****************************************************************/
  135. unsigned long LOG_OpenLogFile(void)
  136. {
  137. char *path = (char*)g_ucLogFileName[g_ucLogFileNo];
  138. char *flag = NULL;
  139. int len = 0;

  140. /*判断文件是否已经打开*/
  141. if(NULL != pFile)
  142. {
  143.   LOG_PRINT("[ACTION] file opened!");
  144.   return 0;
  145. }
  146. /*判断文件名是否有定义*/
  147. if(NULL == path)
  148. {
  149.   LOG_PRINT("[ERROR] file name is NULL.");
  150.   return -1;
  151. }

  152. /*判断文件是否存在*/
  153. if (!access(path, 0))
  154. {
  155.   /*获取文件大小*/
  156.   if (0 > (len = get_file_size(path)))
  157.   {
  158.    LOG_PRINT("[ERROR] get file size failed!");
  159.    return -1;
  160.   }
  161. }
  162. flag = (len > 0 && len < g_ulLogFileSize) ? "a" : "w";

  163. /*打开文件*/
  164. pFile = fopen(path, flag);
  165. if(NULL == pFile)
  166. {
  167.   LOG_PRINT("[ERROR] open file failed!");
  168.   return -1;
  169. }
  170. LOG_PRINT("[DEBUG] open file name = %s", path);
  171. return 0;
  172. }

  173. /*****************************************************************
  174. ** 函数名: LOG_PrintLog
  175. ** 输 入:  unsigned char *ucLogInfo  需要打印或者存储的日志信息
  176.    unsigned char ucType 日志类型
  177. ** 输 出:void
  178. ** 功能描述:日志输出
  179. ** 返回值:unsigned long
  180. ****************************************************************/
  181. unsigned long LOG_PrintLog(unsigned char ucType, unsigned char *pucLogInfo)
  182. {
  183. unsigned long ulResult = 0;
  184. unsigned long ulFileLen = 0;
  185. unsigned char ucTime[STR_COMM_SIZE] = {0};
  186. unsigned char ucLogTypeStr[STR_COMM_SIZE] = {0};
  187. unsigned char ucLogInfo[STR_MAX_SIZE] = {0};

  188. if(NULL == pucLogInfo)
  189. {
  190.   return -1;
  191. }

  192. /*将日志类型转换成字符串*/
  193. ulResult = LOG_LogTypeToStr(ucType, ucLogTypeStr, sizeof(ucLogTypeStr));
  194. /*获取生成日志的时间*/
  195. ulResult += LOG_PrintLogTime(ucTime, sizeof(ucTime));
  196. if(0 != ulResult)
  197. {
  198.   return -1;
  199. }
  200. snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, "[%s] [%s] %s", ucTime, ucLogTypeStr, pucLogInfo);
  201. /*判断是否打印调试日志*/
  202. if(PRINT_LOG_TO_TERM == g_ulPrintLogPlaceFlag)
  203. {
  204.   printf("%s", ucLogInfo);
  205.   return 0;
  206. }
  207. /*加锁保护文件操作*/
  208. pthread_mutex_lock(&g_stSaveLogMutexLock);
  209. /*打开日志文件*/
  210. (void)LOG_OpenLogFile();
  211. if(NULL != pFile)
  212. {
  213.   fputs((char *)ucLogInfo, pFile);
  214.   ulFileLen = ftell(pFile);
  215.   LOG_PRINT("file len = %ld", ulFileLen);
  216.   if(ulFileLen >= g_ulLogFileSize)
  217.   {
  218.    fclose(pFile);
  219.    pFile = NULL;
  220.    g_ucLogFileNo = (g_ucLogFileNo + 1) % MAX_LOG_FILE_NUM;
  221.   }
  222. }
  223. pthread_mutex_unlock(&g_stSaveLogMutexLock);
  224. return 0;
  225. }

  226. /*****************************************************************
  227. ** 函数名: LOG_Init
  228. ** 输 入:  const unsigned char* ucLogFileName  用来保存日志的文件名
  229.    unsigned long ulFileSize 存储日志的文件大小
  230. ** 输 出:void
  231. ** 功能描述:日志打印初始化
  232. ** 返回值:unsigned long
  233. ****************************************************************/
  234. unsigned long LOG_Init(const unsigned char* ucLogFileName, unsigned long ulFileSize)
  235. {
  236. unsigned int i = 0;
  237. /*判断参数的合法性*/
  238. if((NULL == ucLogFileName) || !(ulFileSize > 0))
  239. {
  240.   return -1;
  241. }
  242. /*判断是否将日志输出到日志文件*/
  243. if((PRINT_LOG_TO_FILE != g_ulPrintLogPlaceFlag) || (0 != g_ulLogInitFlag))
  244. {
  245.   printf("g_ulPrintLogPlaceFlag = %ld g_ulLogInitFlag = %ld\n",g_ulPrintLogPlaceFlag,g_ulLogInitFlag);
  246.   LOG_PRINT("print log to termination!!");
  247.   return 0;
  248. }

  249. /*记录日志模块已经被初始化(防止改模块被重复初始化)*/
  250. g_ulLogInitFlag = 1;

  251. /*生成存储日志的文件名*/
  252. for(i = 0; i < NUMBER(g_ucLogFileName); i++)
  253. {
  254.   snprintf((char *)g_ucLogFileName[i], sizeof(g_ucLogFileName[i]) - 1, "%s_%02d", ucLogFileName, i);
  255.   LOG_PRINT("Log File: %s", g_ucLogFileName[i]);
  256.   printf("Log File: %s\n", g_ucLogFileName[i]);
  257. }
  258. /*设置日志文件大小*/
  259. g_ulLogFileSize = ulFileSize;
  260. pthread_mutex_init(&g_stSaveLogMutexLock, NULL);

  261. return 0;
  262. }

  263. /*****************************************************************
  264. ** 函数名: LOG_Destroy
  265. ** 输 入:  void
  266. ** 输 出:void
  267. ** 功能描述:日志打印资源释放
  268. ** 返回值:unsigned long
  269. ****************************************************************/
  270. void LOG_Destroy(void)
  271. {
  272. if(pFile != NULL)
  273. {
  274.   fclose(pFile);
  275.   pFile = NULL;
  276. }
  277. pthread_mutex_destroy(&g_stSaveLogMutexLock);
  278. return;
  279. }
复制代码





作者: 测试积点老人    时间: 2023-8-14 13:09

log.h
  1. #ifndef _LOG_H_
  2. #define _LOG_H_
  3. #include <string.h>

  4. /*通用字符串存储大小定义*/
  5. #define STR_COMM_SIZE 128
  6. #define STR_MAX_SIZE 1024

  7. #define MAX_LOG_FILE_NUM    (3)

  8. #define NUMBER(type) sizeof(type)/sizeof(type[0])

  9. #define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)

  10. /*日志类型*/
  11. enum
  12. {
  13.     LOG_DEBUG = 0,/*调试日志*/
  14.     LOG_ERROR,/*错误日志*/
  15.     LOG_WARNING,/*告警日志*/
  16.     LOG_ACTION,/*运行日志*/
  17.     LOG_SYSTEM,/*系统日志*/
  18.     BUTTOM
  19. };

  20. /*将日志输出到终端*/
  21. #define PRINT_LOG_TO_TERM (0)
  22. /*将日志输出到文件中*/
  23. #define PRINT_LOG_TO_FILE (1)

  24. /*调试日志宏定义*/
  25. #define DEBUG_PRINT 0
  26. #define LOG_PRINT(fmt, ...) do{\
  27. if(DEBUG_PRINT)\
  28. {\
  29.   printf(fmt"  [line:%d] [%s]\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\
  30. }\
  31. }while(0);

  32. /*错误日志打印(在日志打印模块还未启动时使用)*/
  33. #define LOG_ERR(fmt, ...) do{\
  34. printf("[ERROR]  "fmt"  [line:%d] [%s]\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\
  35. }while(0);



  36. /*存储日志标记. 0-不存储日志, 1-存储日志*/
  37. extern unsigned long g_ulPrintLogPlaceFlag;

  38. /*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
  39. extern unsigned long g_ulPrintDebugLogFlag;

  40. unsigned long LOG_PrintLog(unsigned char ucType, unsigned char *pucLogInfo);


  41. /*日志打印宏定义*/
  42. #define LOG_INFO(type, fmt, ...) do{\
  43. if(PRINT_LOG_TO_TERM == g_ulPrintLogPlaceFlag) \
  44. { \
  45.   if((0 == g_ulPrintDebugLogFlag) && (LOG_DEBUG == type)) \
  46.   {\
  47.    break;\
  48.   }\
  49.      unsigned char ucLogInfo[STR_MAX_SIZE] = {0}; \
  50.   snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, fmt"  [%s] [line:%d] [%s]\n", ##__VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__); \
  51.   LOG_PrintLog(type, ucLogInfo); \
  52. } \
  53. else \
  54. { \
  55.   unsigned char ucLogInfo[STR_MAX_SIZE] = {0}; \
  56.   snprintf((char *)ucLogInfo, sizeof(ucLogInfo) - 1, fmt"  [%s] [line:%d] [%s]\n", ##__VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__); \
  57.   LOG_PrintLog(type, ucLogInfo); \
  58. } \
  59. }while(0)


  60. /*是否打印调试日志标记,0-不打印调试日志,1-打印调试日志*/
  61. extern void LOG_SetPrintDebugLogFlag(unsigned long flag);
  62. /*存储日志标记. 0-不存储日志, 1-存储日志*/
  63. extern void LOG_SetPrintLogPlaceFlag(unsigned long flag);

  64. extern unsigned long LOG_Init(const unsigned char* ucLogFileName, unsigned long ulFileSize);
  65. extern void LOG_Destroy(void);

  66. #endif //_LOG_H_
复制代码
main.c
  1. #include <stdio.h>
  2. #include "log.h"

  3. int test_func1()
  4. {
  5. LOG_INFO(LOG_DEBUG, "%s", "hello world!!");
  6. return 0;
  7. }

  8. int main(int argc, char *argv[])
  9. {
  10. LOG_SetPrintDebugLogFlag(1);//打印调试信息
  11. //LOG_SetPrintLogPlaceFlag(1);//保存打印信息到文件
  12. LOG_Init("info", 8000);

  13. LOG_INFO(LOG_DEBUG, "%s", "Init log!!");

  14. test_func1();

  15. LOG_INFO(LOG_DEBUG, "%s", "Destroy log!!");
  16. LOG_Destroy();
  17. }
复制代码

执行结果:

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

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







欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2