blob: ffe6ecff049a235cef93366a38d3bfdeb50dfd86 [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 Weltecd6ed822013-02-19 11:57:14 +010058 { LOGL_DEBUG, "EVERYTHING" }, /* backwards compatibility */
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 Welteb43bc042011-06-27 10:29:17 +0200109};
110
Harald Weltede6e4982012-12-06 21:25:27 +0100111/*! \brief descriptive string for each log level */
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100112/* You have to keep this in sync with the structure loglevel_strs. */
113const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
114 "Log simply everything",
115 "Log debug messages and higher levels",
116 "Log informational messages and higher levels",
117 "Log noticable messages and higher levels",
118 "Log error messages and higher levels",
119 "Log only fatal messages",
120 NULL,
121};
122
Harald Welteb43bc042011-06-27 10:29:17 +0200123/* special magic for negative (library-internal) log subsystem numbers */
124static int subsys_lib2index(int subsys)
125{
126 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
127}
128
Harald Welte18fc4652011-08-17 14:14:17 +0200129/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800130int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800131{
132 return get_string_value(loglevel_strs, lvl);
133}
134
Harald Welte18fc4652011-08-17 14:14:17 +0200135/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200136const char *log_level_str(unsigned int lvl)
137{
Holger Hans Peter Freythera6428d22013-02-27 15:32:51 +0100138 /* backwards compatibility */
139 if (lvl == 0)
140 return loglevel_strs[0].str;
Harald Welte9ac22252010-05-11 11:19:40 +0200141 return get_value_string(loglevel_strs, lvl);
142}
143
Harald Welte18fc4652011-08-17 14:14:17 +0200144/*! \brief parse a human-readable log category into numeric form
145 * \param[in] category human-readable log category name
146 * \returns numeric category value, or -EINVAL otherwise
147 */
Harald Welte3ae27582010-03-26 21:24:24 +0800148int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800149{
150 int i;
151
Harald Welte4ebdf742010-05-19 19:54:00 +0200152 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200153 if (osmo_log_info->cat[i].name == NULL)
154 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200155 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800156 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800157 }
158
159 return -EINVAL;
160}
161
Harald Welte18fc4652011-08-17 14:14:17 +0200162/*! \brief parse the log category mask
163 * \param[in] target log target to be configured
164 * \param[in] _mask log category mask string
165 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800166 * The format can be this: category1:category2:category3
167 * or category1,2:category2,3:...
168 */
Harald Welte3ae27582010-03-26 21:24:24 +0800169void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800170{
171 int i = 0;
172 char *mask = strdup(_mask);
173 char *category_token = NULL;
174
175 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200176 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800177 target->categories[i].enabled = 0;
178
179 category_token = strtok(mask, ":");
180 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200181 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200182 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800183 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200184
185 if (!osmo_log_info->cat[i].name)
186 continue;
187
188 length = strlen(category_token);
189 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200190
191 /* Use longest length not to match subocurrences. */
192 if (cat_length > length)
193 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800194
195 if (colon)
196 length = colon - category_token;
197
Harald Welte4ebdf742010-05-19 19:54:00 +0200198 if (strncasecmp(osmo_log_info->cat[i].name,
199 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800200 int level = 0;
201
202 if (colon)
203 level = atoi(colon+1);
204
Harald Weltefaadfe22010-03-26 21:05:43 +0800205 target->categories[i].enabled = 1;
206 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800207 }
208 }
209 } while ((category_token = strtok(NULL, ":")));
210
211 free(mask);
212}
213
214static const char* color(int subsys)
215{
Harald Welte4ebdf742010-05-19 19:54:00 +0200216 if (subsys < osmo_log_info->num_cat)
217 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800218
Harald Welted788f662010-03-26 09:45:03 +0800219 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800220}
221
Harald Welte3ae27582010-03-26 21:24:24 +0800222static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200223 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100224 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800225{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800226 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200227 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800228
229 /* are we using color */
230 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800231 const char *c = color(subsys);
232 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200233 ret = snprintf(buf + offset, rem, "%s", color(subsys));
234 if (ret < 0)
235 goto err;
236 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800237 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800238 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800239 if (!cont) {
240 if (target->print_timestamp) {
241 char *timestr;
242 time_t tm;
243 tm = time(NULL);
244 timestr = ctime(&tm);
245 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200246 ret = snprintf(buf + offset, rem, "%s ", timestr);
247 if (ret < 0)
248 goto err;
249 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800250 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200251 if (target->print_filename) {
252 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
253 subsys, file, line);
254 if (ret < 0)
255 goto err;
256 OSMO_SNPRINTF_RET(ret, rem, offset, len);
257 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800258 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200259 ret = vsnprintf(buf + offset, rem, format, ap);
260 if (ret < 0)
261 goto err;
262 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800263
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200264 ret = snprintf(buf + offset, rem, "%s",
265 target->use_color ? "\033[0;m" : "");
266 if (ret < 0)
267 goto err;
268 OSMO_SNPRINTF_RET(ret, rem, offset, len);
269err:
270 buf[sizeof(buf)-1] = '\0';
271 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800272}
273
Harald Welte36c5a3e2011-08-27 14:33:19 +0200274/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200275void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200276 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800277{
Harald Welte3ae27582010-03-26 21:24:24 +0800278 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800279
Harald Welteb43bc042011-06-27 10:29:17 +0200280 if (subsys < 0)
281 subsys = subsys_lib2index(subsys);
282
283 if (subsys > osmo_log_info->num_cat)
284 subsys = DLGLOBAL;
285
Harald Welte28222962011-02-18 20:37:04 +0100286 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800287 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800288 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200289 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800290
291 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800292 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800293 if (!category->enabled)
294 continue;
295
296 /* Check the global log level */
297 if (tar->loglevel != 0 && level < tar->loglevel)
298 continue;
299
300 /* Check the category log level */
301 if (tar->loglevel == 0 && category->loglevel != 0 &&
302 level < category->loglevel)
303 continue;
304
305 /* Apply filters here... if that becomes messy we will
306 * need to put filters in a list and each filter will
307 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800308 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800309 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200310 else if (osmo_log_info->filter_fn)
311 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800312 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200313 if (!output)
314 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800315
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200316 /* According to the manpage, vsnprintf leaves the value of ap
317 * in undefined state. Since _output uses vsnprintf and it may
318 * be called several times, we have to pass a copy of ap. */
319 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200320 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200321 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800322 }
323}
324
Harald Weltede6e4982012-12-06 21:25:27 +0100325/*! \brief logging function used by DEBUGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200326void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800327 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800328{
329 va_list ap;
330
331 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200332 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800333 va_end(ap);
334}
335
Harald Weltede6e4982012-12-06 21:25:27 +0100336/*! \brief logging function used by LOGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200337void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800338{
339 va_list ap;
340
341 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200342 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800343 va_end(ap);
344}
345
Harald Welte18fc4652011-08-17 14:14:17 +0200346/*! \brief Register a new log target with the logging core
347 * \param[in] target Log target to be registered
348 */
Harald Welte3ae27582010-03-26 21:24:24 +0800349void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800350{
Harald Welte28222962011-02-18 20:37:04 +0100351 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800352}
353
Harald Welte18fc4652011-08-17 14:14:17 +0200354/*! \brief Unregister a log target from the logging core
355 * \param[in] target Log target to be unregistered
356 */
Harald Welte3ae27582010-03-26 21:24:24 +0800357void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800358{
359 llist_del(&target->entry);
360}
361
Harald Welte18fc4652011-08-17 14:14:17 +0200362/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800363void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800364{
Harald Welte3ae27582010-03-26 21:24:24 +0800365 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800366}
367
Harald Welte18fc4652011-08-17 14:14:17 +0200368/*! \brief Set the logging context
369 * \param[in] ctx_nr logging context number
370 * \param[in] value value to which the context is to be set
371 *
372 * A logging context is something like the subscriber identity to which
373 * the currently processed message relates, or the BTS through which it
374 * was received. As soon as this data is known, it can be set using
375 * this function. The main use of context information is for logging
376 * filters.
377 */
Harald Welte3ae27582010-03-26 21:24:24 +0800378int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800379{
Harald Welte3ae27582010-03-26 21:24:24 +0800380 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800381 return -EINVAL;
382
Harald Welte3ae27582010-03-26 21:24:24 +0800383 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800384
385 return 0;
386}
387
Harald Welte18fc4652011-08-17 14:14:17 +0200388/*! \brief Enable the \ref LOG_FILTER_ALL log filter
389 * \param[in] target Log target to be affected
390 * \param[in] all enable (1) or disable (0) the ALL filter
391 *
392 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
393 * be printed. It acts as a wildcard. Setting it to \a 1 means there
394 * is no filtering.
395 */
Harald Welte3ae27582010-03-26 21:24:24 +0800396void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800397{
398 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800399 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800400 else
Harald Welte3ae27582010-03-26 21:24:24 +0800401 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800402}
403
Harald Welte18fc4652011-08-17 14:14:17 +0200404/*! \brief Enable or disable the use of colored output
405 * \param[in] target Log target to be affected
406 * \param[in] use_color Use color (1) or don't use color (0)
407 */
Harald Welte3ae27582010-03-26 21:24:24 +0800408void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800409{
410 target->use_color = use_color;
411}
412
Harald Welte18fc4652011-08-17 14:14:17 +0200413/*! \brief Enable or disable printing of timestamps while logging
414 * \param[in] target Log target to be affected
415 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
416 */
Harald Welte3ae27582010-03-26 21:24:24 +0800417void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800418{
419 target->print_timestamp = print_timestamp;
420}
421
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200422/*! \brief Enable or disable printing of the filename while logging
423 * \param[in] target Log target to be affected
424 * \param[in] print_filename Enable (1) or disable (0) filenames
425 */
426void log_set_print_filename(struct log_target *target, int print_filename)
427{
428 target->print_filename = print_filename;
429}
430
Harald Welte18fc4652011-08-17 14:14:17 +0200431/*! \brief Set the global log level for a given log target
432 * \param[in] target Log target to be affected
433 * \param[in] log_level New global log level
434 */
Harald Welte3ae27582010-03-26 21:24:24 +0800435void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800436{
437 target->loglevel = log_level;
438}
439
Harald Weltede6e4982012-12-06 21:25:27 +0100440/*! \brief Set a category filter on a given log target
441 * \param[in] target Log target to be affected
442 * \param[in] category Log category to be affected
443 * \param[in] enable whether to enable or disable the filter
444 * \param[in] level Log level of the filter
445 */
Harald Welte3ae27582010-03-26 21:24:24 +0800446void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800447 int enable, int level)
448{
Harald Welte4ebdf742010-05-19 19:54:00 +0200449 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800450 return;
451 target->categories[category].enabled = !!enable;
452 target->categories[category].loglevel = level;
453}
454
Harald Welte76e72ab2011-02-17 15:52:39 +0100455static void _file_output(struct log_target *target, unsigned int level,
456 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800457{
Harald Welte0083cd32010-08-25 14:55:44 +0200458 fprintf(target->tgt_file.out, "%s", log);
459 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800460}
461
Harald Welte18fc4652011-08-17 14:14:17 +0200462/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800463struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800464{
Harald Welte3ae27582010-03-26 21:24:24 +0800465 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800466 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800467
Harald Welte3ae27582010-03-26 21:24:24 +0800468 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800469 if (!target)
470 return NULL;
471
Harald Welteb43bc042011-06-27 10:29:17 +0200472 target->categories = talloc_zero_array(target,
473 struct log_category,
474 osmo_log_info->num_cat);
475 if (!target->categories) {
476 talloc_free(target);
477 return NULL;
478 }
479
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800480 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800481
482 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200483 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800484 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200485 cat->enabled = osmo_log_info->cat[i].enabled;
486 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800487 }
488
489 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800490 target->use_color = 1;
491 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200492 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800493
494 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800495 target->loglevel = 0;
496 return target;
497}
498
Harald Welte18fc4652011-08-17 14:14:17 +0200499/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800500struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800501{
Harald Weltea3b844c2010-03-27 00:04:40 +0800502/* since C89/C99 says stderr is a macro, we can safely do this! */
503#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800504 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800505
Harald Welte3ae27582010-03-26 21:24:24 +0800506 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800507 if (!target)
508 return NULL;
509
Harald Welte28222962011-02-18 20:37:04 +0100510 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200511 target->tgt_file.out = stderr;
512 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800513 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800514#else
515 return NULL;
516#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800517}
518
Harald Welte18fc4652011-08-17 14:14:17 +0200519/*! \brief Create a new file-based log target
520 * \param[in] fname File name of the new log file
521 * \returns Log target in case of success, NULL otherwise
522 */
Harald Welte3086c392010-08-25 19:10:50 +0200523struct log_target *log_target_create_file(const char *fname)
524{
525 struct log_target *target;
526
527 target = log_target_create();
528 if (!target)
529 return NULL;
530
Harald Welte28222962011-02-18 20:37:04 +0100531 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200532 target->tgt_file.out = fopen(fname, "a");
533 if (!target->tgt_file.out)
534 return NULL;
535
536 target->output = _file_output;
537
538 target->tgt_file.fname = talloc_strdup(target, fname);
539
540 return target;
541}
542
Harald Welte18fc4652011-08-17 14:14:17 +0200543/*! \brief Find a registered log target
544 * \param[in] type Log target type
545 * \param[in] fname File name
546 * \returns Log target (if found), NULL otherwise
547 */
Harald Welte28222962011-02-18 20:37:04 +0100548struct log_target *log_target_find(int type, const char *fname)
549{
550 struct log_target *tgt;
551
552 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
553 if (tgt->type != type)
554 continue;
555 if (tgt->type == LOG_TGT_TYPE_FILE) {
556 if (!strcmp(fname, tgt->tgt_file.fname))
557 return tgt;
558 } else
559 return tgt;
560 }
561 return NULL;
562}
563
Harald Welte18fc4652011-08-17 14:14:17 +0200564/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200565void log_target_destroy(struct log_target *target)
566{
567
568 /* just in case, to make sure we don't have any references */
569 log_del_target(target);
570
571 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200572/* since C89/C99 says stderr is a macro, we can safely do this! */
573#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200574 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200575 if (target->tgt_file.out != stderr)
576#endif
577 {
Harald Welte3086c392010-08-25 19:10:50 +0200578 fclose(target->tgt_file.out);
579 target->tgt_file.out = NULL;
580 }
581 }
582
583 talloc_free(target);
584}
585
Harald Welte18fc4652011-08-17 14:14:17 +0200586/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200587int log_target_file_reopen(struct log_target *target)
588{
589 fclose(target->tgt_file.out);
590
591 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
592 if (!target->tgt_file.out)
593 return -errno;
594
595 /* we assume target->output already to be set */
596
597 return 0;
598}
599
Harald Welte4de854d2013-03-18 19:01:40 +0100600/*! \brief close and re-open a log file (for log file rotation) */
601int log_targets_reopen(void)
602{
603 struct log_target *tar;
604 int rc = 0;
605
606 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
607 switch (tar->type) {
608 case LOG_TGT_TYPE_FILE:
609 if (log_target_file_reopen(tar) < 0)
610 rc = -1;
611 break;
612 default:
613 break;
614 }
615 }
616
617 return rc;
618}
619
Harald Welte18fc4652011-08-17 14:14:17 +0200620/*! \brief Generates the logging command string for VTY
621 * \param[in] unused_info Deprecated parameter, no longer used!
622 */
Harald Weltece9fec32011-06-27 14:19:16 +0200623const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200624{
Harald Weltece9fec32011-06-27 14:19:16 +0200625 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100626 int len = 0, offset = 0, ret, i, rem;
627 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200628 char *str;
629
Harald Welteb43bc042011-06-27 10:29:17 +0200630 for (i = 0; i < info->num_cat; i++) {
631 if (info->cat[i].name == NULL)
632 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100633 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200634 }
Harald Welte7638af92010-05-11 16:39:22 +0200635
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100636 for (i = 0; i < LOGLEVEL_DEFS; i++)
637 size += strlen(loglevel_strs[i].str) + 1;
638
639 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200640 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200641 if (!str)
642 return NULL;
643
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200644 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100645 if (ret < 0)
646 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200647 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200648
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100649 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200650 if (info->cat[i].name) {
651 int j, name_len = strlen(info->cat[i].name)+1;
652 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100653
Harald Welteb43bc042011-06-27 10:29:17 +0200654 for (j = 0; j < name_len; j++)
655 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100656
Harald Welteb43bc042011-06-27 10:29:17 +0200657 name[name_len-1] = '\0';
658 ret = snprintf(str + offset, rem, "%s|", name+1);
659 if (ret < 0)
660 goto err;
661 OSMO_SNPRINTF_RET(ret, rem, offset, len);
662 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100663 }
664 offset--; /* to remove the trailing | */
665 rem++;
666
667 ret = snprintf(str + offset, rem, ") (");
668 if (ret < 0)
669 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200670 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100671
672 for (i = 0; i < LOGLEVEL_DEFS; i++) {
673 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
674 char loglevel_str[loglevel_str_len];
675
676 for (j = 0; j < loglevel_str_len; j++)
677 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
678
679 loglevel_str[loglevel_str_len-1] = '\0';
680 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
681 if (ret < 0)
682 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200683 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100684 }
685 offset--; /* to remove the trailing | */
686 rem++;
687
688 ret = snprintf(str + offset, rem, ")");
689 if (ret < 0)
690 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200691 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100692err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200693 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100694 return str;
695}
696
Harald Welte18fc4652011-08-17 14:14:17 +0200697/*! \brief Generates the logging command description for VTY
698 * \param[in] unused_info Deprecated parameter, no longer used!
699 */
Harald Weltece9fec32011-06-27 14:19:16 +0200700const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100701{
Harald Weltece9fec32011-06-27 14:19:16 +0200702 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100703 char *str;
704 int i, ret, len = 0, offset = 0, rem;
705 unsigned int size =
706 strlen(LOGGING_STR
707 "Set the log level for a specified category\n") + 1;
708
Harald Welteb43bc042011-06-27 10:29:17 +0200709 for (i = 0; i < info->num_cat; i++) {
710 if (info->cat[i].name == NULL)
711 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100712 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200713 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100714
715 for (i = 0; i < LOGLEVEL_DEFS; i++)
716 size += strlen(loglevel_descriptions[i]) + 1;
717
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200718 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100719 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200720 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100721 if (!str)
722 return NULL;
723
724 ret = snprintf(str + offset, rem, LOGGING_STR
725 "Set the log level for a specified category\n");
726 if (ret < 0)
727 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200728 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100729
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200730 ret = snprintf(str + offset, rem,
731 "Global setting for all subsystems\n");
732 if (ret < 0)
733 goto err;
734 OSMO_SNPRINTF_RET(ret, rem, offset, len);
735
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100736 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200737 if (info->cat[i].name == NULL)
738 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100739 ret = snprintf(str + offset, rem, "%s\n",
740 info->cat[i].description);
741 if (ret < 0)
742 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200743 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100744 }
745 for (i = 0; i < LOGLEVEL_DEFS; i++) {
746 ret = snprintf(str + offset, rem, "%s\n",
747 loglevel_descriptions[i]);
748 if (ret < 0)
749 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200750 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100751 }
752err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200753 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200754 return str;
755}
756
Harald Welte18fc4652011-08-17 14:14:17 +0200757/*! \brief Initialize the Osmocom logging core
758 * \param[in] inf Information regarding logging categories
759 * \param[in] ctx \ref talloc context for logging allocations
760 * \returns 0 in case of success, negative in case of error
761 */
Harald Welteb43bc042011-06-27 10:29:17 +0200762int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800763{
Harald Welteb43bc042011-06-27 10:29:17 +0200764 int i;
765
766 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
767 if (!tall_log_ctx)
768 return -ENOMEM;
769
770 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
771 if (!osmo_log_info)
772 return -ENOMEM;
773
774 osmo_log_info->num_cat_user = inf->num_cat;
775 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200776 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200777
778 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
779 struct log_info_cat,
780 osmo_log_info->num_cat);
781 if (!osmo_log_info->cat) {
782 talloc_free(osmo_log_info);
783 osmo_log_info = NULL;
784 return -ENOMEM;
785 }
786
787 /* copy over the user part */
788 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200789 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
790 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200791 sizeof(struct log_info_cat));
792 }
793
794 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200795 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200796 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200797 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200798 &internal_cat[i], sizeof(struct log_info_cat));
799 }
800
801 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800802}
Harald Welte18fc4652011-08-17 14:14:17 +0200803
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200804/*! @} */