3dd14a3b70e8597dfda9442119c92a8f58f3c558
[src/app-framework-binder.git] / src / verbose.c
1 /*
2  Copyright (C) 2016-2019 "IoT.bzh"
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18
19 #include <stdio.h>
20 #include <stdarg.h>
21
22 #include "verbose.h"
23
24 #define MASKOF(x)               (1 << (x))
25
26 #if !defined(DEFAULT_LOGLEVEL)
27 # define DEFAULT_LOGLEVEL       Log_Level_Warning
28 #endif
29
30 #if !defined(DEFAULT_LOGMASK)
31 # define DEFAULT_LOGMASK        (MASKOF((DEFAULT_LOGLEVEL) + 1) - 1)
32 #endif
33
34 #if !defined(MINIMAL_LOGLEVEL)
35 # define MINIMAL_LOGLEVEL       Log_Level_Error
36 #endif
37
38 #if !defined(MINIMAL_LOGMASK)
39 # define MINIMAL_LOGMASK        (MASKOF((MINIMAL_LOGLEVEL) + 1) - 1)
40 #endif
41
42 static const char *names[] = {
43         "emergency",
44         "alert",
45         "critical",
46         "error",
47         "warning",
48         "notice",
49         "info",
50         "debug"
51 };
52
53 static const char asort[] = { 1, 2, 7, 0, 3, 6, 5, 4 };
54
55 int logmask = DEFAULT_LOGMASK | MINIMAL_LOGMASK;
56
57 void (*verbose_observer)(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args);
58
59 #define CROP_LOGLEVEL(x) \
60         ((x) < Log_Level_Emergency ? Log_Level_Emergency \
61                                    : (x) > Log_Level_Debug ? Log_Level_Debug : (x))
62
63 #if defined(VERBOSE_WITH_SYSLOG)
64
65 #include <syslog.h>
66
67 static void _vverbose_(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
68 {
69         char *p;
70
71         if (file == NULL || vasprintf(&p, fmt, args) < 0)
72                 vsyslog(loglevel, fmt, args);
73         else {
74                 syslog(CROP_LOGLEVEL(loglevel), "%s [%s:%d, %s]", p, file, line, function?:"?");
75                 free(p);
76         }
77 }
78
79 void verbose_set_name(const char *name, int authority)
80 {
81         openlog(name, LOG_PERROR, authority ? LOG_AUTH : LOG_USER);
82 }
83
84 #elif defined(VERBOSE_WITH_SYSTEMD)
85
86 #define SD_JOURNAL_SUPPRESS_LOCATION
87
88 #include <systemd/sd-journal.h>
89
90 static const char *appname;
91
92 static int appauthority;
93
94 static void _vverbose_(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
95 {
96         char lino[20];
97
98         if (file == NULL) {
99                 sd_journal_printv(loglevel, fmt, args);
100         } else {
101                 sprintf(lino, "%d", line);
102                 sd_journal_printv_with_location(loglevel, file, lino, function, fmt, args);
103         }
104 }
105
106 void verbose_set_name(const char *name, int authority)
107 {
108         appname = name;
109         appauthority = authority;
110 }
111
112 #else
113
114 #include <unistd.h>
115 #include <errno.h>
116 #include <string.h>
117 #include <sys/uio.h>
118 #include <pthread.h>
119
120 static const char *appname;
121
122 static int appauthority;
123
124 static const char *prefixes[] = {
125         "<0> EMERGENCY",
126         "<1> ALERT",
127         "<2> CRITICAL",
128         "<3> ERROR",
129         "<4> WARNING",
130         "<5> NOTICE",
131         "<6> INFO",
132         "<7> DEBUG"
133 };
134
135 static const char *prefixes_colorized[] = {
136         "<0> " COLOR_EMERGENCY  "EMERGENCY"     COLOR_DEFAULT,
137         "<1> " COLOR_ALERT      "ALERT"         COLOR_DEFAULT,
138         "<2> " COLOR_CRITICAL   "CRITICAL"      COLOR_DEFAULT,
139         "<3> " COLOR_ERROR      "ERROR"         COLOR_DEFAULT,
140         "<4> " COLOR_WARNING    "WARNING"       COLOR_DEFAULT,
141         "<5> " COLOR_NOTICE     "NOTICE"        COLOR_DEFAULT,
142         "<6> " COLOR_INFO       "INFO"          COLOR_DEFAULT,
143         "<7> " COLOR_DEBUG      "DEBUG"         COLOR_DEFAULT
144 };
145
146 static int colorize = 0;
147
148 static int tty;
149
150 static const char chars[] = { '\n', '?', ':', ' ', '[', ',', ']' };
151
152 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
153
154 static void _vverbose_(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
155 {
156         char buffer[4000];
157         char lino[40];
158         int saverr, n, rc;
159         struct iovec iov[20];
160
161         /* save errno */
162         saverr = errno;
163
164         /* check if tty (2) or not (1) */
165         if (!tty)
166                 tty = 1 + isatty(STDERR_FILENO);
167
168         /* prefix */
169         if (colorize)
170                 iov[0].iov_base = (void*)prefixes_colorized[CROP_LOGLEVEL(loglevel)] + (tty - 1 ? 4 : 0);
171         else
172                 iov[0].iov_base = (void*)prefixes[CROP_LOGLEVEL(loglevel)] + (tty - 1 ? 4 : 0);
173         iov[0].iov_len = strlen(iov[0].iov_base);
174
175         /* " " */
176         iov[1].iov_base = (void*)&chars[2];
177         iov[1].iov_len = 2;
178
179         n = 2;
180         if (fmt) {
181                 iov[n].iov_base = buffer;
182                 errno = saverr;
183                 rc = vsnprintf(buffer, sizeof buffer, fmt, args);
184                 if (rc < 0)
185                         rc = 0;
186                 else if ((size_t)rc > sizeof buffer) {
187                         /* if too long, ellipsis the end with ... */
188                         rc = (int)sizeof buffer;
189                         buffer[rc - 1] = buffer[rc - 2]  = buffer[rc - 3] = '.';
190                 }
191                 iov[n++].iov_len = (size_t)rc;
192         }
193         if (file && (!fmt || tty == 1 || loglevel <= Log_Level_Warning)) {
194
195                 if (colorize)
196                 {
197                         iov[n].iov_base = (void*)COLOR_FILE;
198                         iov[n++].iov_len = strlen(COLOR_FILE);
199                 }
200
201                 /* "[" (!fmt) or " [" (fmt) */
202                 iov[n].iov_base = (void*)&chars[3 + !fmt];
203                 iov[n++].iov_len = 2 - !fmt;
204                 /* file */
205                 iov[n].iov_base = (void*)file;
206                 iov[n++].iov_len = strlen(file);
207                 /* ":" */
208                 iov[n].iov_base = (void*)&chars[2];
209                 iov[n++].iov_len = 1;
210                 if (line) {
211                         /* line number */
212                         iov[n].iov_base = lino;
213                         iov[n++].iov_len = snprintf(lino, sizeof lino, "%d", line);
214                 } else {
215                         /* "?" */
216                         iov[n].iov_base = (void*)&chars[1];
217                         iov[n++].iov_len = 1;
218                 }
219                 /* "," */
220                 iov[n].iov_base = (void*)&chars[5];
221                 iov[n++].iov_len = 1;
222                 if (function) {
223                         /* function name */
224                         iov[n].iov_base = (void*)function;
225                         iov[n++].iov_len = strlen(function);
226                 } else {
227                         /* "?" */
228                         iov[n].iov_base = (void*)&chars[1];
229                         iov[n++].iov_len = 1;
230                 }
231                 iov[n].iov_base = (void*)&chars[6];
232                 iov[n++].iov_len = 1;
233
234                 if (colorize)
235                 {
236                         iov[n].iov_base = (void*)COLOR_DEFAULT;
237                         iov[n++].iov_len = strlen(COLOR_DEFAULT);
238                 }
239         }
240         if (n == 2) {
241                 /* "?" */
242                 iov[n].iov_base = (void*)&chars[1];
243                 iov[n++].iov_len = 1;
244         }
245         /* "\n" */
246         iov[n].iov_base = (void*)&chars[0];
247         iov[n++].iov_len = 1;
248
249         /* emit the message */
250         pthread_mutex_lock(&mutex);
251         writev(STDERR_FILENO, iov, n);
252         pthread_mutex_unlock(&mutex);
253
254         /* restore errno */
255         errno = saverr;
256 }
257
258 void verbose_set_name(const char *name, int authority)
259 {
260         appname = name;
261         appauthority = authority;
262 }
263
264 #endif
265
266 void vverbose(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args)
267 {
268         void (*observer)(int loglevel, const char *file, int line, const char *function, const char *fmt, va_list args) = verbose_observer;
269
270         if (!observer)
271                 _vverbose_(loglevel, file, line, function, fmt, args);
272         else {
273                 va_list ap;
274                 va_copy(ap, args);
275                 _vverbose_(loglevel, file, line, function, fmt, args);
276                 observer(loglevel, file, line, function, fmt, ap);
277                 va_end(ap);
278         }
279 }
280
281 void verbose(int loglevel, const char *file, int line, const char *function, const char *fmt, ...)
282 {
283         va_list ap;
284
285         va_start(ap, fmt);
286         vverbose(loglevel, file, line, function, fmt, ap);
287         va_end(ap);
288 }
289
290 void set_logmask(int lvl)
291 {
292         logmask = lvl | MINIMAL_LOGMASK;
293 }
294
295 void verbose_add(int level)
296 {
297         set_logmask(logmask | MASKOF(level));
298 }
299
300 void verbose_sub(int level)
301 {
302         set_logmask(logmask & ~MASKOF(level));
303 }
304
305 void verbose_clear()
306 {
307         set_logmask(0);
308 }
309
310 void verbose_dec()
311 {
312         verbosity_set(verbosity_get() - 1);
313 }
314
315 void verbose_inc()
316 {
317         verbosity_set(verbosity_get() + 1);
318 }
319
320 int verbosity_to_mask(int verbo)
321 {
322         int x = verbo + Log_Level_Error;
323         int l = CROP_LOGLEVEL(x);
324         return (1 << (l + 1)) - 1;
325 }
326
327 int verbosity_from_mask(int mask)
328 {
329         int v = 0;
330         while (mask > verbosity_to_mask(v))
331                 v++;
332         return v;
333 }
334
335 void verbosity_set(int verbo)
336 {
337         set_logmask(verbosity_to_mask(verbo));
338 }
339
340 int verbosity_get()
341 {
342         return verbosity_from_mask(logmask);
343 }
344
345 int verbose_level_of_name(const char *name)
346 {
347         int c, i, r, l = 0, u = sizeof names / sizeof * names;
348         while (l < u) {
349                 i = (l + u) >> 1;
350                 r = (int)asort[i];
351                 c = strcasecmp(names[r], name);
352                 if (!c)
353                         return r;
354                 if (c < 0)
355                         l = i + 1;
356                 else
357                         u = i;
358         }
359         return -1;
360 }
361
362 const char *verbose_name_of_level(int level)
363 {
364         return level == CROP_LOGLEVEL(level) ? names[level] : NULL;
365 }
366
367 void verbose_colorize()
368 {
369         colorize = 1;
370 }
371
372 int verbose_is_colorized()
373 {
374         return colorize;
375 }