build: use LT_INIT(pic-only) instead of forcing -fPIC.
[osmocom-bb.git] / src / logging.c
blob8dfc31ed36a641f9537d766e616c5f1ec8411165
1 /* Debugging/Logging support code */
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
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.
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.
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.
23 /* \addtogroup logging
24 * @{
27 /* \file logging.c */
29 #include "../config.h"
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
37 #ifdef HAVE_STRINGS_H
38 #include <strings.h>
39 #endif
40 #include <time.h>
41 #include <errno.h>
43 #include <osmocom/core/talloc.h>
44 #include <osmocom/core/utils.h>
45 #include <osmocom/core/logging.h>
47 #include <osmocom/vty/logging.h> /* for LOGGING_STR. */
49 struct log_info *osmo_log_info;
51 static struct log_context log_context;
52 static void *tall_log_ctx = NULL;
53 LLIST_HEAD(osmo_log_target_list);
55 #define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
57 static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
58 { 0, "EVERYTHING" },
59 { LOGL_DEBUG, "DEBUG" },
60 { LOGL_INFO, "INFO" },
61 { LOGL_NOTICE, "NOTICE" },
62 { LOGL_ERROR, "ERROR" },
63 { LOGL_FATAL, "FATAL" },
64 { 0, NULL },
67 #define INT2IDX(x) (-1*(x)-1)
68 static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
69 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
70 .name = "DLGLOBAL",
71 .description = "Library-internal global log family",
72 .loglevel = LOGL_NOTICE,
73 .enabled = 1,
75 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
76 .name = "DLLAPD",
77 .description = "LAPD in libosmogsm",
78 .loglevel = LOGL_NOTICE,
79 .enabled = 1,
81 [INT2IDX(DLINP)] = {
82 .name = "DLINP",
83 .description = "A-bis Intput Subsystem",
84 .loglevel = LOGL_NOTICE,
85 .enabled = 1,
87 [INT2IDX(DLMUX)] = {
88 .name = "DLMUX",
89 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
90 .loglevel = LOGL_NOTICE,
91 .enabled = 1,
93 [INT2IDX(DLMI)] = {
94 .name = "DLMI",
95 .description = "A-bis Input Driver for Signalling",
96 .enabled = 0, .loglevel = LOGL_NOTICE,
98 [INT2IDX(DLMIB)] = {
99 .name = "DLMIB",
100 .description = "A-bis Input Driver for B-Channels (voice)",
101 .enabled = 0, .loglevel = LOGL_NOTICE,
103 [INT2IDX(DLSMS)] = {
104 .name = "DLSMS",
105 .description = "Layer3 Short Message Service (SMS)",
106 .enabled = 1, .loglevel = LOGL_NOTICE,
107 .color = "\033[1;38m",
111 /* You have to keep this in sync with the structure loglevel_strs. */
112 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
113 "Log simply everything",
114 "Log debug messages and higher levels",
115 "Log informational messages and higher levels",
116 "Log noticable messages and higher levels",
117 "Log error messages and higher levels",
118 "Log only fatal messages",
119 NULL,
122 /* special magic for negative (library-internal) log subsystem numbers */
123 static int subsys_lib2index(int subsys)
125 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
128 /*! \brief Parse a human-readable log level into a numeric value */
129 int log_parse_level(const char *lvl)
131 return get_string_value(loglevel_strs, lvl);
134 /*! \brief convert a numeric log level into human-readable string */
135 const char *log_level_str(unsigned int lvl)
137 return get_value_string(loglevel_strs, lvl);
140 /*! \brief parse a human-readable log category into numeric form
141 * \param[in] category human-readable log category name
142 * \returns numeric category value, or -EINVAL otherwise
144 int log_parse_category(const char *category)
146 int i;
148 for (i = 0; i < osmo_log_info->num_cat; ++i) {
149 if (osmo_log_info->cat[i].name == NULL)
150 continue;
151 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
152 return i;
155 return -EINVAL;
158 /*! \brief parse the log category mask
159 * \param[in] target log target to be configured
160 * \param[in] _mask log category mask string
162 * The format can be this: category1:category2:category3
163 * or category1,2:category2,3:...
165 void log_parse_category_mask(struct log_target* target, const char *_mask)
167 int i = 0;
168 char *mask = strdup(_mask);
169 char *category_token = NULL;
171 /* Disable everything to enable it afterwards */
172 for (i = 0; i < osmo_log_info->num_cat; ++i)
173 target->categories[i].enabled = 0;
175 category_token = strtok(mask, ":");
176 do {
177 for (i = 0; i < osmo_log_info->num_cat; ++i) {
178 char* colon = strstr(category_token, ",");
179 int length = strlen(category_token);
180 int cat_length = strlen(osmo_log_info->cat[i].name);
182 /* Use longest length not to match subocurrences. */
183 if (cat_length > length)
184 length = cat_length;
186 if (!osmo_log_info->cat[i].name)
187 continue;
189 if (colon)
190 length = colon - category_token;
192 if (strncasecmp(osmo_log_info->cat[i].name,
193 category_token, length) == 0) {
194 int level = 0;
196 if (colon)
197 level = atoi(colon+1);
199 target->categories[i].enabled = 1;
200 target->categories[i].loglevel = level;
203 } while ((category_token = strtok(NULL, ":")));
205 free(mask);
208 static const char* color(int subsys)
210 if (subsys < osmo_log_info->num_cat)
211 return osmo_log_info->cat[subsys].color;
213 return NULL;
216 static void _output(struct log_target *target, unsigned int subsys,
217 unsigned int level, char *file, int line, int cont,
218 const char *format, va_list ap)
220 char buf[4096];
221 int ret, len = 0, offset = 0, rem = sizeof(buf);
223 /* are we using color */
224 if (target->use_color) {
225 const char *c = color(subsys);
226 if (c) {
227 ret = snprintf(buf + offset, rem, "%s", color(subsys));
228 if (ret < 0)
229 goto err;
230 OSMO_SNPRINTF_RET(ret, rem, offset, len);
233 if (!cont) {
234 if (target->print_timestamp) {
235 char *timestr;
236 time_t tm;
237 tm = time(NULL);
238 timestr = ctime(&tm);
239 timestr[strlen(timestr)-1] = '\0';
240 ret = snprintf(buf + offset, rem, "%s ", timestr);
241 if (ret < 0)
242 goto err;
243 OSMO_SNPRINTF_RET(ret, rem, offset, len);
245 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
246 subsys, file, line);
247 if (ret < 0)
248 goto err;
249 OSMO_SNPRINTF_RET(ret, rem, offset, len);
251 ret = vsnprintf(buf + offset, rem, format, ap);
252 if (ret < 0)
253 goto err;
254 OSMO_SNPRINTF_RET(ret, rem, offset, len);
256 ret = snprintf(buf + offset, rem, "%s",
257 target->use_color ? "\033[0;m" : "");
258 if (ret < 0)
259 goto err;
260 OSMO_SNPRINTF_RET(ret, rem, offset, len);
261 err:
262 buf[sizeof(buf)-1] = '\0';
263 target->output(target, level, buf);
266 /*! \brief vararg version of logging function */
267 void osmo_vlogp(int subsys, int level, char *file, int line,
268 int cont, const char *format, va_list ap)
270 struct log_target *tar;
272 if (subsys < 0)
273 subsys = subsys_lib2index(subsys);
275 if (subsys > osmo_log_info->num_cat)
276 subsys = DLGLOBAL;
278 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
279 struct log_category *category;
280 int output = 0;
281 va_list bp;
283 category = &tar->categories[subsys];
284 /* subsystem is not supposed to be logged */
285 if (!category->enabled)
286 continue;
288 /* Check the global log level */
289 if (tar->loglevel != 0 && level < tar->loglevel)
290 continue;
292 /* Check the category log level */
293 if (tar->loglevel == 0 && category->loglevel != 0 &&
294 level < category->loglevel)
295 continue;
297 /* Apply filters here... if that becomes messy we will
298 * need to put filters in a list and each filter will
299 * say stop, continue, output */
300 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
301 output = 1;
302 else if (osmo_log_info->filter_fn)
303 output = osmo_log_info->filter_fn(&log_context,
304 tar);
305 if (!output)
306 continue;
308 /* According to the manpage, vsnprintf leaves the value of ap
309 * in undefined state. Since _output uses vsnprintf and it may
310 * be called several times, we have to pass a copy of ap. */
311 va_copy(bp, ap);
312 _output(tar, subsys, level, file, line, cont, format, bp);
313 va_end(bp);
317 void logp(int subsys, char *file, int line, int cont,
318 const char *format, ...)
320 va_list ap;
322 va_start(ap, format);
323 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
324 va_end(ap);
327 void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
329 va_list ap;
331 va_start(ap, format);
332 osmo_vlogp(subsys, level, file, line, cont, format, ap);
333 va_end(ap);
336 /*! \brief Register a new log target with the logging core
337 * \param[in] target Log target to be registered
339 void log_add_target(struct log_target *target)
341 llist_add_tail(&target->entry, &osmo_log_target_list);
344 /*! \brief Unregister a log target from the logging core
345 * \param[in] target Log target to be unregistered
347 void log_del_target(struct log_target *target)
349 llist_del(&target->entry);
352 /*! \brief Reset (clear) the logging context */
353 void log_reset_context(void)
355 memset(&log_context, 0, sizeof(log_context));
358 /*! \brief Set the logging context
359 * \param[in] ctx_nr logging context number
360 * \param[in] value value to which the context is to be set
362 * A logging context is something like the subscriber identity to which
363 * the currently processed message relates, or the BTS through which it
364 * was received. As soon as this data is known, it can be set using
365 * this function. The main use of context information is for logging
366 * filters.
368 int log_set_context(uint8_t ctx_nr, void *value)
370 if (ctx_nr > LOG_MAX_CTX)
371 return -EINVAL;
373 log_context.ctx[ctx_nr] = value;
375 return 0;
378 /*! \brief Enable the \ref LOG_FILTER_ALL log filter
379 * \param[in] target Log target to be affected
380 * \param[in] all enable (1) or disable (0) the ALL filter
382 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
383 * be printed. It acts as a wildcard. Setting it to \a 1 means there
384 * is no filtering.
386 void log_set_all_filter(struct log_target *target, int all)
388 if (all)
389 target->filter_map |= LOG_FILTER_ALL;
390 else
391 target->filter_map &= ~LOG_FILTER_ALL;
394 /*! \brief Enable or disable the use of colored output
395 * \param[in] target Log target to be affected
396 * \param[in] use_color Use color (1) or don't use color (0)
398 void log_set_use_color(struct log_target *target, int use_color)
400 target->use_color = use_color;
403 /*! \brief Enable or disable printing of timestamps while logging
404 * \param[in] target Log target to be affected
405 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
407 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
409 target->print_timestamp = print_timestamp;
412 /*! \brief Set the global log level for a given log target
413 * \param[in] target Log target to be affected
414 * \param[in] log_level New global log level
416 void log_set_log_level(struct log_target *target, int log_level)
418 target->loglevel = log_level;
421 void log_set_category_filter(struct log_target *target, int category,
422 int enable, int level)
424 if (category >= osmo_log_info->num_cat)
425 return;
426 target->categories[category].enabled = !!enable;
427 target->categories[category].loglevel = level;
430 static void _file_output(struct log_target *target, unsigned int level,
431 const char *log)
433 fprintf(target->tgt_file.out, "%s", log);
434 fflush(target->tgt_file.out);
437 /*! \brief Create a new log target skeleton */
438 struct log_target *log_target_create(void)
440 struct log_target *target;
441 unsigned int i;
443 target = talloc_zero(tall_log_ctx, struct log_target);
444 if (!target)
445 return NULL;
447 target->categories = talloc_zero_array(target,
448 struct log_category,
449 osmo_log_info->num_cat);
450 if (!target->categories) {
451 talloc_free(target);
452 return NULL;
455 INIT_LLIST_HEAD(&target->entry);
457 /* initialize the per-category enabled/loglevel from defaults */
458 for (i = 0; i < osmo_log_info->num_cat; i++) {
459 struct log_category *cat = &target->categories[i];
460 cat->enabled = osmo_log_info->cat[i].enabled;
461 cat->loglevel = osmo_log_info->cat[i].loglevel;
464 /* global settings */
465 target->use_color = 1;
466 target->print_timestamp = 0;
468 /* global log level */
469 target->loglevel = 0;
470 return target;
473 /*! \brief Create the STDERR log target */
474 struct log_target *log_target_create_stderr(void)
476 /* since C89/C99 says stderr is a macro, we can safely do this! */
477 #ifdef stderr
478 struct log_target *target;
480 target = log_target_create();
481 if (!target)
482 return NULL;
484 target->type = LOG_TGT_TYPE_STDERR;
485 target->tgt_file.out = stderr;
486 target->output = _file_output;
487 return target;
488 #else
489 return NULL;
490 #endif /* stderr */
493 /*! \brief Create a new file-based log target
494 * \param[in] fname File name of the new log file
495 * \returns Log target in case of success, NULL otherwise
497 struct log_target *log_target_create_file(const char *fname)
499 struct log_target *target;
501 target = log_target_create();
502 if (!target)
503 return NULL;
505 target->type = LOG_TGT_TYPE_FILE;
506 target->tgt_file.out = fopen(fname, "a");
507 if (!target->tgt_file.out)
508 return NULL;
510 target->output = _file_output;
512 target->tgt_file.fname = talloc_strdup(target, fname);
514 return target;
517 /*! \brief Find a registered log target
518 * \param[in] type Log target type
519 * \param[in] fname File name
520 * \returns Log target (if found), NULL otherwise
522 struct log_target *log_target_find(int type, const char *fname)
524 struct log_target *tgt;
526 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
527 if (tgt->type != type)
528 continue;
529 if (tgt->type == LOG_TGT_TYPE_FILE) {
530 if (!strcmp(fname, tgt->tgt_file.fname))
531 return tgt;
532 } else
533 return tgt;
535 return NULL;
538 /*! \brief Unregister, close and delete a log target */
539 void log_target_destroy(struct log_target *target)
542 /* just in case, to make sure we don't have any references */
543 log_del_target(target);
545 if (target->output == &_file_output) {
546 /* since C89/C99 says stderr is a macro, we can safely do this! */
547 #ifdef stderr
548 /* don't close stderr */
549 if (target->tgt_file.out != stderr)
550 #endif
552 fclose(target->tgt_file.out);
553 target->tgt_file.out = NULL;
557 talloc_free(target);
560 /*! \brief close and re-open a log file (for log file rotation) */
561 int log_target_file_reopen(struct log_target *target)
563 fclose(target->tgt_file.out);
565 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
566 if (!target->tgt_file.out)
567 return -errno;
569 /* we assume target->output already to be set */
571 return 0;
574 /*! \brief Generates the logging command string for VTY
575 * \param[in] unused_info Deprecated parameter, no longer used!
577 const char *log_vty_command_string(const struct log_info *unused_info)
579 struct log_info *info = osmo_log_info;
580 int len = 0, offset = 0, ret, i, rem;
581 int size = strlen("logging level () ()") + 1;
582 char *str;
584 for (i = 0; i < info->num_cat; i++) {
585 if (info->cat[i].name == NULL)
586 continue;
587 size += strlen(info->cat[i].name) + 1;
590 for (i = 0; i < LOGLEVEL_DEFS; i++)
591 size += strlen(loglevel_strs[i].str) + 1;
593 rem = size;
594 str = talloc_zero_size(tall_log_ctx, size);
595 if (!str)
596 return NULL;
598 ret = snprintf(str + offset, rem, "logging level (all|");
599 if (ret < 0)
600 goto err;
601 OSMO_SNPRINTF_RET(ret, rem, offset, len);
603 for (i = 0; i < info->num_cat; i++) {
604 if (info->cat[i].name) {
605 int j, name_len = strlen(info->cat[i].name)+1;
606 char name[name_len];
608 for (j = 0; j < name_len; j++)
609 name[j] = tolower(info->cat[i].name[j]);
611 name[name_len-1] = '\0';
612 ret = snprintf(str + offset, rem, "%s|", name+1);
613 if (ret < 0)
614 goto err;
615 OSMO_SNPRINTF_RET(ret, rem, offset, len);
618 offset--; /* to remove the trailing | */
619 rem++;
621 ret = snprintf(str + offset, rem, ") (");
622 if (ret < 0)
623 goto err;
624 OSMO_SNPRINTF_RET(ret, rem, offset, len);
626 for (i = 0; i < LOGLEVEL_DEFS; i++) {
627 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
628 char loglevel_str[loglevel_str_len];
630 for (j = 0; j < loglevel_str_len; j++)
631 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
633 loglevel_str[loglevel_str_len-1] = '\0';
634 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
635 if (ret < 0)
636 goto err;
637 OSMO_SNPRINTF_RET(ret, rem, offset, len);
639 offset--; /* to remove the trailing | */
640 rem++;
642 ret = snprintf(str + offset, rem, ")");
643 if (ret < 0)
644 goto err;
645 OSMO_SNPRINTF_RET(ret, rem, offset, len);
646 err:
647 str[size-1] = '\0';
648 return str;
651 /*! \brief Generates the logging command description for VTY
652 * \param[in] unused_info Deprecated parameter, no longer used!
654 const char *log_vty_command_description(const struct log_info *unused_info)
656 struct log_info *info = osmo_log_info;
657 char *str;
658 int i, ret, len = 0, offset = 0, rem;
659 unsigned int size =
660 strlen(LOGGING_STR
661 "Set the log level for a specified category\n") + 1;
663 for (i = 0; i < info->num_cat; i++) {
664 if (info->cat[i].name == NULL)
665 continue;
666 size += strlen(info->cat[i].description) + 1;
669 for (i = 0; i < LOGLEVEL_DEFS; i++)
670 size += strlen(loglevel_descriptions[i]) + 1;
672 size += strlen("Global setting for all subsystems") + 1;
673 rem = size;
674 str = talloc_zero_size(tall_log_ctx, size);
675 if (!str)
676 return NULL;
678 ret = snprintf(str + offset, rem, LOGGING_STR
679 "Set the log level for a specified category\n");
680 if (ret < 0)
681 goto err;
682 OSMO_SNPRINTF_RET(ret, rem, offset, len);
684 ret = snprintf(str + offset, rem,
685 "Global setting for all subsystems\n");
686 if (ret < 0)
687 goto err;
688 OSMO_SNPRINTF_RET(ret, rem, offset, len);
690 for (i = 0; i < info->num_cat; i++) {
691 if (info->cat[i].name == NULL)
692 continue;
693 ret = snprintf(str + offset, rem, "%s\n",
694 info->cat[i].description);
695 if (ret < 0)
696 goto err;
697 OSMO_SNPRINTF_RET(ret, rem, offset, len);
699 for (i = 0; i < LOGLEVEL_DEFS; i++) {
700 ret = snprintf(str + offset, rem, "%s\n",
701 loglevel_descriptions[i]);
702 if (ret < 0)
703 goto err;
704 OSMO_SNPRINTF_RET(ret, rem, offset, len);
706 err:
707 str[size-1] = '\0';
708 return str;
711 /*! \brief Initialize the Osmocom logging core
712 * \param[in] inf Information regarding logging categories
713 * \param[in] ctx \ref talloc context for logging allocations
714 * \returns 0 in case of success, negative in case of error
716 int log_init(const struct log_info *inf, void *ctx)
718 int i;
720 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
721 if (!tall_log_ctx)
722 return -ENOMEM;
724 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
725 if (!osmo_log_info)
726 return -ENOMEM;
728 osmo_log_info->num_cat_user = inf->num_cat;
729 /* total number = number of user cat + library cat */
730 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
732 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
733 struct log_info_cat,
734 osmo_log_info->num_cat);
735 if (!osmo_log_info->cat) {
736 talloc_free(osmo_log_info);
737 osmo_log_info = NULL;
738 return -ENOMEM;
741 /* copy over the user part */
742 for (i = 0; i < inf->num_cat; i++) {
743 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
744 sizeof(struct log_info_cat));
747 /* copy over the library part */
748 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
749 unsigned int cn = osmo_log_info->num_cat_user + i;
750 memcpy(&osmo_log_info->cat[cn],
751 &internal_cat[i], sizeof(struct log_info_cat));
754 return 0;
757 /*! @} */