blob: c007a45f38171fb2bfbfe97b153b3594cc18c78e [file] [log] [blame]
Harald Welte4a2bb9e2010-03-26 09:33:40 +08001/* 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
Harald Welte18fc4652011-08-17 14:14:17 +020023/* \addtogroup logging
24 * @{
25 */
26
27/* \file logging.c */
28
Harald Welte01fd5cb2010-03-26 23:51:31 +080029#include "../config.h"
30
Harald Welte4a2bb9e2010-03-26 09:33:40 +080031#include <stdarg.h>
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010035#include <ctype.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080036
37#ifdef HAVE_STRINGS_H
Harald Welte4a2bb9e2010-03-26 09:33:40 +080038#include <strings.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080039#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +080040#include <time.h>
41#include <errno.h>
42
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010043#include <osmocom/core/talloc.h>
44#include <osmocom/core/utils.h>
45#include <osmocom/core/logging.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080046
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010047#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
48
Harald Welteb43bc042011-06-27 10:29:17 +020049struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080050
Harald Welte3ae27582010-03-26 21:24:24 +080051static struct log_context log_context;
52static void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010053LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080054
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010055#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
56
57static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
Harald Welte9b186702013-03-19 09:55:28 +010058 { 0, "EVERYTHING" },
Harald Welte4a2bb9e2010-03-26 09:33:40 +080059 { LOGL_DEBUG, "DEBUG" },
60 { LOGL_INFO, "INFO" },
61 { LOGL_NOTICE, "NOTICE" },
62 { LOGL_ERROR, "ERROR" },
63 { LOGL_FATAL, "FATAL" },
64 { 0, NULL },
65};
66
Harald Welteb43bc042011-06-27 10:29:17 +020067#define INT2IDX(x) (-1*(x)-1)
68static 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,
74 },
root8a996b42011-09-26 11:22:21 +020075 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
76 .name = "DLLAPD",
77 .description = "LAPD in libosmogsm",
Harald Welte1f0b8c22011-06-27 10:51:37 +020078 .loglevel = LOGL_NOTICE,
79 .enabled = 1,
80 },
Harald Welte892e6212011-07-19 14:31:44 +020081 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +020082 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020083 .description = "A-bis Intput Subsystem",
84 .loglevel = LOGL_NOTICE,
85 .enabled = 1,
86 },
Harald Welte892e6212011-07-19 14:31:44 +020087 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +020088 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020089 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
90 .loglevel = LOGL_NOTICE,
91 .enabled = 1,
92 },
Harald Welte892e6212011-07-19 14:31:44 +020093 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +020094 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020095 .description = "A-bis Input Driver for Signalling",
96 .enabled = 0, .loglevel = LOGL_NOTICE,
97 },
Harald Welte892e6212011-07-19 14:31:44 +020098 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +020099 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200100 .description = "A-bis Input Driver for B-Channels (voice)",
101 .enabled = 0, .loglevel = LOGL_NOTICE,
102 },
Andreas Eversbergc626da92011-10-28 03:53:50 +0200103 [INT2IDX(DLSMS)] = {
104 .name = "DLSMS",
105 .description = "Layer3 Short Message Service (SMS)",
106 .enabled = 1, .loglevel = LOGL_NOTICE,
107 .color = "\033[1;38m",
108 },
Harald Welte7fd0c832014-08-20 19:58:13 +0200109 [INT2IDX(DLCTRL)] = {
110 .name = "DLCTRL",
111 .description = "Control Interface",
112 .enabled = 1, .loglevel = LOGL_NOTICE,
113 },
Holger Hans Peter Freythera5dc19d2014-12-04 14:35:21 +0100114 [INT2IDX(DLGTP)] = {
115 .name = "DLGTP",
116 .description = "GPRS GTP library",
117 .enabled = 1, .loglevel = LOGL_NOTICE,
118 },
Harald Welteb43bc042011-06-27 10:29:17 +0200119};
120
Harald Weltede6e4982012-12-06 21:25:27 +0100121/*! \brief descriptive string for each log level */
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100122/* You have to keep this in sync with the structure loglevel_strs. */
123const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
Holger Hans Peter Freyther6ec6bd92014-12-28 18:27:38 +0100124 "Don't use. It doesn't log anything",
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100125 "Log debug messages and higher levels",
126 "Log informational messages and higher levels",
127 "Log noticable messages and higher levels",
128 "Log error messages and higher levels",
129 "Log only fatal messages",
130 NULL,
131};
132
Harald Welteb43bc042011-06-27 10:29:17 +0200133/* special magic for negative (library-internal) log subsystem numbers */
134static int subsys_lib2index(int subsys)
135{
136 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
137}
138
Harald Welte18fc4652011-08-17 14:14:17 +0200139/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800140int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800141{
142 return get_string_value(loglevel_strs, lvl);
143}
144
Harald Welte18fc4652011-08-17 14:14:17 +0200145/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200146const char *log_level_str(unsigned int lvl)
147{
148 return get_value_string(loglevel_strs, lvl);
149}
150
Harald Welte18fc4652011-08-17 14:14:17 +0200151/*! \brief parse a human-readable log category into numeric form
152 * \param[in] category human-readable log category name
153 * \returns numeric category value, or -EINVAL otherwise
154 */
Harald Welte3ae27582010-03-26 21:24:24 +0800155int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800156{
157 int i;
158
Harald Welte4ebdf742010-05-19 19:54:00 +0200159 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200160 if (osmo_log_info->cat[i].name == NULL)
161 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200162 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800163 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800164 }
165
166 return -EINVAL;
167}
168
Harald Welte18fc4652011-08-17 14:14:17 +0200169/*! \brief parse the log category mask
170 * \param[in] target log target to be configured
171 * \param[in] _mask log category mask string
172 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800173 * The format can be this: category1:category2:category3
174 * or category1,2:category2,3:...
175 */
Harald Welte3ae27582010-03-26 21:24:24 +0800176void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800177{
178 int i = 0;
179 char *mask = strdup(_mask);
180 char *category_token = NULL;
181
182 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200183 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800184 target->categories[i].enabled = 0;
185
186 category_token = strtok(mask, ":");
187 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200188 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200189 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800190 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200191
192 if (!osmo_log_info->cat[i].name)
193 continue;
194
195 length = strlen(category_token);
196 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200197
198 /* Use longest length not to match subocurrences. */
199 if (cat_length > length)
200 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800201
202 if (colon)
203 length = colon - category_token;
204
Harald Welte4ebdf742010-05-19 19:54:00 +0200205 if (strncasecmp(osmo_log_info->cat[i].name,
206 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800207 int level = 0;
208
209 if (colon)
210 level = atoi(colon+1);
211
Harald Weltefaadfe22010-03-26 21:05:43 +0800212 target->categories[i].enabled = 1;
213 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800214 }
215 }
216 } while ((category_token = strtok(NULL, ":")));
217
218 free(mask);
219}
220
221static const char* color(int subsys)
222{
Harald Welte4ebdf742010-05-19 19:54:00 +0200223 if (subsys < osmo_log_info->num_cat)
224 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800225
Harald Welted788f662010-03-26 09:45:03 +0800226 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800227}
228
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100229static const char* category_name(int subsys)
230{
231 if (subsys < osmo_log_info->num_cat)
232 return osmo_log_info->cat[subsys].name;
233
234 return NULL;
235}
236
Harald Welte3ae27582010-03-26 21:24:24 +0800237static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200238 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100239 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800240{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800241 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200242 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800243
244 /* are we using color */
245 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800246 const char *c = color(subsys);
247 if (c) {
Holger Hans Peter Freyther5f91a402014-12-05 10:29:45 +0100248 ret = snprintf(buf + offset, rem, "%s", c);
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200249 if (ret < 0)
250 goto err;
251 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800252 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800253 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800254 if (!cont) {
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100255 if (target->print_ext_timestamp) {
256 struct tm tm;
257 time_t timep = time(NULL);
258 localtime_r(&timep, &tm);
259 ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d000 ",
260 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
261 tm.tm_hour, tm.tm_min, tm.tm_sec);
262 if (ret < 0)
263 goto err;
264 OSMO_SNPRINTF_RET(ret, rem, offset, len);
265 } else if (target->print_timestamp) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800266 char *timestr;
267 time_t tm;
268 tm = time(NULL);
269 timestr = ctime(&tm);
270 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200271 ret = snprintf(buf + offset, rem, "%s ", timestr);
272 if (ret < 0)
273 goto err;
274 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800275 }
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100276 if (target->print_category) {
277 ret = snprintf(buf + offset, rem, "%s ", category_name(subsys));
278 if (ret < 0)
279 goto err;
280 OSMO_SNPRINTF_RET(ret, rem, offset, len);
281 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200282 if (target->print_filename) {
283 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
284 subsys, file, line);
285 if (ret < 0)
286 goto err;
287 OSMO_SNPRINTF_RET(ret, rem, offset, len);
288 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800289 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200290 ret = vsnprintf(buf + offset, rem, format, ap);
291 if (ret < 0)
292 goto err;
293 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800294
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200295 ret = snprintf(buf + offset, rem, "%s",
296 target->use_color ? "\033[0;m" : "");
297 if (ret < 0)
298 goto err;
299 OSMO_SNPRINTF_RET(ret, rem, offset, len);
300err:
301 buf[sizeof(buf)-1] = '\0';
302 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800303}
304
Harald Welte36c5a3e2011-08-27 14:33:19 +0200305/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200306void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200307 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800308{
Harald Welte3ae27582010-03-26 21:24:24 +0800309 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800310
Harald Welteb43bc042011-06-27 10:29:17 +0200311 if (subsys < 0)
312 subsys = subsys_lib2index(subsys);
313
314 if (subsys > osmo_log_info->num_cat)
315 subsys = DLGLOBAL;
316
Harald Welte28222962011-02-18 20:37:04 +0100317 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800318 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800319 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200320 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800321
322 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800323 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800324 if (!category->enabled)
325 continue;
326
327 /* Check the global log level */
328 if (tar->loglevel != 0 && level < tar->loglevel)
329 continue;
330
331 /* Check the category log level */
332 if (tar->loglevel == 0 && category->loglevel != 0 &&
333 level < category->loglevel)
334 continue;
335
336 /* Apply filters here... if that becomes messy we will
337 * need to put filters in a list and each filter will
338 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800339 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800340 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200341 else if (osmo_log_info->filter_fn)
342 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800343 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200344 if (!output)
345 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800346
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200347 /* According to the manpage, vsnprintf leaves the value of ap
348 * in undefined state. Since _output uses vsnprintf and it may
349 * be called several times, we have to pass a copy of ap. */
350 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200351 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200352 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800353 }
354}
355
Harald Weltede6e4982012-12-06 21:25:27 +0100356/*! \brief logging function used by DEBUGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200357void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800358 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800359{
360 va_list ap;
361
362 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200363 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800364 va_end(ap);
365}
366
Harald Weltede6e4982012-12-06 21:25:27 +0100367/*! \brief logging function used by LOGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200368void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800369{
370 va_list ap;
371
372 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200373 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800374 va_end(ap);
375}
376
Harald Welte18fc4652011-08-17 14:14:17 +0200377/*! \brief Register a new log target with the logging core
378 * \param[in] target Log target to be registered
379 */
Harald Welte3ae27582010-03-26 21:24:24 +0800380void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800381{
Harald Welte28222962011-02-18 20:37:04 +0100382 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800383}
384
Harald Welte18fc4652011-08-17 14:14:17 +0200385/*! \brief Unregister a log target from the logging core
386 * \param[in] target Log target to be unregistered
387 */
Harald Welte3ae27582010-03-26 21:24:24 +0800388void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800389{
390 llist_del(&target->entry);
391}
392
Harald Welte18fc4652011-08-17 14:14:17 +0200393/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800394void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800395{
Harald Welte3ae27582010-03-26 21:24:24 +0800396 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800397}
398
Harald Welte18fc4652011-08-17 14:14:17 +0200399/*! \brief Set the logging context
400 * \param[in] ctx_nr logging context number
401 * \param[in] value value to which the context is to be set
402 *
403 * A logging context is something like the subscriber identity to which
404 * the currently processed message relates, or the BTS through which it
405 * was received. As soon as this data is known, it can be set using
406 * this function. The main use of context information is for logging
407 * filters.
408 */
Harald Welte3ae27582010-03-26 21:24:24 +0800409int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800410{
Harald Welte3ae27582010-03-26 21:24:24 +0800411 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800412 return -EINVAL;
413
Harald Welte3ae27582010-03-26 21:24:24 +0800414 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800415
416 return 0;
417}
418
Harald Welte18fc4652011-08-17 14:14:17 +0200419/*! \brief Enable the \ref LOG_FILTER_ALL log filter
420 * \param[in] target Log target to be affected
421 * \param[in] all enable (1) or disable (0) the ALL filter
422 *
423 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
424 * be printed. It acts as a wildcard. Setting it to \a 1 means there
425 * is no filtering.
426 */
Harald Welte3ae27582010-03-26 21:24:24 +0800427void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800428{
429 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800430 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800431 else
Harald Welte3ae27582010-03-26 21:24:24 +0800432 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800433}
434
Harald Welte18fc4652011-08-17 14:14:17 +0200435/*! \brief Enable or disable the use of colored output
436 * \param[in] target Log target to be affected
437 * \param[in] use_color Use color (1) or don't use color (0)
438 */
Harald Welte3ae27582010-03-26 21:24:24 +0800439void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800440{
441 target->use_color = use_color;
442}
443
Harald Welte18fc4652011-08-17 14:14:17 +0200444/*! \brief Enable or disable printing of timestamps while logging
445 * \param[in] target Log target to be affected
446 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
447 */
Harald Welte3ae27582010-03-26 21:24:24 +0800448void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800449{
450 target->print_timestamp = print_timestamp;
451}
452
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100453/*! \brief Enable or disable printing of extended timestamps while logging
454 * \param[in] target Log target to be affected
455 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
456 *
457 * When both timestamp and extended timestamp is enabled then only
458 * the extended timestamp will be used. The format of the timestamp
459 * is YYYYMMDDhhmmssnnn.
460 */
461void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp)
462{
463 target->print_ext_timestamp = print_timestamp;
464}
465
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200466/*! \brief Enable or disable printing of the filename while logging
467 * \param[in] target Log target to be affected
468 * \param[in] print_filename Enable (1) or disable (0) filenames
469 */
470void log_set_print_filename(struct log_target *target, int print_filename)
471{
472 target->print_filename = print_filename;
473}
474
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100475/*! \brief Enable or disable printing of the category name
476 * \param[in] target Log target to be affected
477 * \param[in] print_catname Enable (1) or disable (0) filenames
478 *
479 * Print the category/subsys name in front of every log message.
480 */
481void log_set_print_category(struct log_target *target, int print_category)
482{
483 target->print_category = print_category;
484}
485
Harald Welte18fc4652011-08-17 14:14:17 +0200486/*! \brief Set the global log level for a given log target
487 * \param[in] target Log target to be affected
488 * \param[in] log_level New global log level
489 */
Harald Welte3ae27582010-03-26 21:24:24 +0800490void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800491{
492 target->loglevel = log_level;
493}
494
Harald Weltede6e4982012-12-06 21:25:27 +0100495/*! \brief Set a category filter on a given log target
496 * \param[in] target Log target to be affected
497 * \param[in] category Log category to be affected
498 * \param[in] enable whether to enable or disable the filter
499 * \param[in] level Log level of the filter
500 */
Harald Welte3ae27582010-03-26 21:24:24 +0800501void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800502 int enable, int level)
503{
Harald Welte4ebdf742010-05-19 19:54:00 +0200504 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800505 return;
506 target->categories[category].enabled = !!enable;
507 target->categories[category].loglevel = level;
508}
509
Harald Welte76e72ab2011-02-17 15:52:39 +0100510static void _file_output(struct log_target *target, unsigned int level,
511 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800512{
Harald Welte0083cd32010-08-25 14:55:44 +0200513 fprintf(target->tgt_file.out, "%s", log);
514 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800515}
516
Harald Welte18fc4652011-08-17 14:14:17 +0200517/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800518struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800519{
Harald Welte3ae27582010-03-26 21:24:24 +0800520 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800521 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800522
Harald Welte3ae27582010-03-26 21:24:24 +0800523 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800524 if (!target)
525 return NULL;
526
Harald Welteb43bc042011-06-27 10:29:17 +0200527 target->categories = talloc_zero_array(target,
528 struct log_category,
529 osmo_log_info->num_cat);
530 if (!target->categories) {
531 talloc_free(target);
532 return NULL;
533 }
534
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800535 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800536
537 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200538 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800539 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200540 cat->enabled = osmo_log_info->cat[i].enabled;
541 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800542 }
543
544 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800545 target->use_color = 1;
546 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200547 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800548
549 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800550 target->loglevel = 0;
551 return target;
552}
553
Harald Welte18fc4652011-08-17 14:14:17 +0200554/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800555struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800556{
Harald Weltea3b844c2010-03-27 00:04:40 +0800557/* since C89/C99 says stderr is a macro, we can safely do this! */
558#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800559 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800560
Harald Welte3ae27582010-03-26 21:24:24 +0800561 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800562 if (!target)
563 return NULL;
564
Harald Welte28222962011-02-18 20:37:04 +0100565 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200566 target->tgt_file.out = stderr;
567 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800568 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800569#else
570 return NULL;
571#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800572}
573
Harald Welte18fc4652011-08-17 14:14:17 +0200574/*! \brief Create a new file-based log target
575 * \param[in] fname File name of the new log file
576 * \returns Log target in case of success, NULL otherwise
577 */
Harald Welte3086c392010-08-25 19:10:50 +0200578struct log_target *log_target_create_file(const char *fname)
579{
580 struct log_target *target;
581
582 target = log_target_create();
583 if (!target)
584 return NULL;
585
Harald Welte28222962011-02-18 20:37:04 +0100586 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200587 target->tgt_file.out = fopen(fname, "a");
588 if (!target->tgt_file.out)
589 return NULL;
590
591 target->output = _file_output;
592
593 target->tgt_file.fname = talloc_strdup(target, fname);
594
595 return target;
596}
597
Harald Welte18fc4652011-08-17 14:14:17 +0200598/*! \brief Find a registered log target
599 * \param[in] type Log target type
600 * \param[in] fname File name
601 * \returns Log target (if found), NULL otherwise
602 */
Harald Welte28222962011-02-18 20:37:04 +0100603struct log_target *log_target_find(int type, const char *fname)
604{
605 struct log_target *tgt;
606
607 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
608 if (tgt->type != type)
609 continue;
610 if (tgt->type == LOG_TGT_TYPE_FILE) {
611 if (!strcmp(fname, tgt->tgt_file.fname))
612 return tgt;
613 } else
614 return tgt;
615 }
616 return NULL;
617}
618
Harald Welte18fc4652011-08-17 14:14:17 +0200619/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200620void log_target_destroy(struct log_target *target)
621{
622
623 /* just in case, to make sure we don't have any references */
624 log_del_target(target);
625
626 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200627/* since C89/C99 says stderr is a macro, we can safely do this! */
628#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200629 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200630 if (target->tgt_file.out != stderr)
631#endif
632 {
Harald Welte3086c392010-08-25 19:10:50 +0200633 fclose(target->tgt_file.out);
634 target->tgt_file.out = NULL;
635 }
636 }
637
638 talloc_free(target);
639}
640
Harald Welte18fc4652011-08-17 14:14:17 +0200641/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200642int log_target_file_reopen(struct log_target *target)
643{
644 fclose(target->tgt_file.out);
645
646 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
647 if (!target->tgt_file.out)
648 return -errno;
649
650 /* we assume target->output already to be set */
651
652 return 0;
653}
654
Harald Welte4de854d2013-03-18 19:01:40 +0100655/*! \brief close and re-open a log file (for log file rotation) */
656int log_targets_reopen(void)
657{
658 struct log_target *tar;
659 int rc = 0;
660
661 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
662 switch (tar->type) {
663 case LOG_TGT_TYPE_FILE:
664 if (log_target_file_reopen(tar) < 0)
665 rc = -1;
666 break;
667 default:
668 break;
669 }
670 }
671
672 return rc;
673}
674
Harald Welte18fc4652011-08-17 14:14:17 +0200675/*! \brief Generates the logging command string for VTY
676 * \param[in] unused_info Deprecated parameter, no longer used!
677 */
Harald Weltece9fec32011-06-27 14:19:16 +0200678const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200679{
Harald Weltece9fec32011-06-27 14:19:16 +0200680 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100681 int len = 0, offset = 0, ret, i, rem;
682 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200683 char *str;
684
Harald Welteb43bc042011-06-27 10:29:17 +0200685 for (i = 0; i < info->num_cat; i++) {
686 if (info->cat[i].name == NULL)
687 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100688 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200689 }
Harald Welte7638af92010-05-11 16:39:22 +0200690
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100691 for (i = 0; i < LOGLEVEL_DEFS; i++)
692 size += strlen(loglevel_strs[i].str) + 1;
693
694 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200695 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200696 if (!str)
697 return NULL;
698
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200699 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100700 if (ret < 0)
701 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200702 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200703
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100704 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200705 if (info->cat[i].name) {
706 int j, name_len = strlen(info->cat[i].name)+1;
707 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100708
Harald Welteb43bc042011-06-27 10:29:17 +0200709 for (j = 0; j < name_len; j++)
710 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100711
Harald Welteb43bc042011-06-27 10:29:17 +0200712 name[name_len-1] = '\0';
713 ret = snprintf(str + offset, rem, "%s|", name+1);
714 if (ret < 0)
715 goto err;
716 OSMO_SNPRINTF_RET(ret, rem, offset, len);
717 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100718 }
719 offset--; /* to remove the trailing | */
720 rem++;
721
722 ret = snprintf(str + offset, rem, ") (");
723 if (ret < 0)
724 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200725 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100726
727 for (i = 0; i < LOGLEVEL_DEFS; i++) {
728 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
729 char loglevel_str[loglevel_str_len];
730
731 for (j = 0; j < loglevel_str_len; j++)
732 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
733
734 loglevel_str[loglevel_str_len-1] = '\0';
735 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
736 if (ret < 0)
737 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200738 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100739 }
740 offset--; /* to remove the trailing | */
741 rem++;
742
743 ret = snprintf(str + offset, rem, ")");
744 if (ret < 0)
745 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200746 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100747err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200748 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100749 return str;
750}
751
Harald Welte18fc4652011-08-17 14:14:17 +0200752/*! \brief Generates the logging command description for VTY
753 * \param[in] unused_info Deprecated parameter, no longer used!
754 */
Harald Weltece9fec32011-06-27 14:19:16 +0200755const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100756{
Harald Weltece9fec32011-06-27 14:19:16 +0200757 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100758 char *str;
759 int i, ret, len = 0, offset = 0, rem;
760 unsigned int size =
761 strlen(LOGGING_STR
762 "Set the log level for a specified category\n") + 1;
763
Harald Welteb43bc042011-06-27 10:29:17 +0200764 for (i = 0; i < info->num_cat; i++) {
765 if (info->cat[i].name == NULL)
766 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100767 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200768 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100769
770 for (i = 0; i < LOGLEVEL_DEFS; i++)
771 size += strlen(loglevel_descriptions[i]) + 1;
772
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200773 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100774 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200775 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100776 if (!str)
777 return NULL;
778
779 ret = snprintf(str + offset, rem, LOGGING_STR
780 "Set the log level for a specified category\n");
781 if (ret < 0)
782 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200783 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100784
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200785 ret = snprintf(str + offset, rem,
786 "Global setting for all subsystems\n");
787 if (ret < 0)
788 goto err;
789 OSMO_SNPRINTF_RET(ret, rem, offset, len);
790
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100791 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200792 if (info->cat[i].name == NULL)
793 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100794 ret = snprintf(str + offset, rem, "%s\n",
795 info->cat[i].description);
796 if (ret < 0)
797 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200798 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100799 }
800 for (i = 0; i < LOGLEVEL_DEFS; i++) {
801 ret = snprintf(str + offset, rem, "%s\n",
802 loglevel_descriptions[i]);
803 if (ret < 0)
804 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200805 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100806 }
807err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200808 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200809 return str;
810}
811
Harald Welte18fc4652011-08-17 14:14:17 +0200812/*! \brief Initialize the Osmocom logging core
813 * \param[in] inf Information regarding logging categories
814 * \param[in] ctx \ref talloc context for logging allocations
815 * \returns 0 in case of success, negative in case of error
816 */
Harald Welteb43bc042011-06-27 10:29:17 +0200817int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800818{
Harald Welteb43bc042011-06-27 10:29:17 +0200819 int i;
820
821 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
822 if (!tall_log_ctx)
823 return -ENOMEM;
824
825 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
826 if (!osmo_log_info)
827 return -ENOMEM;
828
Holger Hans Peter Freytherb7d0f462013-12-29 19:38:01 +0100829 osmo_log_info->filter_fn = inf->filter_fn;
Harald Welteb43bc042011-06-27 10:29:17 +0200830 osmo_log_info->num_cat_user = inf->num_cat;
831 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200832 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200833
834 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
835 struct log_info_cat,
836 osmo_log_info->num_cat);
837 if (!osmo_log_info->cat) {
838 talloc_free(osmo_log_info);
839 osmo_log_info = NULL;
840 return -ENOMEM;
841 }
842
843 /* copy over the user part */
844 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200845 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
846 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200847 sizeof(struct log_info_cat));
848 }
849
850 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200851 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200852 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200853 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200854 &internal_cat[i], sizeof(struct log_info_cat));
855 }
856
857 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800858}
Harald Welte18fc4652011-08-17 14:14:17 +0200859
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200860/*! @} */