RaspberrPi project source code
guowenxue
2024-04-11 69b42a43ca4b2d93be203c34f6b45f5de1e32a15
commit | author | age
d6b4a7 1 /*********************************************************************************
G 2  *      Copyright:  (C) 2023 LingYun IoT System Studio.
3  *                  All rights reserved.
4  *
5  *       Filename:  logger.c
6  *    Description:  This file is common logger API functions
7  *                 
8  *        Version:  1.0.0(11/08/23)
9  *         Author:  Guo Wenxue <guowenxue@gmail.com>
10  *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
11  *                 
12  ********************************************************************************/
13
14 #include <stdio.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <string.h>
19 #include <time.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <pthread.h>
24
25 #include "logger.h"
26
27 typedef void (*log_LockFn)(void *udata, int lock);
28
29 static struct {
30     char        file[32]; /* logger file name */
31     FILE       *fp;       /* logger file pointer */
32     long        size;     /* logger file max size */
33     int         level;    /* logger level */
34     log_LockFn  lockfn;   /* lock function */
35     void       *udata;    /* lock data */
36 } L;
37
38 static const char *level_names[] = {
39     "ERROR",
40     "WARN",
41     "INFO",
42     "DEBUG",
43     "TRACE"
44 };
45
46 static const char *level_colors[] = {
47     "\x1b[31m",
48     "\x1b[33m",
49     "\x1b[32m",
50     "\x1b[36m",
51     "\x1b[94m"
52 };
53
54 static inline void time_to_str(char *buf)
55 {
56     struct timeval   tv;
57     struct tm       *tm;
58     int              len;
59
60     gettimeofday(&tv, NULL);
61     tm = localtime(&tv.tv_sec);
62
63     len = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d ",
64             tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
65             tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec);
66
67     buf[len] = '\0';
68 }
69
70 static void mutex_lock(void *udata, int lock)
71 {
72     int              err;
73     pthread_mutex_t *l = (pthread_mutex_t *) udata;
74
75     if (lock)
76     {
77         if ( (err = pthread_mutex_lock(l)) != 0 )
78             log_error("Unable to lock log lock: %s", strerror(err));
79     }
80     else
81     {
82         if ( (err = pthread_mutex_unlock(l) != 0) )
83             log_error("Unable to unlock log lock: %s", strerror(err));
84     }
85 }
86
87 int log_open(char *fname, int level, int size, int lock)
88 {
89     FILE            *fp;
90
91     L.level = level;
92     L.size = size*1024;
93
94     if( !fname || !strcmp(fname, "console") || !strcmp(fname, "stderr") )
95     {
96         strcpy(L.file, "console");
97         L.fp = stderr;
98         L.size = 0; /* console don't need rollback */
99     }
100     else
101     {
102         if ( !(fp = fopen(fname, "a+")) )
103         {
104             fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno));
105             return -2;
106         }
107         L.fp = fp;
108         strncpy(L.file, fname, sizeof(L.file));
109     }
110
111
112     if( lock )
113     {
114         static pthread_mutex_t     log_lock;
115
116         pthread_mutex_init(&log_lock, NULL);
117         L.udata = (void *)&log_lock;
118         L.lockfn = mutex_lock;
119     }
120
121     fprintf(L.fp, "\n");
122     log_info("logger system(%s) start: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
123             LOG_VERSION, L.file, level_names[level], size);
124
125     return 0;
126 }
127
128 void log_close(void)
129 {
130     if( L.fp && L.fp!=stderr )
131         fclose(L.fp);
132
133     if (L.udata )
134         pthread_mutex_destroy( L.udata);
135 }
136
137 static void log_rollback(void)
138 {
139     char       cmd[128]={0};
140     long       fsize;
141
142     /* don't need rollback */
143     if(L.size <= 0 )
144         return ;
145
146     fsize = ftell(L.fp);
147     if( fsize < L.size )
148         return ;
149
150     /* backup current log file  */
151     snprintf(cmd, sizeof(cmd), "cp %s %s.bak", L.file, L.file);
152     system(cmd);
153
154     /* rollback file */
155     fseek(L.fp, 0, SEEK_SET);
156     truncate(L.file, 0);
157
158     fprintf(L.fp, "\n");
159     log_info("logger system(%s) rollback: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
160             LOG_VERSION, L.file, level_names[L.level], L.size/1024);
161
162     return ;
163 }
164
165 void _log_write(int level, const char *file, int line, const char *fmt, ...)
166 {
167     va_list    args;
168     char       time_string[100];
169
170     if ( !L.fp || level>L.level )
171         return;
172
173     /* Acquire lock */
174     if ( L.lockfn )
175         L.lockfn(L.udata, 1);
176
177     log_rollback();
178
179     /* check and rollback file */
180     time_to_str(time_string);
181
182     /* Log to stderr */
183     if ( L.fp == stderr )
184     {
185         fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ",
186                 time_string, level_colors[level], level_names[level], file, line);
187     }
188     else /* Log to file */
189     {
190         fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line);
191     }
192
193     va_start(args, fmt);
194     vfprintf(L.fp, fmt, args);
195     va_end(args);
196
197     fflush(L.fp);
198
199     /* Release lock */
200     if ( L.lockfn )
201         L.lockfn(L.udata, 0);
202 }
203
204 #define LINELEN 81
205 #define CHARS_PER_LINE 16
206 static char *print_char =
207 "                "
208 "                "
209 " !\"#$%&'()*+,-./"
210 "0123456789:;<=>?"
211 "@ABCDEFGHIJKLMNO"
212 "PQRSTUVWXYZ[\\]^_"
213 "`abcdefghijklmno"
214 "pqrstuvwxyz{|}~ "
215 "                "
216 "                "
217 " ???????????????"
218 "????????????????"
219 "????????????????"
220 "????????????????"
221 "????????????????"
222 "????????????????";
223
224 void log_dump(int level, const char *prompt, char *buf, size_t len)
225 {
226     int rc;
227     int idx;
228     char prn[LINELEN];
229     char lit[CHARS_PER_LINE + 2];
230     char hc[4];
231     short line_done = 1;
232
233     if (!L.fp || level>L.level)
234         return;
235
236     if( prompt )
237         _log_write(level, __FILE__, __LINE__, "%s", prompt);
238
239     rc = len;
240     idx = 0;
241     lit[CHARS_PER_LINE] = '\0';
242
243     while (rc > 0)
244     {
245         if (line_done)
246             snprintf(prn, LINELEN, "%08X: ", idx);
247
248         do
249         {
250             unsigned char c = buf[idx];
251             snprintf(hc, 4, "%02X ", c);
252             strncat(prn, hc, LINELEN);
253
254             lit[idx % CHARS_PER_LINE] = print_char[c];
255         }
256         while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
257
258         line_done = (idx % CHARS_PER_LINE) == 0;
259         if (line_done)
260         {
261             if (L.fp)
262                 fprintf(L.fp, "%s  %s\n", prn, lit);
263         }
264     }
265
266     if (!line_done)
267     {
268         int ldx = idx % CHARS_PER_LINE;
269         lit[ldx++] = print_char[(int)buf[idx]];
270         lit[ldx] = '\0';
271
272         while ((++idx % CHARS_PER_LINE) != 0)
273             strncat(prn, "   ", sizeof(prn)-strlen(prn));
274
275         if (L.fp)
276             fprintf(L.fp, "%s  %s\n", prn, lit);
277
278     }
279 }