blob: 20b0596b7187acf6366fb0f03ceb983528403461 [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>
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +010041#include <sys/time.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080042#include <errno.h>
43
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010044#include <osmocom/core/talloc.h>
45#include <osmocom/core/utils.h>
46#include <osmocom/core/logging.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080047
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010048#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
49
Harald Welteb43bc042011-06-27 10:29:17 +020050struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080051
Harald Welte3ae27582010-03-26 21:24:24 +080052static struct log_context log_context;
53static void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010054LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080055
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010056#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
57
58static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
Harald Welte9b186702013-03-19 09:55:28 +010059 { 0, "EVERYTHING" },
Harald Welte4a2bb9e2010-03-26 09:33:40 +080060 { LOGL_DEBUG, "DEBUG" },
61 { LOGL_INFO, "INFO" },
62 { LOGL_NOTICE, "NOTICE" },
63 { LOGL_ERROR, "ERROR" },
64 { LOGL_FATAL, "FATAL" },
65 { 0, NULL },
66};
67
Harald Welteb43bc042011-06-27 10:29:17 +020068#define INT2IDX(x) (-1*(x)-1)
69static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
70 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
71 .name = "DLGLOBAL",
72 .description = "Library-internal global log family",
73 .loglevel = LOGL_NOTICE,
74 .enabled = 1,
75 },
root8a996b42011-09-26 11:22:21 +020076 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
77 .name = "DLLAPD",
78 .description = "LAPD in libosmogsm",
Harald Welte1f0b8c22011-06-27 10:51:37 +020079 .loglevel = LOGL_NOTICE,
80 .enabled = 1,
81 },
Harald Welte892e6212011-07-19 14:31:44 +020082 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +020083 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020084 .description = "A-bis Intput Subsystem",
85 .loglevel = LOGL_NOTICE,
86 .enabled = 1,
87 },
Harald Welte892e6212011-07-19 14:31:44 +020088 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +020089 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020090 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
91 .loglevel = LOGL_NOTICE,
92 .enabled = 1,
93 },
Harald Welte892e6212011-07-19 14:31:44 +020094 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +020095 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020096 .description = "A-bis Input Driver for Signalling",
97 .enabled = 0, .loglevel = LOGL_NOTICE,
98 },
Harald Welte892e6212011-07-19 14:31:44 +020099 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200100 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200101 .description = "A-bis Input Driver for B-Channels (voice)",
102 .enabled = 0, .loglevel = LOGL_NOTICE,
103 },
Andreas Eversbergc626da92011-10-28 03:53:50 +0200104 [INT2IDX(DLSMS)] = {
105 .name = "DLSMS",
106 .description = "Layer3 Short Message Service (SMS)",
107 .enabled = 1, .loglevel = LOGL_NOTICE,
108 .color = "\033[1;38m",
109 },
Harald Welte7fd0c832014-08-20 19:58:13 +0200110 [INT2IDX(DLCTRL)] = {
111 .name = "DLCTRL",
112 .description = "Control Interface",
113 .enabled = 1, .loglevel = LOGL_NOTICE,
114 },
Holger Hans Peter Freythera5dc19d2014-12-04 14:35:21 +0100115 [INT2IDX(DLGTP)] = {
116 .name = "DLGTP",
117 .description = "GPRS GTP library",
118 .enabled = 1, .loglevel = LOGL_NOTICE,
119 },
Harald Welteb43bc042011-06-27 10:29:17 +0200120};
121
Harald Weltede6e4982012-12-06 21:25:27 +0100122/*! \brief descriptive string for each log level */
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100123/* You have to keep this in sync with the structure loglevel_strs. */
124const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
Holger Hans Peter Freyther6ec6bd92014-12-28 18:27:38 +0100125 "Don't use. It doesn't log anything",
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100126 "Log debug messages and higher levels",
127 "Log informational messages and higher levels",
128 "Log noticable messages and higher levels",
129 "Log error messages and higher levels",
130 "Log only fatal messages",
131 NULL,
132};
133
Harald Welteb43bc042011-06-27 10:29:17 +0200134/* special magic for negative (library-internal) log subsystem numbers */
135static int subsys_lib2index(int subsys)
136{
137 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
138}
139
Harald Welte18fc4652011-08-17 14:14:17 +0200140/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800141int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800142{
143 return get_string_value(loglevel_strs, lvl);
144}
145
Harald Welte18fc4652011-08-17 14:14:17 +0200146/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200147const char *log_level_str(unsigned int lvl)
148{
149 return get_value_string(loglevel_strs, lvl);
150}
151
Harald Welte18fc4652011-08-17 14:14:17 +0200152/*! \brief parse a human-readable log category into numeric form
153 * \param[in] category human-readable log category name
154 * \returns numeric category value, or -EINVAL otherwise
155 */
Harald Welte3ae27582010-03-26 21:24:24 +0800156int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800157{
158 int i;
159
Harald Welte4ebdf742010-05-19 19:54:00 +0200160 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200161 if (osmo_log_info->cat[i].name == NULL)
162 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200163 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800164 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800165 }
166
167 return -EINVAL;
168}
169
Harald Welte18fc4652011-08-17 14:14:17 +0200170/*! \brief parse the log category mask
171 * \param[in] target log target to be configured
172 * \param[in] _mask log category mask string
173 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800174 * The format can be this: category1:category2:category3
175 * or category1,2:category2,3:...
176 */
Harald Welte3ae27582010-03-26 21:24:24 +0800177void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800178{
179 int i = 0;
180 char *mask = strdup(_mask);
181 char *category_token = NULL;
182
183 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200184 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800185 target->categories[i].enabled = 0;
186
187 category_token = strtok(mask, ":");
188 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200189 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200190 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800191 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200192
193 if (!osmo_log_info->cat[i].name)
194 continue;
195
196 length = strlen(category_token);
197 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200198
199 /* Use longest length not to match subocurrences. */
200 if (cat_length > length)
201 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800202
203 if (colon)
204 length = colon - category_token;
205
Harald Welte4ebdf742010-05-19 19:54:00 +0200206 if (strncasecmp(osmo_log_info->cat[i].name,
207 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800208 int level = 0;
209
210 if (colon)
211 level = atoi(colon+1);
212
Harald Weltefaadfe22010-03-26 21:05:43 +0800213 target->categories[i].enabled = 1;
214 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800215 }
216 }
217 } while ((category_token = strtok(NULL, ":")));
218
219 free(mask);
220}
221
222static const char* color(int subsys)
223{
Harald Welte4ebdf742010-05-19 19:54:00 +0200224 if (subsys < osmo_log_info->num_cat)
225 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800226
Harald Welted788f662010-03-26 09:45:03 +0800227 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800228}
229
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100230static const char* category_name(int subsys)
231{
232 if (subsys < osmo_log_info->num_cat)
233 return osmo_log_info->cat[subsys].name;
234
235 return NULL;
236}
237
Harald Welte3ae27582010-03-26 21:24:24 +0800238static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200239 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100240 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800241{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800242 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200243 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800244
245 /* are we using color */
246 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800247 const char *c = color(subsys);
248 if (c) {
Holger Hans Peter Freyther5f91a402014-12-05 10:29:45 +0100249 ret = snprintf(buf + offset, rem, "%s", c);
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200250 if (ret < 0)
251 goto err;
252 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800253 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800254 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800255 if (!cont) {
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100256 if (target->print_ext_timestamp) {
257 struct tm tm;
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100258 struct timeval tv;
259 gettimeofday(&tv, NULL);
260 localtime_r(&tv.tv_sec, &tm);
261 ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ",
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100262 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100263 tm.tm_hour, tm.tm_min, tm.tm_sec,
264 (int)(tv.tv_usec / 1000));
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100265 if (ret < 0)
266 goto err;
267 OSMO_SNPRINTF_RET(ret, rem, offset, len);
268 } else if (target->print_timestamp) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800269 char *timestr;
270 time_t tm;
271 tm = time(NULL);
272 timestr = ctime(&tm);
273 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200274 ret = snprintf(buf + offset, rem, "%s ", timestr);
275 if (ret < 0)
276 goto err;
277 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800278 }
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100279 if (target->print_category) {
280 ret = snprintf(buf + offset, rem, "%s ", category_name(subsys));
281 if (ret < 0)
282 goto err;
283 OSMO_SNPRINTF_RET(ret, rem, offset, len);
284 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200285 if (target->print_filename) {
286 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
287 subsys, file, line);
288 if (ret < 0)
289 goto err;
290 OSMO_SNPRINTF_RET(ret, rem, offset, len);
291 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800292 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200293 ret = vsnprintf(buf + offset, rem, format, ap);
294 if (ret < 0)
295 goto err;
296 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800297
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200298 ret = snprintf(buf + offset, rem, "%s",
299 target->use_color ? "\033[0;m" : "");
300 if (ret < 0)
301 goto err;
302 OSMO_SNPRINTF_RET(ret, rem, offset, len);
303err:
304 buf[sizeof(buf)-1] = '\0';
305 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800306}
307
Harald Welte36c5a3e2011-08-27 14:33:19 +0200308/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200309void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200310 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800311{
Harald Welte3ae27582010-03-26 21:24:24 +0800312 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800313
Harald Welteb43bc042011-06-27 10:29:17 +0200314 if (subsys < 0)
315 subsys = subsys_lib2index(subsys);
316
317 if (subsys > osmo_log_info->num_cat)
318 subsys = DLGLOBAL;
319
Harald Welte28222962011-02-18 20:37:04 +0100320 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800321 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800322 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200323 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800324
325 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800326 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800327 if (!category->enabled)
328 continue;
329
330 /* Check the global log level */
331 if (tar->loglevel != 0 && level < tar->loglevel)
332 continue;
333
334 /* Check the category log level */
335 if (tar->loglevel == 0 && category->loglevel != 0 &&
336 level < category->loglevel)
337 continue;
338
339 /* Apply filters here... if that becomes messy we will
340 * need to put filters in a list and each filter will
341 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800342 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800343 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200344 else if (osmo_log_info->filter_fn)
345 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800346 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200347 if (!output)
348 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800349
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200350 /* According to the manpage, vsnprintf leaves the value of ap
351 * in undefined state. Since _output uses vsnprintf and it may
352 * be called several times, we have to pass a copy of ap. */
353 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200354 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200355 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800356 }
357}
358
Harald Weltede6e4982012-12-06 21:25:27 +0100359/*! \brief logging function used by DEBUGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200360void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800361 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800362{
363 va_list ap;
364
365 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200366 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800367 va_end(ap);
368}
369
Harald Weltede6e4982012-12-06 21:25:27 +0100370/*! \brief logging function used by LOGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200371void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800372{
373 va_list ap;
374
375 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200376 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800377 va_end(ap);
378}
379
Harald Welte18fc4652011-08-17 14:14:17 +0200380/*! \brief Register a new log target with the logging core
381 * \param[in] target Log target to be registered
382 */
Harald Welte3ae27582010-03-26 21:24:24 +0800383void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800384{
Harald Welte28222962011-02-18 20:37:04 +0100385 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800386}
387
Harald Welte18fc4652011-08-17 14:14:17 +0200388/*! \brief Unregister a log target from the logging core
389 * \param[in] target Log target to be unregistered
390 */
Harald Welte3ae27582010-03-26 21:24:24 +0800391void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800392{
393 llist_del(&target->entry);
394}
395
Harald Welte18fc4652011-08-17 14:14:17 +0200396/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800397void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800398{
Harald Welte3ae27582010-03-26 21:24:24 +0800399 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800400}
401
Harald Welte18fc4652011-08-17 14:14:17 +0200402/*! \brief Set the logging context
403 * \param[in] ctx_nr logging context number
404 * \param[in] value value to which the context is to be set
405 *
406 * A logging context is something like the subscriber identity to which
407 * the currently processed message relates, or the BTS through which it
408 * was received. As soon as this data is known, it can be set using
409 * this function. The main use of context information is for logging
410 * filters.
411 */
Harald Welte3ae27582010-03-26 21:24:24 +0800412int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800413{
Harald Welte3ae27582010-03-26 21:24:24 +0800414 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800415 return -EINVAL;
416
Harald Welte3ae27582010-03-26 21:24:24 +0800417 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800418
419 return 0;
420}
421
Harald Welte18fc4652011-08-17 14:14:17 +0200422/*! \brief Enable the \ref LOG_FILTER_ALL log filter
423 * \param[in] target Log target to be affected
424 * \param[in] all enable (1) or disable (0) the ALL filter
425 *
426 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
427 * be printed. It acts as a wildcard. Setting it to \a 1 means there
428 * is no filtering.
429 */
Harald Welte3ae27582010-03-26 21:24:24 +0800430void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800431{
432 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800433 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800434 else
Harald Welte3ae27582010-03-26 21:24:24 +0800435 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800436}
437
Harald Welte18fc4652011-08-17 14:14:17 +0200438/*! \brief Enable or disable the use of colored output
439 * \param[in] target Log target to be affected
440 * \param[in] use_color Use color (1) or don't use color (0)
441 */
Harald Welte3ae27582010-03-26 21:24:24 +0800442void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800443{
444 target->use_color = use_color;
445}
446
Harald Welte18fc4652011-08-17 14:14:17 +0200447/*! \brief Enable or disable printing of timestamps while logging
448 * \param[in] target Log target to be affected
449 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
450 */
Harald Welte3ae27582010-03-26 21:24:24 +0800451void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800452{
453 target->print_timestamp = print_timestamp;
454}
455
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100456/*! \brief Enable or disable printing of extended timestamps while logging
457 * \param[in] target Log target to be affected
458 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
459 *
460 * When both timestamp and extended timestamp is enabled then only
461 * the extended timestamp will be used. The format of the timestamp
462 * is YYYYMMDDhhmmssnnn.
463 */
464void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp)
465{
466 target->print_ext_timestamp = print_timestamp;
467}
468
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200469/*! \brief Enable or disable printing of the filename while logging
470 * \param[in] target Log target to be affected
471 * \param[in] print_filename Enable (1) or disable (0) filenames
472 */
473void log_set_print_filename(struct log_target *target, int print_filename)
474{
475 target->print_filename = print_filename;
476}
477
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100478/*! \brief Enable or disable printing of the category name
479 * \param[in] target Log target to be affected
480 * \param[in] print_catname Enable (1) or disable (0) filenames
481 *
482 * Print the category/subsys name in front of every log message.
483 */
484void log_set_print_category(struct log_target *target, int print_category)
485{
486 target->print_category = print_category;
487}
488
Harald Welte18fc4652011-08-17 14:14:17 +0200489/*! \brief Set the global log level for a given log target
490 * \param[in] target Log target to be affected
491 * \param[in] log_level New global log level
492 */
Harald Welte3ae27582010-03-26 21:24:24 +0800493void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800494{
495 target->loglevel = log_level;
496}
497
Harald Weltede6e4982012-12-06 21:25:27 +0100498/*! \brief Set a category filter on a given log target
499 * \param[in] target Log target to be affected
500 * \param[in] category Log category to be affected
501 * \param[in] enable whether to enable or disable the filter
502 * \param[in] level Log level of the filter
503 */
Harald Welte3ae27582010-03-26 21:24:24 +0800504void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800505 int enable, int level)
506{
Harald Welte4ebdf742010-05-19 19:54:00 +0200507 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800508 return;
509 target->categories[category].enabled = !!enable;
510 target->categories[category].loglevel = level;
511}
512
Harald Welte76e72ab2011-02-17 15:52:39 +0100513static void _file_output(struct log_target *target, unsigned int level,
514 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800515{
Harald Welte0083cd32010-08-25 14:55:44 +0200516 fprintf(target->tgt_file.out, "%s", log);
517 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800518}
519
Harald Welte18fc4652011-08-17 14:14:17 +0200520/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800521struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800522{
Harald Welte3ae27582010-03-26 21:24:24 +0800523 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800524 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800525
Harald Welte3ae27582010-03-26 21:24:24 +0800526 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800527 if (!target)
528 return NULL;
529
Harald Welteb43bc042011-06-27 10:29:17 +0200530 target->categories = talloc_zero_array(target,
531 struct log_category,
532 osmo_log_info->num_cat);
533 if (!target->categories) {
534 talloc_free(target);
535 return NULL;
536 }
537
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800538 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800539
540 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200541 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800542 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200543 cat->enabled = osmo_log_info->cat[i].enabled;
544 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800545 }
546
547 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800548 target->use_color = 1;
549 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200550 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800551
552 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800553 target->loglevel = 0;
554 return target;
555}
556
Harald Welte18fc4652011-08-17 14:14:17 +0200557/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800558struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800559{
Harald Weltea3b844c2010-03-27 00:04:40 +0800560/* since C89/C99 says stderr is a macro, we can safely do this! */
561#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800562 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800563
Harald Welte3ae27582010-03-26 21:24:24 +0800564 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800565 if (!target)
566 return NULL;
567
Harald Welte28222962011-02-18 20:37:04 +0100568 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200569 target->tgt_file.out = stderr;
570 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800571 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800572#else
573 return NULL;
574#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800575}
576
Harald Welte18fc4652011-08-17 14:14:17 +0200577/*! \brief Create a new file-based log target
578 * \param[in] fname File name of the new log file
579 * \returns Log target in case of success, NULL otherwise
580 */
Harald Welte3086c392010-08-25 19:10:50 +0200581struct log_target *log_target_create_file(const char *fname)
582{
583 struct log_target *target;
584
585 target = log_target_create();
586 if (!target)
587 return NULL;
588
Harald Welte28222962011-02-18 20:37:04 +0100589 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200590 target->tgt_file.out = fopen(fname, "a");
591 if (!target->tgt_file.out)
592 return NULL;
593
594 target->output = _file_output;
595
596 target->tgt_file.fname = talloc_strdup(target, fname);
597
598 return target;
599}
600
Harald Welte18fc4652011-08-17 14:14:17 +0200601/*! \brief Find a registered log target
602 * \param[in] type Log target type
603 * \param[in] fname File name
604 * \returns Log target (if found), NULL otherwise
605 */
Harald Welte28222962011-02-18 20:37:04 +0100606struct log_target *log_target_find(int type, const char *fname)
607{
608 struct log_target *tgt;
609
610 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
611 if (tgt->type != type)
612 continue;
613 if (tgt->type == LOG_TGT_TYPE_FILE) {
614 if (!strcmp(fname, tgt->tgt_file.fname))
615 return tgt;
616 } else
617 return tgt;
618 }
619 return NULL;
620}
621
Harald Welte18fc4652011-08-17 14:14:17 +0200622/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200623void log_target_destroy(struct log_target *target)
624{
625
626 /* just in case, to make sure we don't have any references */
627 log_del_target(target);
628
629 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200630/* since C89/C99 says stderr is a macro, we can safely do this! */
631#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200632 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200633 if (target->tgt_file.out != stderr)
634#endif
635 {
Harald Welte3086c392010-08-25 19:10:50 +0200636 fclose(target->tgt_file.out);
637 target->tgt_file.out = NULL;
638 }
639 }
640
641 talloc_free(target);
642}
643
Harald Welte18fc4652011-08-17 14:14:17 +0200644/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200645int log_target_file_reopen(struct log_target *target)
646{
647 fclose(target->tgt_file.out);
648
649 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
650 if (!target->tgt_file.out)
651 return -errno;
652
653 /* we assume target->output already to be set */
654
655 return 0;
656}
657
Harald Welte4de854d2013-03-18 19:01:40 +0100658/*! \brief close and re-open a log file (for log file rotation) */
659int log_targets_reopen(void)
660{
661 struct log_target *tar;
662 int rc = 0;
663
664 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
665 switch (tar->type) {
666 case LOG_TGT_TYPE_FILE:
667 if (log_target_file_reopen(tar) < 0)
668 rc = -1;
669 break;
670 default:
671 break;
672 }
673 }
674
675 return rc;
676}
677
Harald Welte18fc4652011-08-17 14:14:17 +0200678/*! \brief Generates the logging command string for VTY
679 * \param[in] unused_info Deprecated parameter, no longer used!
680 */
Harald Weltece9fec32011-06-27 14:19:16 +0200681const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200682{
Harald Weltece9fec32011-06-27 14:19:16 +0200683 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100684 int len = 0, offset = 0, ret, i, rem;
685 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200686 char *str;
687
Harald Welteb43bc042011-06-27 10:29:17 +0200688 for (i = 0; i < info->num_cat; i++) {
689 if (info->cat[i].name == NULL)
690 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100691 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200692 }
Harald Welte7638af92010-05-11 16:39:22 +0200693
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100694 for (i = 0; i < LOGLEVEL_DEFS; i++)
695 size += strlen(loglevel_strs[i].str) + 1;
696
697 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200698 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200699 if (!str)
700 return NULL;
701
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200702 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100703 if (ret < 0)
704 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200705 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200706
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100707 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200708 if (info->cat[i].name) {
709 int j, name_len = strlen(info->cat[i].name)+1;
710 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100711
Harald Welteb43bc042011-06-27 10:29:17 +0200712 for (j = 0; j < name_len; j++)
713 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100714
Harald Welteb43bc042011-06-27 10:29:17 +0200715 name[name_len-1] = '\0';
716 ret = snprintf(str + offset, rem, "%s|", name+1);
717 if (ret < 0)
718 goto err;
719 OSMO_SNPRINTF_RET(ret, rem, offset, len);
720 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100721 }
722 offset--; /* to remove the trailing | */
723 rem++;
724
725 ret = snprintf(str + offset, rem, ") (");
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
730 for (i = 0; i < LOGLEVEL_DEFS; i++) {
731 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
732 char loglevel_str[loglevel_str_len];
733
734 for (j = 0; j < loglevel_str_len; j++)
735 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
736
737 loglevel_str[loglevel_str_len-1] = '\0';
738 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
739 if (ret < 0)
740 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200741 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100742 }
743 offset--; /* to remove the trailing | */
744 rem++;
745
746 ret = snprintf(str + offset, rem, ")");
747 if (ret < 0)
748 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200749 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100750err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200751 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100752 return str;
753}
754
Harald Welte18fc4652011-08-17 14:14:17 +0200755/*! \brief Generates the logging command description for VTY
756 * \param[in] unused_info Deprecated parameter, no longer used!
757 */
Harald Weltece9fec32011-06-27 14:19:16 +0200758const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100759{
Harald Weltece9fec32011-06-27 14:19:16 +0200760 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100761 char *str;
762 int i, ret, len = 0, offset = 0, rem;
763 unsigned int size =
764 strlen(LOGGING_STR
765 "Set the log level for a specified category\n") + 1;
766
Harald Welteb43bc042011-06-27 10:29:17 +0200767 for (i = 0; i < info->num_cat; i++) {
768 if (info->cat[i].name == NULL)
769 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100770 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200771 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100772
773 for (i = 0; i < LOGLEVEL_DEFS; i++)
774 size += strlen(loglevel_descriptions[i]) + 1;
775
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200776 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100777 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200778 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100779 if (!str)
780 return NULL;
781
782 ret = snprintf(str + offset, rem, LOGGING_STR
783 "Set the log level for a specified category\n");
784 if (ret < 0)
785 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200786 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100787
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200788 ret = snprintf(str + offset, rem,
789 "Global setting for all subsystems\n");
790 if (ret < 0)
791 goto err;
792 OSMO_SNPRINTF_RET(ret, rem, offset, len);
793
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100794 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200795 if (info->cat[i].name == NULL)
796 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100797 ret = snprintf(str + offset, rem, "%s\n",
798 info->cat[i].description);
799 if (ret < 0)
800 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200801 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100802 }
803 for (i = 0; i < LOGLEVEL_DEFS; i++) {
804 ret = snprintf(str + offset, rem, "%s\n",
805 loglevel_descriptions[i]);
806 if (ret < 0)
807 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200808 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100809 }
810err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200811 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200812 return str;
813}
814
Harald Welte18fc4652011-08-17 14:14:17 +0200815/*! \brief Initialize the Osmocom logging core
816 * \param[in] inf Information regarding logging categories
817 * \param[in] ctx \ref talloc context for logging allocations
818 * \returns 0 in case of success, negative in case of error
819 */
Harald Welteb43bc042011-06-27 10:29:17 +0200820int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800821{
Harald Welteb43bc042011-06-27 10:29:17 +0200822 int i;
823
824 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
825 if (!tall_log_ctx)
826 return -ENOMEM;
827
828 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
829 if (!osmo_log_info)
830 return -ENOMEM;
831
Holger Hans Peter Freytherb7d0f462013-12-29 19:38:01 +0100832 osmo_log_info->filter_fn = inf->filter_fn;
Harald Welteb43bc042011-06-27 10:29:17 +0200833 osmo_log_info->num_cat_user = inf->num_cat;
834 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200835 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200836
837 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
838 struct log_info_cat,
839 osmo_log_info->num_cat);
840 if (!osmo_log_info->cat) {
841 talloc_free(osmo_log_info);
842 osmo_log_info = NULL;
843 return -ENOMEM;
844 }
845
846 /* copy over the user part */
847 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200848 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
849 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200850 sizeof(struct log_info_cat));
851 }
852
853 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200854 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200855 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200856 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200857 &internal_cat[i], sizeof(struct log_info_cat));
858 }
859
860 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800861}
Harald Welte18fc4652011-08-17 14:14:17 +0200862
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200863/*! @} */