手撸了一个超轻量级的日志系统

发布时间 2023-09-11 15:23:29作者: ywx-super

  最近手撸了一个超级轻量级的日志系统,以适应项目需求。纯 C 语言编写,实现代码总共才500多行,关键功能还齐全,对于嵌入式开发的猿猿们,再也不用寻找其它开源的日志系统,简直就是福音。话不多说,首先看看功能和优缺点,如果达不到各位猿猿们的要求,可直接跳过。

  日志功能:

  1. 支持日志路径可配,不存在自动创建;
  2. 支持日志级别,按日志优先级打印;
  3. 支持打印多种输出方式(串口和网络方式输出待完善);
  4. 支持日志文件大小和数量可配置,日志文件滚动;
  5. 支持日志输出超过磁盘限制大小改变输出方式;
  6. 支持按模块来打印日志,每个模块可独立配置和打印日志;
  7. 支持多线程输出日志。

  日志优点:

  1. 超级轻量,代码总共才500多行,接口简单易用;
  2. 性能极高,内部无缓存,内存占用可忽略,对嵌入式开发极为友好;
  3. 可根据项目要求高度定制

  日志缺点:

  1. 日志输出格式固定,不能配置(一般项目上配置好之后也不会轻易修改);
  2. 模块内多线程打印有资源竞争时会等待锁,有一定的业务实时性影响。

  日志配置文件:

 1 #全局日志配置
 2 #日志存储路径,不存在时会自动创建,默认“./log”
 3 log_path = "./log"
 4 
 5 #日志实时输出开关 0:关闭,1:开启
 6 log_flush = 1
 7 
 8 #日志文件保存数量,超过数量会滚动删除,配置范围[0, 65535]
 9 log_file_count = 5
