rename log_info to osmo_log_info to avoid namespace clash with app
[osmocom-bb.git] / src / logging.c
1 /* Debugging/Logging support code */
2
3 /* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4  * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
5  * All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include "../config.h"
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #ifdef HAVE_STRINGS_H
31 #include <strings.h>
32 #endif
33 #include <time.h>
34 #include <errno.h>
35
36 #include <osmocore/talloc.h>
37 #include <osmocore/utils.h>
38 #include <osmocore/logging.h>
39
40 const struct log_info *osmo_log_info;
41
42 static struct log_context log_context;
43 static void *tall_log_ctx = NULL;
44 static LLIST_HEAD(target_list);
45
46 static const struct value_string loglevel_strs[] = {
47         { 0,            "EVERYTHING" },
48         { LOGL_DEBUG,   "DEBUG" },
49         { LOGL_INFO,    "INFO" },
50         { LOGL_NOTICE,  "NOTICE" },
51         { LOGL_ERROR,   "ERROR" },
52         { LOGL_FATAL,   "FATAL" },
53         { 0, NULL },
54 };
55
56 int log_parse_level(const char *lvl)
57 {
58         return get_string_value(loglevel_strs, lvl);
59 }
60
61 const char *log_level_str(unsigned int lvl)
62 {
63         return get_value_string(loglevel_strs, lvl);
64 }
65
66 int log_parse_category(const char *category)
67 {
68         int i;
69
70         for (i = 0; i < osmo_log_info->num_cat; ++i) {
71                 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
72                         return i;
73         }
74
75         return -EINVAL;
76 }
77
78 /*
79  * Parse the category mask.
80  * The format can be this: category1:category2:category3
81  * or category1,2:category2,3:...
82  */
83 void log_parse_category_mask(struct log_target* target, const char *_mask)
84 {
85         int i = 0;
86         char *mask = strdup(_mask);
87         char *category_token = NULL;
88
89         /* Disable everything to enable it afterwards */
90         for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
91                 target->categories[i].enabled = 0;
92
93         category_token = strtok(mask, ":");
94         do {
95                 for (i = 0; i < osmo_log_info->num_cat; ++i) {
96                         char* colon = strstr(category_token, ",");
97                         int length = strlen(category_token);
98
99                         if (colon)
100                             length = colon - category_token;
101
102                         if (strncasecmp(osmo_log_info->cat[i].name,
103                                         category_token, length) == 0) {
104                                 int level = 0;
105
106                                 if (colon)
107                                         level = atoi(colon+1);
108
109                                 target->categories[i].enabled = 1;
110                                 target->categories[i].loglevel = level;
111                         }
112                 }
113         } while ((category_token = strtok(NULL, ":")));
114
115         free(mask);
116 }
117
118 static const char* color(int subsys)
119 {
120         if (subsys < osmo_log_info->num_cat)
121                 return osmo_log_info->cat[subsys].color;
122
123         return NULL;
124 }
125
126 static void _output(struct log_target *target, unsigned int subsys,
127                     char *file, int line, int cont, const char *format,
128                     va_list ap)
129 {
130         char col[30];
131         char sub[30];
132         char tim[30];
133         char buf[4096];
134         char final[4096];
135
136         /* prepare the data */
137         col[0] = '\0';
138         sub[0] = '\0';
139         tim[0] = '\0';
140         buf[0] = '\0';
141
142         /* are we using color */
143         if (target->use_color) {
144                 const char *c = color(subsys);
145                 if (c) {
146                         snprintf(col, sizeof(col), "%s", color(subsys));
147                         col[sizeof(col)-1] = '\0';
148                 }
149         }
150         vsnprintf(buf, sizeof(buf), format, ap);
151         buf[sizeof(buf)-1] = '\0';
152
153         if (!cont) {
154                 if (target->print_timestamp) {
155                         char *timestr;
156                         time_t tm;
157                         tm = time(NULL);
158                         timestr = ctime(&tm);
159                         timestr[strlen(timestr)-1] = '\0';
160                         snprintf(tim, sizeof(tim), "%s ", timestr);
161                         tim[sizeof(tim)-1] = '\0';
162                 }
163                 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
164                 sub[sizeof(sub)-1] = '\0';
165         }
166
167         snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
168         final[sizeof(final)-1] = '\0';
169         target->output(target, final);
170 }
171
172
173 static void _logp(unsigned int subsys, int level, char *file, int line,
174                   int cont, const char *format, va_list ap)
175 {
176         struct log_target *tar;
177
178         llist_for_each_entry(tar, &target_list, entry) {
179                 struct log_category *category;
180                 int output = 0;
181
182                 category = &tar->categories[subsys];
183                 /* subsystem is not supposed to be logged */
184                 if (!category->enabled)
185                         continue;
186
187                 /* Check the global log level */
188                 if (tar->loglevel != 0 && level < tar->loglevel)
189                         continue;
190
191                 /* Check the category log level */
192                 if (tar->loglevel == 0 && category->loglevel != 0 &&
193                     level < category->loglevel)
194                         continue;
195
196                 /* Apply filters here... if that becomes messy we will
197                  * need to put filters in a list and each filter will
198                  * say stop, continue, output */
199                 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
200                         output = 1;
201                 else if (osmo_log_info->filter_fn)
202                         output = osmo_log_info->filter_fn(&log_context,
203                                                        tar);
204
205                 if (output) {
206                         /* FIXME: copying the va_list is an ugly
207                          * workaround against a bug hidden somewhere in
208                          * _output.  If we do not copy here, the first
209                          * call to _output() will corrupt the va_list
210                          * contents, and any further _output() calls
211                          * with the same va_list will segfault */
212                         va_list bp;
213                         va_copy(bp, ap);
214                         _output(tar, subsys, file, line, cont, format, bp);
215                         va_end(bp);
216                 }
217         }
218 }
219
220 void logp(unsigned int subsys, char *file, int line, int cont,
221           const char *format, ...)
222 {
223         va_list ap;
224
225         va_start(ap, format);
226         _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
227         va_end(ap);
228 }
229
230 void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
231 {
232         va_list ap;
233
234         va_start(ap, format);
235         _logp(subsys, level, file, line, cont, format, ap);
236         va_end(ap);
237 }
238
239 static char hexd_buff[4096];
240
241 char *hexdump(const unsigned char *buf, int len)
242 {
243         int i;
244         char *cur = hexd_buff;
245
246         hexd_buff[0] = 0;
247         for (i = 0; i < len; i++) {
248                 int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
249                 int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
250                 if (rc <= 0)
251                         break;
252                 cur += rc;
253         }
254         hexd_buff[sizeof(hexd_buff)-1] = 0;
255         return hexd_buff;
256 }
257
258 void log_add_target(struct log_target *target)
259 {
260         llist_add_tail(&target->entry, &target_list);
261 }
262
263 void log_del_target(struct log_target *target)
264 {
265         llist_del(&target->entry);
266 }
267
268 void log_reset_context(void)
269 {
270         memset(&log_context, 0, sizeof(log_context));
271 }
272
273 int log_set_context(uint8_t ctx_nr, void *value)
274 {
275         if (ctx_nr > LOG_MAX_CTX)
276                 return -EINVAL;
277
278         log_context.ctx[ctx_nr] = value;
279
280         return 0;
281 }
282
283 void log_set_all_filter(struct log_target *target, int all)
284 {
285         if (all)
286                 target->filter_map |= LOG_FILTER_ALL;
287         else
288                 target->filter_map &= ~LOG_FILTER_ALL;
289 }
290
291 void log_set_use_color(struct log_target *target, int use_color)
292 {
293         target->use_color = use_color;
294 }
295
296 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
297 {
298         target->print_timestamp = print_timestamp;
299 }
300
301 void log_set_log_level(struct log_target *target, int log_level)
302 {
303         target->loglevel = log_level;
304 }
305
306 void log_set_category_filter(struct log_target *target, int category,
307                                int enable, int level)
308 {
309         if (category >= osmo_log_info->num_cat)
310                 return;
311         target->categories[category].enabled = !!enable;
312         target->categories[category].loglevel = level;
313 }
314
315 /* since C89/C99 says stderr is a macro, we can safely do this! */
316 #ifdef stderr
317 static void _stderr_output(struct log_target *target, const char *log)
318 {
319         fprintf(target->tgt_stdout.out, "%s", log);
320         fflush(target->tgt_stdout.out);
321 }
322 #endif
323
324 struct log_target *log_target_create(void)
325 {
326         struct log_target *target;
327         unsigned int i;
328
329         target = talloc_zero(tall_log_ctx, struct log_target);
330         if (!target)
331                 return NULL;
332
333         INIT_LLIST_HEAD(&target->entry);
334
335         /* initialize the per-category enabled/loglevel from defaults */
336         for (i = 0; i < osmo_log_info->num_cat; i++) {
337                 struct log_category *cat = &target->categories[i];
338                 cat->enabled = osmo_log_info->cat[i].enabled;
339                 cat->loglevel = osmo_log_info->cat[i].loglevel;
340         }
341
342         /* global settings */
343         target->use_color = 1;
344         target->print_timestamp = 0;
345
346         /* global log level */
347         target->loglevel = 0;
348         return target;
349 }
350
351 struct log_target *log_target_create_stderr(void)
352 {
353 /* since C89/C99 says stderr is a macro, we can safely do this! */
354 #ifdef stderr
355         struct log_target *target;
356
357         target = log_target_create();
358         if (!target)
359                 return NULL;
360
361         target->tgt_stdout.out = stderr;
362         target->output = _stderr_output;
363         return target;
364 #else
365         return NULL;
366 #endif /* stderr */
367 }
368
369 const char *log_vty_level_string(struct log_info *info)
370 {
371         const struct value_string *vs;
372         unsigned int len = 3; /* ()\0 */
373         char *str;
374
375         for (vs = loglevel_strs; vs->value || vs->str; vs++)
376                 len += strlen(vs->str) + 1;
377
378         str = talloc_zero_size(NULL, len);
379         if (!str)
380                 return NULL;
381
382         str[0] = '(';
383         for (vs = loglevel_strs; vs->value || vs->str; vs++) {
384                 strcat(str, vs->str);
385                 strcat(str, "|");
386         }
387         str[strlen(str)-1] = ')';
388
389         return str;
390 }
391
392 const char *log_vty_category_string(struct log_info *info)
393 {
394         unsigned int len = 3;   /* "()\0" */
395         unsigned int i;
396         char *str;
397
398         for (i = 0; i < info->num_cat; i++)
399                 len += strlen(info->cat[i].name) + 1;
400
401         str = talloc_zero_size(NULL, len);
402         if (!str)
403                 return NULL;
404
405         str[0] = '(';
406         for (i = 0; i < info->num_cat; i++) {
407                 strcat(str, info->cat[i].name+1);
408                 strcat(str, "|");
409         }
410         str[strlen(str)-1] = ')';
411
412         return str;
413 }
414
415 void log_init(const struct log_info *cat)
416 {
417         tall_log_ctx = talloc_named_const(NULL, 1, "logging");
418         osmo_log_info = cat;
419 }