blob: f9d789d74a72e5213e7724a4764f919152727213 [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 },
Harald Welteb43bc042011-06-27 10:29:17 +0200114};
115
Harald Weltede6e4982012-12-06 21:25:27 +0100116/*! \brief descriptive string for each log level */
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100117/* You have to keep this in sync with the structure loglevel_strs. */
118const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
119 "Log simply everything",
120 "Log debug messages and higher levels",
121 "Log informational messages and higher levels",
122 "Log noticable messages and higher levels",
123 "Log error messages and higher levels",
124 "Log only fatal messages",
125 NULL,
126};
127
Harald Welteb43bc042011-06-27 10:29:17 +0200128/* special magic for negative (library-internal) log subsystem numbers */
129static int subsys_lib2index(int subsys)
130{
131 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
132}
133
Harald Welte18fc4652011-08-17 14:14:17 +0200134/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800135int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800136{
137 return get_string_value(loglevel_strs, lvl);
138}
139
Harald Welte18fc4652011-08-17 14:14:17 +0200140/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200141const char *log_level_str(unsigned int lvl)
142{
143 return get_value_string(loglevel_strs, lvl);
144}
145
Harald Welte18fc4652011-08-17 14:14:17 +0200146/*! \brief parse a human-readable log category into numeric form
147 * \param[in] category human-readable log category name
148 * \returns numeric category value, or -EINVAL otherwise
149 */
Harald Welte3ae27582010-03-26 21:24:24 +0800150int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800151{
152 int i;
153
Harald Welte4ebdf742010-05-19 19:54:00 +0200154 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200155 if (osmo_log_info->cat[i].name == NULL)
156 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200157 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800158 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800159 }
160
161 return -EINVAL;
162}
163
Harald Welte18fc4652011-08-17 14:14:17 +0200164/*! \brief parse the log category mask
165 * \param[in] target log target to be configured
166 * \param[in] _mask log category mask string
167 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800168 * The format can be this: category1:category2:category3
169 * or category1,2:category2,3:...
170 */
Harald Welte3ae27582010-03-26 21:24:24 +0800171void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800172{
173 int i = 0;
174 char *mask = strdup(_mask);
175 char *category_token = NULL;
176
177 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200178 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800179 target->categories[i].enabled = 0;
180
181 category_token = strtok(mask, ":");
182 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200183 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200184 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800185 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200186
187 if (!osmo_log_info->cat[i].name)
188 continue;
189
190 length = strlen(category_token);
191 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200192
193 /* Use longest length not to match subocurrences. */
194 if (cat_length > length)
195 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800196
197 if (colon)
198 length = colon - category_token;
199
Harald Welte4ebdf742010-05-19 19:54:00 +0200200 if (strncasecmp(osmo_log_info->cat[i].name,
201 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800202 int level = 0;
203
204 if (colon)
205 level = atoi(colon+1);
206
Harald Weltefaadfe22010-03-26 21:05:43 +0800207 target->categories[i].enabled = 1;
208 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800209 }
210 }
211 } while ((category_token = strtok(NULL, ":")));
212
213 free(mask);
214}
215
216static const char* color(int subsys)
217{
Harald Welte4ebdf742010-05-19 19:54:00 +0200218 if (subsys < osmo_log_info->num_cat)
219 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800220
Harald Welted788f662010-03-26 09:45:03 +0800221 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800222}
223
Harald Welte3ae27582010-03-26 21:24:24 +0800224static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200225 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100226 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800227{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800228 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200229 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800230
231 /* are we using color */
232 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800233 const char *c = color(subsys);
234 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200235 ret = snprintf(buf + offset, rem, "%s", color(subsys));
236 if (ret < 0)
237 goto err;
238 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800239 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800240 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800241 if (!cont) {
242 if (target->print_timestamp) {
243 char *timestr;
244 time_t tm;
245 tm = time(NULL);
246 timestr = ctime(&tm);
247 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200248 ret = snprintf(buf + offset, rem, "%s ", timestr);
249 if (ret < 0)
250 goto err;
251 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800252 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200253 if (target->print_filename) {
254 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
255 subsys, file, line);
256 if (ret < 0)
257 goto err;
258 OSMO_SNPRINTF_RET(ret, rem, offset, len);
259 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800260 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200261 ret = vsnprintf(buf + offset, rem, format, ap);
262 if (ret < 0)
263 goto err;
264 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800265
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200266 ret = snprintf(buf + offset, rem, "%s",
267 target->use_color ? "\033[0;m" : "");
268 if (ret < 0)
269 goto err;
270 OSMO_SNPRINTF_RET(ret, rem, offset, len);
271err:
272 buf[sizeof(buf)-1] = '\0';
273 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800274}
275
Harald Welte36c5a3e2011-08-27 14:33:19 +0200276/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200277void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200278 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800279{
Harald Welte3ae27582010-03-26 21:24:24 +0800280 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800281
Harald Welteb43bc042011-06-27 10:29:17 +0200282 if (subsys < 0)
283 subsys = subsys_lib2index(subsys);
284
285 if (subsys > osmo_log_info->num_cat)
286 subsys = DLGLOBAL;
287
Harald Welte28222962011-02-18 20:37:04 +0100288 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800289 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800290 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200291 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800292
293 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800294 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800295 if (!category->enabled)
296 continue;
297
298 /* Check the global log level */
299 if (tar->loglevel != 0 && level < tar->loglevel)
300 continue;
301
302 /* Check the category log level */
303 if (tar->loglevel == 0 && category->loglevel != 0 &&
304 level < category->loglevel)
305 continue;
306
307 /* Apply filters here... if that becomes messy we will
308 * need to put filters in a list and each filter will
309 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800310 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800311 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200312 else if (osmo_log_info->filter_fn)
313 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800314 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200315 if (!output)
316 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800317
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200318 /* According to the manpage, vsnprintf leaves the value of ap
319 * in undefined state. Since _output uses vsnprintf and it may
320 * be called several times, we have to pass a copy of ap. */
321 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200322 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200323 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800324 }
325}
326
Harald Weltede6e4982012-12-06 21:25:27 +0100327/*! \brief logging function used by DEBUGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200328void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800329 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800330{
331 va_list ap;
332
333 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200334 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800335 va_end(ap);
336}
337
Harald Weltede6e4982012-12-06 21:25:27 +0100338/*! \brief logging function used by LOGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200339void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800340{
341 va_list ap;
342
343 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200344 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800345 va_end(ap);
346}
347
Harald Welte18fc4652011-08-17 14:14:17 +0200348/*! \brief Register a new log target with the logging core
349 * \param[in] target Log target to be registered
350 */
Harald Welte3ae27582010-03-26 21:24:24 +0800351void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800352{
Harald Welte28222962011-02-18 20:37:04 +0100353 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800354}
355
Harald Welte18fc4652011-08-17 14:14:17 +0200356/*! \brief Unregister a log target from the logging core
357 * \param[in] target Log target to be unregistered
358 */
Harald Welte3ae27582010-03-26 21:24:24 +0800359void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800360{
361 llist_del(&target->entry);
362}
363
Harald Welte18fc4652011-08-17 14:14:17 +0200364/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800365void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800366{
Harald Welte3ae27582010-03-26 21:24:24 +0800367 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800368}
369
Harald Welte18fc4652011-08-17 14:14:17 +0200370/*! \brief Set the logging context
371 * \param[in] ctx_nr logging context number
372 * \param[in] value value to which the context is to be set
373 *
374 * A logging context is something like the subscriber identity to which
375 * the currently processed message relates, or the BTS through which it
376 * was received. As soon as this data is known, it can be set using
377 * this function. The main use of context information is for logging
378 * filters.
379 */
Harald Welte3ae27582010-03-26 21:24:24 +0800380int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800381{
Harald Welte3ae27582010-03-26 21:24:24 +0800382 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800383 return -EINVAL;
384
Harald Welte3ae27582010-03-26 21:24:24 +0800385 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800386
387 return 0;
388}
389
Harald Welte18fc4652011-08-17 14:14:17 +0200390/*! \brief Enable the \ref LOG_FILTER_ALL log filter
391 * \param[in] target Log target to be affected
392 * \param[in] all enable (1) or disable (0) the ALL filter
393 *
394 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
395 * be printed. It acts as a wildcard. Setting it to \a 1 means there
396 * is no filtering.
397 */
Harald Welte3ae27582010-03-26 21:24:24 +0800398void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800399{
400 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800401 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800402 else
Harald Welte3ae27582010-03-26 21:24:24 +0800403 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800404}
405
Harald Welte18fc4652011-08-17 14:14:17 +0200406/*! \brief Enable or disable the use of colored output
407 * \param[in] target Log target to be affected
408 * \param[in] use_color Use color (1) or don't use color (0)
409 */
Harald Welte3ae27582010-03-26 21:24:24 +0800410void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800411{
412 target->use_color = use_color;
413}
414
Harald Welte18fc4652011-08-17 14:14:17 +0200415/*! \brief Enable or disable printing of timestamps while logging
416 * \param[in] target Log target to be affected
417 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
418 */
Harald Welte3ae27582010-03-26 21:24:24 +0800419void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800420{
421 target->print_timestamp = print_timestamp;
422}
423
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200424/*! \brief Enable or disable printing of the filename while logging
425 * \param[in] target Log target to be affected
426 * \param[in] print_filename Enable (1) or disable (0) filenames
427 */
428void log_set_print_filename(struct log_target *target, int print_filename)
429{
430 target->print_filename = print_filename;
431}
432
Harald Welte18fc4652011-08-17 14:14:17 +0200433/*! \brief Set the global log level for a given log target
434 * \param[in] target Log target to be affected
435 * \param[in] log_level New global log level
436 */
Harald Welte3ae27582010-03-26 21:24:24 +0800437void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800438{
439 target->loglevel = log_level;
440}
441
Harald Weltede6e4982012-12-06 21:25:27 +0100442/*! \brief Set a category filter on a given log target
443 * \param[in] target Log target to be affected
444 * \param[in] category Log category to be affected
445 * \param[in] enable whether to enable or disable the filter
446 * \param[in] level Log level of the filter
447 */
Harald Welte3ae27582010-03-26 21:24:24 +0800448void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800449 int enable, int level)
450{
Harald Welte4ebdf742010-05-19 19:54:00 +0200451 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800452 return;
453 target->categories[category].enabled = !!enable;
454 target->categories[category].loglevel = level;
455}
456
Harald Welte76e72ab2011-02-17 15:52:39 +0100457static void _file_output(struct log_target *target, unsigned int level,
458 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800459{
Harald Welte0083cd32010-08-25 14:55:44 +0200460 fprintf(target->tgt_file.out, "%s", log);
461 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800462}
463
Harald Welte18fc4652011-08-17 14:14:17 +0200464/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800465struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800466{
Harald Welte3ae27582010-03-26 21:24:24 +0800467 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800468 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800469
Harald Welte3ae27582010-03-26 21:24:24 +0800470 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800471 if (!target)
472 return NULL;
473
Harald Welteb43bc042011-06-27 10:29:17 +0200474 target->categories = talloc_zero_array(target,
475 struct log_category,
476 osmo_log_info->num_cat);
477 if (!target->categories) {
478 talloc_free(target);
479 return NULL;
480 }
481
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800482 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800483
484 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200485 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800486 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200487 cat->enabled = osmo_log_info->cat[i].enabled;
488 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800489 }
490
491 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800492 target->use_color = 1;
493 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200494 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800495
496 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800497 target->loglevel = 0;
498 return target;
499}
500
Harald Welte18fc4652011-08-17 14:14:17 +0200501/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800502struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800503{
Harald Weltea3b844c2010-03-27 00:04:40 +0800504/* since C89/C99 says stderr is a macro, we can safely do this! */
505#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800506 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800507
Harald Welte3ae27582010-03-26 21:24:24 +0800508 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800509 if (!target)
510 return NULL;
511
Harald Welte28222962011-02-18 20:37:04 +0100512 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200513 target->tgt_file.out = stderr;
514 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800515 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800516#else
517 return NULL;
518#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800519}
520
Harald Welte18fc4652011-08-17 14:14:17 +0200521/*! \brief Create a new file-based log target
522 * \param[in] fname File name of the new log file
523 * \returns Log target in case of success, NULL otherwise
524 */
Harald Welte3086c392010-08-25 19:10:50 +0200525struct log_target *log_target_create_file(const char *fname)
526{
527 struct log_target *target;
528
529 target = log_target_create();
530 if (!target)
531 return NULL;
532
Harald Welte28222962011-02-18 20:37:04 +0100533 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200534 target->tgt_file.out = fopen(fname, "a");
535 if (!target->tgt_file.out)
536 return NULL;
537
538 target->output = _file_output;
539
540 target->tgt_file.fname = talloc_strdup(target, fname);
541
542 return target;
543}
544
Harald Welte18fc4652011-08-17 14:14:17 +0200545/*! \brief Find a registered log target
546 * \param[in] type Log target type
547 * \param[in] fname File name
548 * \returns Log target (if found), NULL otherwise
549 */
Harald Welte28222962011-02-18 20:37:04 +0100550struct log_target *log_target_find(int type, const char *fname)
551{
552 struct log_target *tgt;
553
554 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
555 if (tgt->type != type)
556 continue;
557 if (tgt->type == LOG_TGT_TYPE_FILE) {
558 if (!strcmp(fname, tgt->tgt_file.fname))
559 return tgt;
560 } else
561 return tgt;
562 }
563 return NULL;
564}
565
Harald Welte18fc4652011-08-17 14:14:17 +0200566/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200567void log_target_destroy(struct log_target *target)
568{
569
570 /* just in case, to make sure we don't have any references */
571 log_del_target(target);
572
573 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200574/* since C89/C99 says stderr is a macro, we can safely do this! */
575#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200576 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200577 if (target->tgt_file.out != stderr)
578#endif
579 {
Harald Welte3086c392010-08-25 19:10:50 +0200580 fclose(target->tgt_file.out);
581 target->tgt_file.out = NULL;
582 }
583 }
584
585 talloc_free(target);
586}
587
Harald Welte18fc4652011-08-17 14:14:17 +0200588/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200589int log_target_file_reopen(struct log_target *target)
590{
591 fclose(target->tgt_file.out);
592
593 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
594 if (!target->tgt_file.out)
595 return -errno;
596
597 /* we assume target->output already to be set */
598
599 return 0;
600}
601
Harald Welte4de854d2013-03-18 19:01:40 +0100602/*! \brief close and re-open a log file (for log file rotation) */
603int log_targets_reopen(void)
604{
605 struct log_target *tar;
606 int rc = 0;
607
608 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
609 switch (tar->type) {
610 case LOG_TGT_TYPE_FILE:
611 if (log_target_file_reopen(tar) < 0)
612 rc = -1;
613 break;
614 default:
615 break;
616 }
617 }
618
619 return rc;
620}
621
Harald Welte18fc4652011-08-17 14:14:17 +0200622/*! \brief Generates the logging command string for VTY
623 * \param[in] unused_info Deprecated parameter, no longer used!
624 */
Harald Weltece9fec32011-06-27 14:19:16 +0200625const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200626{
Harald Weltece9fec32011-06-27 14:19:16 +0200627 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100628 int len = 0, offset = 0, ret, i, rem;
629 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200630 char *str;
631
Harald Welteb43bc042011-06-27 10:29:17 +0200632 for (i = 0; i < info->num_cat; i++) {
633 if (info->cat[i].name == NULL)
634 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100635 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200636 }
Harald Welte7638af92010-05-11 16:39:22 +0200637
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100638 for (i = 0; i < LOGLEVEL_DEFS; i++)
639 size += strlen(loglevel_strs[i].str) + 1;
640
641 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200642 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200643 if (!str)
644 return NULL;
645
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200646 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100647 if (ret < 0)
648 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200649 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200650
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100651 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200652 if (info->cat[i].name) {
653 int j, name_len = strlen(info->cat[i].name)+1;
654 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100655
Harald Welteb43bc042011-06-27 10:29:17 +0200656 for (j = 0; j < name_len; j++)
657 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100658
Harald Welteb43bc042011-06-27 10:29:17 +0200659 name[name_len-1] = '\0';
660 ret = snprintf(str + offset, rem, "%s|", name+1);
661 if (ret < 0)
662 goto err;
663 OSMO_SNPRINTF_RET(ret, rem, offset, len);
664 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100665 }
666 offset--; /* to remove the trailing | */
667 rem++;
668
669 ret = snprintf(str + offset, rem, ") (");
670 if (ret < 0)
671 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200672 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100673
674 for (i = 0; i < LOGLEVEL_DEFS; i++) {
675 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
676 char loglevel_str[loglevel_str_len];
677
678 for (j = 0; j < loglevel_str_len; j++)
679 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
680
681 loglevel_str[loglevel_str_len-1] = '\0';
682 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
683 if (ret < 0)
684 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200685 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100686 }
687 offset--; /* to remove the trailing | */
688 rem++;
689
690 ret = snprintf(str + offset, rem, ")");
691 if (ret < 0)
692 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200693 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100694err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200695 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100696 return str;
697}
698
Harald Welte18fc4652011-08-17 14:14:17 +0200699/*! \brief Generates the logging command description for VTY
700 * \param[in] unused_info Deprecated parameter, no longer used!
701 */
Harald Weltece9fec32011-06-27 14:19:16 +0200702const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100703{
Harald Weltece9fec32011-06-27 14:19:16 +0200704 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100705 char *str;
706 int i, ret, len = 0, offset = 0, rem;
707 unsigned int size =
708 strlen(LOGGING_STR
709 "Set the log level for a specified category\n") + 1;
710
Harald Welteb43bc042011-06-27 10:29:17 +0200711 for (i = 0; i < info->num_cat; i++) {
712 if (info->cat[i].name == NULL)
713 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100714 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200715 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100716
717 for (i = 0; i < LOGLEVEL_DEFS; i++)
718 size += strlen(loglevel_descriptions[i]) + 1;
719
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200720 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100721 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200722 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100723 if (!str)
724 return NULL;
725
726 ret = snprintf(str + offset, rem, LOGGING_STR
727 "Set the log level for a specified category\n");
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
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200732 ret = snprintf(str + offset, rem,
733 "Global setting for all subsystems\n");
734 if (ret < 0)
735 goto err;
736 OSMO_SNPRINTF_RET(ret, rem, offset, len);
737
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100738 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200739 if (info->cat[i].name == NULL)
740 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100741 ret = snprintf(str + offset, rem, "%s\n",
742 info->cat[i].description);
743 if (ret < 0)
744 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200745 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100746 }
747 for (i = 0; i < LOGLEVEL_DEFS; i++) {
748 ret = snprintf(str + offset, rem, "%s\n",
749 loglevel_descriptions[i]);
750 if (ret < 0)
751 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200752 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100753 }
754err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200755 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200756 return str;
757}
758
Harald Welte18fc4652011-08-17 14:14:17 +0200759/*! \brief Initialize the Osmocom logging core
760 * \param[in] inf Information regarding logging categories
761 * \param[in] ctx \ref talloc context for logging allocations
762 * \returns 0 in case of success, negative in case of error
763 */
Harald Welteb43bc042011-06-27 10:29:17 +0200764int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800765{
Harald Welteb43bc042011-06-27 10:29:17 +0200766 int i;
767
768 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
769 if (!tall_log_ctx)
770 return -ENOMEM;
771
772 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
773 if (!osmo_log_info)
774 return -ENOMEM;
775
Holger Hans Peter Freytherb7d0f462013-12-29 19:38:01 +0100776 osmo_log_info->filter_fn = inf->filter_fn;
Harald Welteb43bc042011-06-27 10:29:17 +0200777 osmo_log_info->num_cat_user = inf->num_cat;
778 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200779 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200780
781 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
782 struct log_info_cat,
783 osmo_log_info->num_cat);
784 if (!osmo_log_info->cat) {
785 talloc_free(osmo_log_info);
786 osmo_log_info = NULL;
787 return -ENOMEM;
788 }
789
790 /* copy over the user part */
791 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200792 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
793 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200794 sizeof(struct log_info_cat));
795 }
796
797 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200798 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200799 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200800 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200801 &internal_cat[i], sizeof(struct log_info_cat));
802 }
803
804 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800805}
Harald Welte18fc4652011-08-17 14:14:17 +0200806
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200807/*! @} */