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>
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
29 #include "../config.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] = {
59 { LOGL_DEBUG
, "DEBUG" },
60 { LOGL_INFO
, "INFO" },
61 { LOGL_NOTICE
, "NOTICE" },
62 { LOGL_ERROR
, "ERROR" },
63 { LOGL_FATAL
, "FATAL" },
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 */
71 .description
= "Library-internal global log family",
72 .loglevel
= LOGL_NOTICE
,
75 [INT2IDX(DLLAPD
)] = { /* -2 becomes 1 */
77 .description
= "LAPD in libosmogsm",
78 .loglevel
= LOGL_NOTICE
,
83 .description
= "A-bis Intput Subsystem",
84 .loglevel
= LOGL_NOTICE
,
89 .description
= "A-bis B-Subchannel TRAU Frame Multiplex",
90 .loglevel
= LOGL_NOTICE
,
95 .description
= "A-bis Input Driver for Signalling",
96 .enabled
= 0, .loglevel
= LOGL_NOTICE
,
100 .description
= "A-bis Input Driver for B-Channels (voice)",
101 .enabled
= 0, .loglevel
= LOGL_NOTICE
,
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",
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
)
148 for (i
= 0; i
< osmo_log_info
->num_cat
; ++i
) {
149 if (osmo_log_info
->cat
[i
].name
== NULL
)
151 if (!strcasecmp(osmo_log_info
->cat
[i
].name
+1, category
))
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
)
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
, ":");
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
)
186 if (!osmo_log_info
->cat
[i
].name
)
190 length
= colon
- category_token
;
192 if (strncasecmp(osmo_log_info
->cat
[i
].name
,
193 category_token
, length
) == 0) {
197 level
= atoi(colon
+1);
199 target
->categories
[i
].enabled
= 1;
200 target
->categories
[i
].loglevel
= level
;
203 } while ((category_token
= strtok(NULL
, ":")));
208 static const char* color(int subsys
)
210 if (subsys
< osmo_log_info
->num_cat
)
211 return osmo_log_info
->cat
[subsys
].color
;
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
)
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
);
227 ret
= snprintf(buf
+ offset
, rem
, "%s", color(subsys
));
230 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
234 if (target
->print_timestamp
) {
238 timestr
= ctime(&tm
);
239 timestr
[strlen(timestr
)-1] = '\0';
240 ret
= snprintf(buf
+ offset
, rem
, "%s ", timestr
);
243 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
245 ret
= snprintf(buf
+ offset
, rem
, "<%4.4x> %s:%d ",
249 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
251 ret
= vsnprintf(buf
+ offset
, rem
, format
, ap
);
254 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
256 ret
= snprintf(buf
+ offset
, rem
, "%s",
257 target
->use_color
? "\033[0;m" : "");
260 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
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
;
273 subsys
= subsys_lib2index(subsys
);
275 if (subsys
> osmo_log_info
->num_cat
)
278 llist_for_each_entry(tar
, &osmo_log_target_list
, entry
) {
279 struct log_category
*category
;
283 category
= &tar
->categories
[subsys
];
284 /* subsystem is not supposed to be logged */
285 if (!category
->enabled
)
288 /* Check the global log level */
289 if (tar
->loglevel
!= 0 && level
< tar
->loglevel
)
292 /* Check the category log level */
293 if (tar
->loglevel
== 0 && category
->loglevel
!= 0 &&
294 level
< category
->loglevel
)
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)
302 else if (osmo_log_info
->filter_fn
)
303 output
= osmo_log_info
->filter_fn(&log_context
,
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. */
312 _output(tar
, subsys
, level
, file
, line
, cont
, format
, bp
);
317 void logp(int subsys
, char *file
, int line
, int cont
,
318 const char *format
, ...)
322 va_start(ap
, format
);
323 osmo_vlogp(subsys
, LOGL_DEBUG
, file
, line
, cont
, format
, ap
);
327 void logp2(int subsys
, unsigned int level
, char *file
, int line
, int cont
, const char *format
, ...)
331 va_start(ap
, format
);
332 osmo_vlogp(subsys
, level
, file
, line
, cont
, format
, 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
368 int log_set_context(uint8_t ctx_nr
, void *value
)
370 if (ctx_nr
> LOG_MAX_CTX
)
373 log_context
.ctx
[ctx_nr
] = value
;
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
386 void log_set_all_filter(struct log_target
*target
, int all
)
389 target
->filter_map
|= LOG_FILTER_ALL
;
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
)
426 target
->categories
[category
].enabled
= !!enable
;
427 target
->categories
[category
].loglevel
= level
;
430 static void _file_output(struct log_target
*target
, unsigned int level
,
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
;
443 target
= talloc_zero(tall_log_ctx
, struct log_target
);
447 target
->categories
= talloc_zero_array(target
,
449 osmo_log_info
->num_cat
);
450 if (!target
->categories
) {
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;
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! */
478 struct log_target
*target
;
480 target
= log_target_create();
484 target
->type
= LOG_TGT_TYPE_STDERR
;
485 target
->tgt_file
.out
= stderr
;
486 target
->output
= _file_output
;
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();
505 target
->type
= LOG_TGT_TYPE_FILE
;
506 target
->tgt_file
.out
= fopen(fname
, "a");
507 if (!target
->tgt_file
.out
)
510 target
->output
= _file_output
;
512 target
->tgt_file
.fname
= talloc_strdup(target
, fname
);
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
)
529 if (tgt
->type
== LOG_TGT_TYPE_FILE
) {
530 if (!strcmp(fname
, tgt
->tgt_file
.fname
))
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! */
548 /* don't close stderr */
549 if (target
->tgt_file
.out
!= stderr
)
552 fclose(target
->tgt_file
.out
);
553 target
->tgt_file
.out
= NULL
;
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
)
569 /* we assume target->output already to be set */
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;
584 for (i
= 0; i
< info
->num_cat
; i
++) {
585 if (info
->cat
[i
].name
== NULL
)
587 size
+= strlen(info
->cat
[i
].name
) + 1;
590 for (i
= 0; i
< LOGLEVEL_DEFS
; i
++)
591 size
+= strlen(loglevel_strs
[i
].str
) + 1;
594 str
= talloc_zero_size(tall_log_ctx
, size
);
598 ret
= snprintf(str
+ offset
, rem
, "logging level (all|");
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;
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);
615 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
618 offset
--; /* to remove the trailing | */
621 ret
= snprintf(str
+ offset
, rem
, ") (");
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
);
637 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
639 offset
--; /* to remove the trailing | */
642 ret
= snprintf(str
+ offset
, rem
, ")");
645 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
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
;
658 int i
, ret
, len
= 0, offset
= 0, rem
;
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
)
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;
674 str
= talloc_zero_size(tall_log_ctx
, size
);
678 ret
= snprintf(str
+ offset
, rem
, LOGGING_STR
679 "Set the log level for a specified category\n");
682 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
684 ret
= snprintf(str
+ offset
, rem
,
685 "Global setting for all subsystems\n");
688 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
690 for (i
= 0; i
< info
->num_cat
; i
++) {
691 if (info
->cat
[i
].name
== NULL
)
693 ret
= snprintf(str
+ offset
, rem
, "%s\n",
694 info
->cat
[i
].description
);
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
]);
704 OSMO_SNPRINTF_RET(ret
, rem
, offset
, len
);
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
)
720 tall_log_ctx
= talloc_named_const(ctx
, 1, "logging");
724 osmo_log_info
= talloc_zero(tall_log_ctx
, struct log_info
);
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
,
734 osmo_log_info
->num_cat
);
735 if (!osmo_log_info
->cat
) {
736 talloc_free(osmo_log_info
);
737 osmo_log_info
= NULL
;
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
));