blob: eab8bdf835f18399c648560c13c6787a9113fc7c [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 Welte18fc4652011-08-17 14:14:17 +0200600/*! \brief Generates the logging command string for VTY
601 * \param[in] unused_info Deprecated parameter, no longer used!
602 */
Harald Weltece9fec32011-06-27 14:19:16 +0200603const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200604{
Harald Weltece9fec32011-06-27 14:19:16 +0200605 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100606 int len = 0, offset = 0, ret, i, rem;
607 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200608 char *str;
609
Harald Welteb43bc042011-06-27 10:29:17 +0200610 for (i = 0; i < info->num_cat; i++) {
611 if (info->cat[i].name == NULL)
612 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100613 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200614 }
Harald Welte7638af92010-05-11 16:39:22 +0200615
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100616 for (i = 0; i < LOGLEVEL_DEFS; i++)
617 size += strlen(loglevel_strs[i].str) + 1;
618
619 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200620 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200621 if (!str)
622 return NULL;
623
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200624 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100625 if (ret < 0)
626 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200627 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200628
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100629 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200630 if (info->cat[i].name) {
631 int j, name_len = strlen(info->cat[i].name)+1;
632 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100633
Harald Welteb43bc042011-06-27 10:29:17 +0200634 for (j = 0; j < name_len; j++)
635 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100636
Harald Welteb43bc042011-06-27 10:29:17 +0200637 name[name_len-1] = '\0';
638 ret = snprintf(str + offset, rem, "%s|", name+1);
639 if (ret < 0)
640 goto err;
641 OSMO_SNPRINTF_RET(ret, rem, offset, len);
642 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100643 }
644 offset--; /* to remove the trailing | */
645 rem++;
646
647 ret = snprintf(str + offset, rem, ") (");
648 if (ret < 0)
649 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200650 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100651
652 for (i = 0; i < LOGLEVEL_DEFS; i++) {
653 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
654 char loglevel_str[loglevel_str_len];
655
656 for (j = 0; j < loglevel_str_len; j++)
657 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
658
659 loglevel_str[loglevel_str_len-1] = '\0';
660 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
661 if (ret < 0)
662 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200663 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100664 }
665 offset--; /* to remove the trailing | */
666 rem++;
667
668 ret = snprintf(str + offset, rem, ")");
669 if (ret < 0)
670 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200671 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100672err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200673 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100674 return str;
675}
676
Harald Welte18fc4652011-08-17 14:14:17 +0200677/*! \brief Generates the logging command description for VTY
678 * \param[in] unused_info Deprecated parameter, no longer used!
679 */
Harald Weltece9fec32011-06-27 14:19:16 +0200680const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100681{
Harald Weltece9fec32011-06-27 14:19:16 +0200682 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100683 char *str;
684 int i, ret, len = 0, offset = 0, rem;
685 unsigned int size =
686 strlen(LOGGING_STR
687 "Set the log level for a specified category\n") + 1;
688
Harald Welteb43bc042011-06-27 10:29:17 +0200689 for (i = 0; i < info->num_cat; i++) {
690 if (info->cat[i].name == NULL)
691 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100692 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200693 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100694
695 for (i = 0; i < LOGLEVEL_DEFS; i++)
696 size += strlen(loglevel_descriptions[i]) + 1;
697
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200698 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100699 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200700 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100701 if (!str)
702 return NULL;
703
704 ret = snprintf(str + offset, rem, LOGGING_STR
705 "Set the log level for a specified category\n");
706 if (ret < 0)
707 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200708 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100709
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200710 ret = snprintf(str + offset, rem,
711 "Global setting for all subsystems\n");
712 if (ret < 0)
713 goto err;
714 OSMO_SNPRINTF_RET(ret, rem, offset, len);
715
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100716 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200717 if (info->cat[i].name == NULL)
718 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100719 ret = snprintf(str + offset, rem, "%s\n",
720 info->cat[i].description);
721 if (ret < 0)
722 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200723 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100724 }
725 for (i = 0; i < LOGLEVEL_DEFS; i++) {
726 ret = snprintf(str + offset, rem, "%s\n",
727 loglevel_descriptions[i]);
728 if (ret < 0)
729 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200730 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100731 }
732err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200733 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200734 return str;
735}
736
Harald Welte18fc4652011-08-17 14:14:17 +0200737/*! \brief Initialize the Osmocom logging core
738 * \param[in] inf Information regarding logging categories
739 * \param[in] ctx \ref talloc context for logging allocations
740 * \returns 0 in case of success, negative in case of error
741 */
Harald Welteb43bc042011-06-27 10:29:17 +0200742int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800743{
Harald Welteb43bc042011-06-27 10:29:17 +0200744 int i;
745
746 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
747 if (!tall_log_ctx)
748 return -ENOMEM;
749
750 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
751 if (!osmo_log_info)
752 return -ENOMEM;
753
754 osmo_log_info->num_cat_user = inf->num_cat;
755 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200756 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200757
758 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
759 struct log_info_cat,
760 osmo_log_info->num_cat);
761 if (!osmo_log_info->cat) {
762 talloc_free(osmo_log_info);
763 osmo_log_info = NULL;
764 return -ENOMEM;
765 }
766
767 /* copy over the user part */
768 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200769 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
770 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200771 sizeof(struct log_info_cat));
772 }
773
774 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200775 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200776 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200777 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200778 &internal_cat[i], sizeof(struct log_info_cat));
779 }
780
781 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800782}
Harald Welte18fc4652011-08-17 14:14:17 +0200783
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200784/*! @} */