10 
11 #日志文件单个大小,单位:MB,超过大小切换文件,配置范围(0, 65535]
12 log_file_size = 30
13 
14 #日志预留空间大小,单位:MB,达到阈值日志输出到终端,配置范围[0, +∞)
15 log_reserve_space = 200
16 
17 #各模块日志配置,有几个模块配置几个
18 #日志输出类型:0:off,1:console,2:file,4:serial,8:net,可叠加
19 log_A_output = 2
20 log_B_output = 2
21 log_C_output = 2
22 log_D_output = 2
23 
24 #日志等级类型:0:trace,1:debug,2:info,3:warn,4:error,5:fatal,6:close
25 log_A_level = 2
26 log_B_level = 2
27 log_C_level = 2
28 log_D_level = 2

  日志头文件

  1 /**
  2 * Copyright (c) 2023 yuwanxian
  3 *     All rights reserved.
  4 *
  5 * File: loglite.h
  6 * Author: yuwanxian
  7 * Time: 2023/07/07
  8 * Version: 1.0.0
  9 *
 10 * Loglite interface declaration
 11 *
 12 */
 13 
 14 #ifndef _LOGLITE_H_
 15 #define _LOGLITE_H_
 16 
 17 #include <string.h>
 18 
 19 #ifdef __cplusplus
 20 extern "C" {
 21 #endif
 22 
 23 
 24 /**
 25 * @brief 模块ID
 26 */
 27 typedef enum {
 28     MODULE_A,
 29     MODULE_B,
 30     MODULE_C,
 31     MODULE_D,
 32     MODULE_ID_MAX
 33 } MODULE_ID;
 34 
 35 /**
 36 * @brief 日志等级
 37 */
 38 typedef enum {
 39     LOG_TRACE,
 40     LOG_DEBUG,
 41     LOG_INFO,
 42     LOG_WARN,
 43     LOG_ERROR,
 44     LOG_FATAL,
 45     LOG_CLOSE,
 46     LOG_LEVEL_MAX
 47 } LOG_LEVEL;
 48 
 49 /**
 50 * @brief 日志初始化
 51 *
 52 * @param config [in] 日志配置文件路径
 53 *
 54 * @return int 0:成功,其它:失败
 55 */
 56 int log_init(const char* config);
 57 
 58 /**
 59 * @brief 日志输出
 60 *
 61 * @param level  [in] 日志等级
 62 * @param id     [in] 日志模块ID
 63 * @param file   [in] 日志输出时所在文件名
 64 * @param line   [in] 日志输出时所在代码行数
 65 * @param func   [in] 日志输出时所在函数名
 66 * @param format [in] 日志格式化字符串
 67 * @param suffix [in] 日志后缀,可自定义
 68 * @param ...    [in] 日志格式化可变参数
 69 *
 70 * @return void
 71 */
 72 void logging(LOG_LEVEL level, MODULE_ID id, const char* file, int line, 
 73     const char* func, const char* format, const char* suffix, ...);
 74 
 75 /**
 76 * @brief 日志结束
 77 *
 78 * @param void
 79 *
 80 * @return int 0:成功,其它:失败
 81 */
 82 int log_drop(void);
 83 
 84 
 85 /**
 86 * @brief 日志打印宏,子模块可根据需要再定义
 87 *
 88 * @example 参考子模块自定义日志打印宏
 89 */
 90 #define LOG_FILE_NAME (strrchr(__FILE__, '/')+1)
 91 #define LOG_VAARGS_EX LOG_FILE_NAME, __LINE__, __func__
 92 
 93 #define LOGT(id, format, suffix, ...)\
 94         logging(LOG_TRACE, id, LOG_VAARGS_EX, format, suffix, ##__VA_ARGS__)
 95 
 96 #define LOGD(id, format, suffix, ...)\
 97         logging(LOG_DEBUG, id, LOG_VAARGS_EX, format, suffix, ##__VA_ARGS__)
 98 
 99 #define LOGI(id, format, suffix, ...)\
100         logging(LOG_INFO,  id, LOG_VAARGS_EX, format, suffix, ##__VA_ARGS__)
101 
102 #define LOGW(id, format, suffix, ...)\
103         logging(LOG_WARN,  id, LOG_VAARGS_EX, format, suffix, ##__VA_ARGS__)
104 
105 #define LOGE(id, format, suffix, ...)\
106         logging(LOG_ERROR, id, LOG_VAARGS_EX, format, suffix, ##__VA_ARGS__)
107         
108 #define LOGF(id, format, suffix, ...)\
109         logging(LOG_FATAL, id, LOG_VAARGS_EX, format, suffix, ##__VA_ARGS__)
110 
111 /**
112 * @brief 子模块自定义日志打印宏
113 *
114 * @note 注意:1.日志自带换行,2.结尾自带分号
115 */
116 #define A_LOGT(format, ...) LOGT(MODULE_A, format, "\n", ##__VA_ARGS__);
117 #define A_LOGD(format, ...) LOGD(MODULE_A, format, "\n", ##__VA_ARGS__);
118 #define A_LOGI(format, ...) LOGI(MODULE_A, format, "\n", ##__VA_ARGS__);
119 #define A_LOGW(format, ...) LOGW(MODULE_A, format, "\n", ##__VA_ARGS__);
120 #define A_LOGE(format, ...) LOGE(MODULE_A, format, "\n", ##__VA_ARGS__);
121 #define A_LOGF(format, ...) LOGF(MODULE_A, format, "\n", ##__VA_ARGS__);
122 
123 
124 #ifdef __cplusplus
125 }
126 #endif
127 
128 #endif // _LOGLITE_H_
loglite.h

  日志源文件

  1 /**
  2 * Copyright (c) 2023 yuwanxian
  3 *     All rights reserved.
  4 *
  5 * File: loglite.c
  6 * Author: yuwanxian
  7 * Time: 2023/07/07
  8 * Version: 1.0.0
  9 * 
 10 * Loglite interface implementation
 11 *
 12 */
 13 #include "loglite.h"
 14 
 15 #include <stdio.h>
 16 #include <stdlib.h>
 17 #include <stdarg.h>
 18 #include <unistd.h>
 19 #include <dirent.h>
 20 #include <sys/stat.h>
 21 #include <sys/statvfs.h>
 22 #include <sys/time.h>
 23 #include <time.h>
 24 #include <pthread.h>
 25 
 26 #define PATH_MAX_LEN 200U
 27 #define FILE_MAX_LEN 256U
 28 #define BINARY_UNIT 1000U
 29 
 30 typedef enum {
 31     LOG_OFF = 0,
 32     LOG_CONSOLE = 1,
 33     LOG_FILE = 2,
 34     LOG_SERIAL = 4,
 35     LOG_NET = 8,
 36 } LOG_OUTPUT;
 37 
 38 typedef struct {
 39     char path[PATH_MAX_LEN];
 40     uint16_t output_flush;
 41     uint16_t file_count;
 42     uint16_t file_size;
 43     uint32_t reserve_space;
 44 } GlobalLogParam;
 45 
 46 typedef struct {
 47     FILE* file;
 48     char  name[16];
 49     uint16_t file_num;
 50     LOG_LEVEL  level;
 51     LOG_OUTPUT output;
 52     pthread_mutex_t mutex;
 53 } ModuleLogParam;
 54 
 55 static GlobalLogParam g_global_param = {
 56     "./log", 1, 5, 30, 100
 57 };
 58 
 59 static ModuleLogParam g_module_param[MODULE_ID_MAX] = {
 60     { NULL, "A", 0, LOG_INFO, LOG_FILE, PTHREAD_MUTEX_INITIALIZER },
 61     { NULL, "B", 0, LOG_INFO, LOG_FILE, PTHREAD_MUTEX_INITIALIZER },
 62     { NULL, "C", 0, LOG_INFO, LOG_FILE, PTHREAD_MUTEX_INITIALIZER },
 63     { NULL, "D", 0, LOG_INFO, LOG_FILE, PTHREAD_MUTEX_INITIALIZER }
 64 };
 65 
 66 static char const* const g_log_level[LOG_LEVEL_MAX] = {
 67     "TRACE", "DEBUG", "INFO", "WRAN", "ERROR", "FATAL", "CLOSE"
 68 };
 69 
 70 // remove spaces and endl
 71 static void trim(char* str)
 72 {
 73     char* start = str;
 74     char* end = str;
 75 
 76     while (' ' == *start) {
 77         start++;
 78     }
 79 
 80     while (*end != '\r' && *end != '\n' && *end != '\0') {
 81         end++;
 82     }
 83 
 84     int i = 0
 85     for (; start < end; i++, start++) {
 86         str[i] = *start;
 87     }
 88 
 89     str[i] = '\0';
 90 }
 91 
 92 // Reading the configuration of numerical types
 93 static void get_config_number(const char* data, const char* name, void* dst, uint16_t width)
 94 {
 95     if (strstr(data, name) != NULL)
 96     {
 97         char* pos = (char*)strstr(data, "=");
 98         if (pos != NULL)
 99         {
100             pos += 1;
101             trim(pos);
102             
103             char* endptr;
104             long value = strtol(pos, &endptr, 10);
105             if ('\0' == *endptr)
106             {
107                 memcpy(dst, &value, width);
108             }
109         }
110     }
111 }
112 
113 // Reading the configuration of string types
114 static void get_config_string(const char* data, const char* name, char* dst, uint16_t length)
115 {
116     if (strstr(data, name) != NULL)
117     {
118         const char* posBeg = strstr(data, "\"");
119         const char* posEnd = strrchr(data, '\"');
120         if (posBeg != NULL && posEnd != NULL)
121         {
122             size_t size = posEnd - posBeg - 1U;
123             size = size > length ? length : size;
124             strncpy(dst, posBeg + 1U, size);
125         }
126     }    
127 }
128 
129 static char* get_log_time(char time[32])
130 {
131     struct timeval tm;
132     gettimeofday(&tm, NULL);
133     
134     struct tm* ptm = localtime(&tm.tv_sec);
135     strftime(time, 27, "%Y-%m-%d %H:%M:%S", ptm);
136     
137     uint32_t msec = tm.tv_usec / 1000U;
138     sprintf(&time[strlen(time)], ".%03d", msec);
139     
140     return time;
141 }
142 
143 static void rolling_log_file(MODULE_ID id)
144 {
145     // 1.close logfile
146     if (g_module_param[id].file != NULL)
147     {
148         fflush(g_module_param[id].file);
149         fclose(g_module_param[id].file);
150         g_module_param[id].file = NULL;
151     }
152     
153     // 2.remove excess logfile
154     if (g_module_param[id].file_num == g_global_param.file_count)
155     {
156         char oldfile[FILE_MAX_LEN] = { 0 };
157         if (0 == g_global_param.file_count)
158         {
159             snprintf(oldfile, sizeof(oldfile), "%s/%s.log",
160                 g_global_param.path, g_module_param[id].name);
161         }
162         else
163         {
164             snprintf(oldfile, sizeof(oldfile), "%s/%s_%d.log",
165                 g_global_param.path, g_module_param[id].name, g_global_param.file_count);
166         }
167         
168         if (0 == access(oldfile, F_OK))
169         {
170             if (0 != remove(oldfile))
171             {
172                 printf("logfile: %s remove failed\n", oldfile);
173             }
174         }
175     }
176     
177     // 3.rolling logfile
178     for (uint16_t i = g_module_param[id].file_num; i > 0; i--)
179     {
180         char oldname[FILE_MAX_LEN] = { 0 };
181         char newname[FILE_MAX_LEN] = { 0 };
182         
183         snprintf(oldname, sizeof(oldname), "%s/%s_%d.log",
184             g_global_param.path, g_module_param[id].name, i);
185         snprintf(newname, sizeof(newname), "%s/%s_%d.log",
186             g_global_param.path, g_module_param[id].name, i+1);
187                 
188         if (0 == access(oldname, F_OK))
189         {
190             if (0 != rename(oldname, newname))
191             {
192                 printf("logfile: %s rename %s failed\n", oldname, newname);
193             }
194         }
195     }
196     
197     // 4.rolling self
198     if (g_global_param.file_count > 0)
199     {
200         char oldname[FILE_MAX_LEN] = { 0 };
201         char newname[FILE_MAX_LEN] = { 0 };
202         
203         snprintf(oldname, sizeof(oldname), "%s/%s.log"
204             g_global_param.path, g_module_param[id].name);
205         snprintf(newname, sizeof(newname), "%s/%s_1.log"
206             g_global_param.path, g_module_param[id].name);
207                 
208         if (0 == access(oldname, F_OK))
209         {
210             if (0 != rename(oldname, newname))
211             {
212                 printf("logfile: %s rename %s failed\n", oldname, newname);
213             }
214         }
215     }
216     
217     // 5.file count++
218     if (g_module_param[id].file_num < g_global_param.file_count)
219     {
220         g_module_param[id].file_num++;
221     }
222 }
223 
224 static void open_log_file(MODULE_ID id)
225 {
226     char file[FILE_MAX_LEN] = { 0 };
227     snprintf(file, sizeof(file), "%s/%s.log",
228         g_global_param.path, g_module_param[id].name);
229         
230     g_module_param[id].file = fopen(file, "a");
231     if (g_module_param[id].file != NULL)
232     {
233         if (g_global_param.output_flush)
234         {
235             // 遇到换行或者缓冲满时写入
236             setvbuf(g_module_param[id].file, NULL, _IOLBF, 0);
237         }
238     }
239     else
240     {
241         printf("logfile: %s open failed\n", file);
242     }
243 }
244 
245 static void check_log_file(MODULE_ID id)
246 {
247     long fileSize = ftell(g_module_param[id].file);
248     if (fileSize > g_global_param.file_size * BINARY_UNIT * BINARY_UNIT)
249     {
250         struct statvfs fs;
251         if (0 == statvfs(g_global_param.path, &fs))
252         {
253             unsigned long freeSpace = fs.f_bavail * fs.f_frsize;
254             if (freeSpace / (BINARY_UNIT * BINARY_UNIT) < g_global_param.reserve_space)
255             {
256                 fputs("The disk will be full, and log switch console output\n", 
257                     g_module_param[id].file);
258                 g_module_param[id].output = LOG_CONSOLE;
259             }
260         }
261         
262         rolling_log_file(id);
263     }
264 }
265 
266 static int init_log_config(const char* config)
267 {
268     int result = 0;
269     
270     do {
271         if (NULL == config)
272         {
273             printf("init log config failed, file is null\n");
274             result = -1;
275         }
276         
277         FILE *cfg = fopen(config, "r");
278         if (NULL == cfg)
279         {
280             printf("init log config failed, open %s error\n", config);
281             result = -1;
282         }
283     
284         char line[512] = { 0 };
285         while (fgets(line, sizeof(line), cfg) != NULL)
286         {
287             trim(line);
288             if (line[0] != '#')
289             {
290                 get_config_string(line, "log_path", g_global_param.path, sizeof(g_global_param.path));
291                 get_config_number(line, "log_flush", &g_global_param.output_flush, sizeof(g_global_param.output_flush));
292                 get_config_number(line, "log_file_count", &g_global_param.file_count, sizeof(g_global_param.file_count));
293                 get_config_number(line, "log_file_size", &g_global_param.file_size, sizeof(g_global_param.file_size));
294                 get_config_number(line, "log_reserve_space", &g_global_param.reserve_space, sizeof(g_global_param.reserve_space));
295                 
296                 get_config_number(line, "log_A_level",  &g_module_param[MODULE_A].level,  sizeof(g_module_param[MODULE_A].level));
297                 get_config_number(line, "log_B_level",  &g_module_param[MODULE_B].level,  sizeof(g_module_param[MODULE_B].level));
298                 get_config_number(line, "log_C_level",  &g_module_param[MODULE_C].level,  sizeof(g_module_param[MODULE_C].level));
299                 get_config_number(line, "log_D_level",  &g_module_param[MODULE_D].level,  sizeof(g_module_param[MODULE_D].level));
300                 get_config_number(line, "log_A_output", &g_module_param[MODULE_A].output, sizeof(g_module_param[MODULE_A].output));
301                 get_config_number(line, "log_B_output", &g_module_param[MODULE_B].output, sizeof(g_module_param[MODULE_B].output));
302                 get_config_number(line, "log_C_output", &g_module_param[MODULE_C].output, sizeof(g_module_param[MODULE_C].output));
303                 get_config_number(line, "log_D_output", &g_module_param[MODULE_D].output, sizeof(g_module_param[MODULE_D].output));
304             }
305         }
306     } while(0);
307     
308     return result;
309 }
310 
311 static int check_log_config(void)
312 {
313     int result = 0;
314     
315     for (uint16_t i = 0; i < MODULE_ID_MAX; i++)
316     {
317         if (g_module_param[i].level < 0 || g_module_param[i].level >= LOG_LEVEL_MAX)
318         {
319             printf("%s log level config is invalid\n", g_module_param[i].name);
320             result = -1;
321             break;
322         }
323         
324         if (g_module_param[i].output < 0 || g_module_param[i].output > 15)
325         {
326             printf("%s log output config is invalid\n", g_module_param[i].name);
327             result = -1;
328             break;
329         }
330         
331         if ((g_module_param[i].output & LOG_FILE) && g_global_param.file_size == 0)
332         {
333             printf("log file size config is invalid\n");
334             result = -1;
335             break;
336         }
337     }
338     
339     return result;
340 }
341 
342 static int init_log_path(void)
343 {
344     int result = 0;
345     
346     do {
347         if (0 == access(g_global_param.path, F_OK))
348         {
349             break;
350         }
351         
352         for (uint16_t i = 0; g_global_param.path[i] != '\0'; i++)
353         {
354             if ('/' == g_global_param.path[i])
355             {
356                 g_global_param.path[i] = '\0';
357                 if (g_global_param.path[0] != '\0'
358                     && 0 != strcmp(g_global_param.path, ".")
359                     && 0 != access(g_global_param.path, F_OK))
360                 {
361                     if (0 != mkdir(g_global_param.path, 0777))
362                     {
363                         printf("logpath: %s mkdir failed\n", g_global_param.path);
364                         g_global_param.path[i] = '/';
365                         result = -1;
366                         break;
367                     }
368                 }
369                 
370                 g_global_param.path[i] = '/';
371             }
372         }
373         
374         if (0 == result)
375         {
376             size_t len = strlen(g_global_param.path);
377             if (g_global_param.path[len - 1] != '/')
378             {
379                 if (0 != mkdir(g_global_param.path, 0777))
380                 {
381                     printf("logpath: %s mkdir failed\n", g_global_param.path);
382                     result = -1;
383                 }
384             }
385         }
386         
387     } while (0);
388     
389     return result;
390 }
391 
392 static int init_log_file_number(void)
393 {
394     int result = 0;
395     
396     DIR* dp = opendir(g_global_param.path);
397     if (NULL != dp)
398     {
399         struct dirent* entry = NULL;
400         while ((entry = readdir(dp)) != NULL)
401         {
402             char* name = entry->d_name;
403             if (0 == strcmp(name, ".") || 0 == strcmp(name, ".."))
404                 continue;
405             
406             for (uint16_t i = 0; i < MODULE_ID_MAX; i++)
407             {
408                 if (strstr(name, g_module_param[i]) != NULL)
409                 {
410                     char head[20] = { 0 };
411                     size_t headlen = strlen(g_module_param[i].name);
412                     strncpy(head, g_module_param[i].name, sizeof(head));
413                     head[headlen] = '_';
414                     head[headlen+1] = '\0';
415                     
416                     char* posBeg = strstr(name, head);
417                     char* posEnd = strstr(name, ".log");
418                     if (posBeg != NULL && posEnd != NULL)
419                     {
420                         char value[16] = { 0 };
421                         posBeg = posBeg + headlen + 1;
422                         strncpy(value, posBeg, posEnd-posBeg);
423                         
424                         char* endptr;
425                         long total = strtol(value, &endptr, 10);
426                         if (*endptr == '\0')
427                         {
428                             unsigned char num = total;
429                             if (num > g_module_param[i].file_num)
430                             {
431                                 g_module_param[i].file_num = num;
432                             }
433                             
434                             // 配置改小时,上次多余的日志文件不主动移除
435                             if (g_module_param[i].file_num > g_global_param.file_count)
436                             {
437                                 g_module_param[i].file_num = g_global_param.file_count;
438                             }
439                         }
440                     }
441                 }
442             }
443         }
444     }
445     else
446     {
447         printf("logpath: %s open failed\n", g_global_param.path);
448         result = -1;
449     }
450     
451     closedir(dp);
452     return result;
453 }
454 
455 int log_init(const char* config)
456 {
457     // 1.init log config
458     int result = init_log_config(config);
459     
460     // 2.check log config
461     if (0 == result)
462     {
463         result = check_log_config();
464     }
465     
466     // 3.init log path
467     if (0 == result)
468     {
469         result = init_log_path();
470     }
471     
472     // 4.init log file number
473     if (0 == result)
474     {
475         result = init_log_file_number();
476     }
477     
478     return result;
479 }
480 
481 void logging(LOG_LEVEL level, MODULE_ID id, const char* file, int line, 
482     const char* func, const char* format, const char* suffix, ...)
483 {
484     if (g_module_param[id].level <= level
485         && g_module_param[id].output != LOG_OFF
486         && format != NULL && suffix != NULL)
487     {
488         char tm[32] = { 0 };
489         get_log_time(tm);
490         pthread_t tid = pthread_self();
491         
492         pthread_mutex_lock(&g_module_param[id].mutex);
493         
494         if (g_module_param[id].output & LOG_CONSOLE)
495         {
496             printf("[%s] [%-5s] [%lu] [%s:%d %s] ",
497                 tm, g_log_level[level], tid, file, line, func);
498             
499             va_list args;
500             va_start(args, suffix);
501             vprintf(format, args);
502             va_end(args);
503             
504             printf("%s", suffix);
505         }
506         
507         if (g_module_param[id].output & LOG_FILE)
508         {
509             if (NULL == g_module_param[id].file)
510             {
511                 open_log_file(id);
512             }
513             
514             if (NULL != g_module_param[id].file)
515             {
516                 fprintf(g_module_param[id].file, "[%s] [%-5s] [%lu] [%s:%d %s] ",
517                     tm, g_log_level[level], tid, file, line, func);
518                 
519                 va_list args;
520                 va_start(args, suffix);
521                 vfprintf(g_module_param[id].file, format, args);
522                 va_end(args);
523                 
524                 fprintf(g_module_param[id].file, "%s", suffix);
525                 
526                 check_log_file(id);
527             }
528         }
529         
530         pthread_mutex_unlock(&g_module_param[id].mutex);
531     }
532 }
533 
534 int log_drop(void)
535 {
536     for (uint16_t i = 0; i < MODULE_ID_MAX; i++)
537     {
538         if (g_module_param[i].file != NULL)
539         {
540             fflush(g_module_param[i].file);
541             fclose(g_module_param[i].file);
542             g_module_param[i].file = NULL;
543             g_module_param[i].output = LOG_OFF;
544         }
545     }
546     
547     return 0;
548 }
loglite.c

  测试代码

 1 #include <loglite.h>
 2 #include <loglite.c>
 3 
 4 int main(int argc, char** argv)
 5 {
 6     log_init("./log.config");
 7 
 8     LOGW(MODULE_A, "%s--%d", "\n", "Hello world", 1);
 9     LOGE(MODULE_A, "%s--%d", "\n", "Hello world", 2);
10 
11     char* test = "Hello world";
12 
13     A_LOGI(test);
14     A_LOGW("%s--%d", test, 4);
15 
16     log_drop();
17 
18     retrun 0;
19 }

  打印的日志格式为:[%Y-%m-%d %H:%M:%S.%03d] [%-5s] [%lu] [%s:%d %s]  %s

                                       [年-月-日 时:分:秒.毫秒] [等级] [线程ID] [文件名:行数 函数名] 自定义日志内容

  上面测试环境为 ubuntu16,输出内容为:

  [2023-09-11 13:49:56.440] [WARN  ] [140737353963328] [main.c:8 main] Hello world--1

  [2023-09-11 13:49:56.440] [ERROR] [140737353963328] [main.c:9 main] Hello world--2

  [2023-09-11 13:49:56.440] [INFO   ] [140737353963328] [main.c:13 main] Hello world

  [2023-09-11 13:49:56.440] [WARN ] [140737353963328] [main.c:14 main] Hello world--4

  欢迎大家试用,有问题可随时联系讨论。

 

作者:博客园博主 KeepHopes,对大数据、人工智能领域颇感兴趣,请多多赐教!
原文链接:https://www.cnblogs.com/yuwanxian/p/17693648.html
版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议。转载请注明出处,谢谢!