blob: f58265f76f44de0f5faf271e7f6661cd7e14d738 [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 Welte4a2bb9e2010-03-26 09:33:40 +080058 { 0, "EVERYTHING" },
59 { 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
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100111/* You have to keep this in sync with the structure loglevel_strs. */
112const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
113 "Log simply everything",
114 "Log debug messages and higher levels",
115 "Log informational messages and higher levels",
116 "Log noticable messages and higher levels",
117 "Log error messages and higher levels",
118 "Log only fatal messages",
119 NULL,
120};
121
Harald Welteb43bc042011-06-27 10:29:17 +0200122/* special magic for negative (library-internal) log subsystem numbers */
123static int subsys_lib2index(int subsys)
124{
125 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
126}
127
Harald Welte18fc4652011-08-17 14:14:17 +0200128/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800129int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800130{
131 return get_string_value(loglevel_strs, lvl);
132}
133
Harald Welte18fc4652011-08-17 14:14:17 +0200134/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200135const char *log_level_str(unsigned int lvl)
136{
137 return get_value_string(loglevel_strs, lvl);
138}
139
Harald Welte18fc4652011-08-17 14:14:17 +0200140/*! \brief parse a human-readable log category into numeric form
141 * \param[in] category human-readable log category name
142 * \returns numeric category value, or -EINVAL otherwise
143 */
Harald Welte3ae27582010-03-26 21:24:24 +0800144int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800145{
146 int i;
147
Harald Welte4ebdf742010-05-19 19:54:00 +0200148 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200149 if (osmo_log_info->cat[i].name == NULL)
150 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200151 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800152 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800153 }
154
155 return -EINVAL;
156}
157
Harald Welte18fc4652011-08-17 14:14:17 +0200158/*! \brief parse the log category mask
159 * \param[in] target log target to be configured
160 * \param[in] _mask log category mask string
161 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800162 * The format can be this: category1:category2:category3
163 * or category1,2:category2,3:...
164 */
Harald Welte3ae27582010-03-26 21:24:24 +0800165void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800166{
167 int i = 0;
168 char *mask = strdup(_mask);
169 char *category_token = NULL;
170
171 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200172 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800173 target->categories[i].enabled = 0;
174
175 category_token = strtok(mask, ":");
176 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200177 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200178 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800179 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200180
181 if (!osmo_log_info->cat[i].name)
182 continue;
183
184 length = strlen(category_token);
185 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200186
187 /* Use longest length not to match subocurrences. */
188 if (cat_length > length)
189 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800190
191 if (colon)
192 length = colon - category_token;
193
Harald Welte4ebdf742010-05-19 19:54:00 +0200194 if (strncasecmp(osmo_log_info->cat[i].name,
195 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800196 int level = 0;
197
198 if (colon)
199 level = atoi(colon+1);
200
Harald Weltefaadfe22010-03-26 21:05:43 +0800201 target->categories[i].enabled = 1;
202 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800203 }
204 }
205 } while ((category_token = strtok(NULL, ":")));
206
207 free(mask);
208}
209
210static const char* color(int subsys)
211{
Harald Welte4ebdf742010-05-19 19:54:00 +0200212 if (subsys < osmo_log_info->num_cat)
213 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800214
Harald Welted788f662010-03-26 09:45:03 +0800215 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800216}
217
Harald Welte3ae27582010-03-26 21:24:24 +0800218static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200219 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100220 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800221{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800222 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200223 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800224
225 /* are we using color */
226 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800227 const char *c = color(subsys);
228 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200229 ret = snprintf(buf + offset, rem, "%s", color(subsys));
230 if (ret < 0)
231 goto err;
232 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800233 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800234 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800235 if (!cont) {
236 if (target->print_timestamp) {
237 char *timestr;
238 time_t tm;
239 tm = time(NULL);
240 timestr = ctime(&tm);
241 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200242 ret = snprintf(buf + offset, rem, "%s ", timestr);
243 if (ret < 0)
244 goto err;
245 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800246 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200247 if (target->print_filename) {
248 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
249 subsys, file, line);
250 if (ret < 0)
251 goto err;
252 OSMO_SNPRINTF_RET(ret, rem, offset, len);
253 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800254 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200255 ret = vsnprintf(buf + offset, rem, format, ap);
256 if (ret < 0)
257 goto err;
258 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800259
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200260 ret = snprintf(buf + offset, rem, "%s",
261 target->use_color ? "\033[0;m" : "");
262 if (ret < 0)
263 goto err;
264 OSMO_SNPRINTF_RET(ret, rem, offset, len);
265err:
266 buf[sizeof(buf)-1] = '\0';
267 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800268}
269
Harald Welte36c5a3e2011-08-27 14:33:19 +0200270/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200271void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200272 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800273{
Harald Welte3ae27582010-03-26 21:24:24 +0800274 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800275
Harald Welteb43bc042011-06-27 10:29:17 +0200276 if (subsys < 0)
277 subsys = subsys_lib2index(subsys);
278
279 if (subsys > osmo_log_info->num_cat)
280 subsys = DLGLOBAL;
281
Harald Welte28222962011-02-18 20:37:04 +0100282 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800283 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800284 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200285 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800286
287 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800288 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800289 if (!category->enabled)
290 continue;
291
292 /* Check the global log level */
293 if (tar->loglevel != 0 && level < tar->loglevel)
294 continue;
295
296 /* Check the category log level */
297 if (tar->loglevel == 0 && category->loglevel != 0 &&
298 level < category->loglevel)
299 continue;
300
301 /* Apply filters here... if that becomes messy we will
302 * need to put filters in a list and each filter will
303 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800304 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800305 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200306 else if (osmo_log_info->filter_fn)
307 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800308 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200309 if (!output)
310 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800311
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200312 /* According to the manpage, vsnprintf leaves the value of ap
313 * in undefined state. Since _output uses vsnprintf and it may
314 * be called several times, we have to pass a copy of ap. */
315 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200316 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200317 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800318 }
319}
320
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200321void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800322 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800323{
324 va_list ap;
325
326 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200327 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800328 va_end(ap);
329}
330
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200331void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800332{
333 va_list ap;
334
335 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200336 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800337 va_end(ap);
338}
339
Harald Welte18fc4652011-08-17 14:14:17 +0200340/*! \brief Register a new log target with the logging core
341 * \param[in] target Log target to be registered
342 */
Harald Welte3ae27582010-03-26 21:24:24 +0800343void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800344{
Harald Welte28222962011-02-18 20:37:04 +0100345 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800346}
347
Harald Welte18fc4652011-08-17 14:14:17 +0200348/*! \brief Unregister a log target from the logging core
349 * \param[in] target Log target to be unregistered
350 */
Harald Welte3ae27582010-03-26 21:24:24 +0800351void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800352{
353 llist_del(&target->entry);
354}
355
Harald Welte18fc4652011-08-17 14:14:17 +0200356/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800357void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800358{
Harald Welte3ae27582010-03-26 21:24:24 +0800359 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800360}
361
Harald Welte18fc4652011-08-17 14:14:17 +0200362/*! \brief Set the logging context
363 * \param[in] ctx_nr logging context number
364 * \param[in] value value to which the context is to be set
365 *
366 * A logging context is something like the subscriber identity to which
367 * the currently processed message relates, or the BTS through which it
368 * was received. As soon as this data is known, it can be set using
369 * this function. The main use of context information is for logging
370 * filters.
371 */
Harald Welte3ae27582010-03-26 21:24:24 +0800372int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800373{
Harald Welte3ae27582010-03-26 21:24:24 +0800374 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800375 return -EINVAL;
376
Harald Welte3ae27582010-03-26 21:24:24 +0800377 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800378
379 return 0;
380}
381
Harald Welte18fc4652011-08-17 14:14:17 +0200382/*! \brief Enable the \ref LOG_FILTER_ALL log filter
383 * \param[in] target Log target to be affected
384 * \param[in] all enable (1) or disable (0) the ALL filter
385 *
386 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
387 * be printed. It acts as a wildcard. Setting it to \a 1 means there
388 * is no filtering.
389 */
Harald Welte3ae27582010-03-26 21:24:24 +0800390void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800391{
392 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800393 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800394 else
Harald Welte3ae27582010-03-26 21:24:24 +0800395 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800396}
397
Harald Welte18fc4652011-08-17 14:14:17 +0200398/*! \brief Enable or disable the use of colored output
399 * \param[in] target Log target to be affected
400 * \param[in] use_color Use color (1) or don't use color (0)
401 */
Harald Welte3ae27582010-03-26 21:24:24 +0800402void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800403{
404 target->use_color = use_color;
405}
406
Harald Welte18fc4652011-08-17 14:14:17 +0200407/*! \brief Enable or disable printing of timestamps while logging
408 * \param[in] target Log target to be affected
409 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
410 */
Harald Welte3ae27582010-03-26 21:24:24 +0800411void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800412{
413 target->print_timestamp = print_timestamp;
414}
415
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200416/*! \brief Enable or disable printing of the filename while logging
417 * \param[in] target Log target to be affected
418 * \param[in] print_filename Enable (1) or disable (0) filenames
419 */
420void log_set_print_filename(struct log_target *target, int print_filename)
421{
422 target->print_filename = print_filename;
423}
424
Harald Welte18fc4652011-08-17 14:14:17 +0200425/*! \brief Set the global log level for a given log target
426 * \param[in] target Log target to be affected
427 * \param[in] log_level New global log level
428 */
Harald Welte3ae27582010-03-26 21:24:24 +0800429void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800430{
431 target->loglevel = log_level;
432}
433
Harald Welte3ae27582010-03-26 21:24:24 +0800434void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800435 int enable, int level)
436{
Harald Welte4ebdf742010-05-19 19:54:00 +0200437 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800438 return;
439 target->categories[category].enabled = !!enable;
440 target->categories[category].loglevel = level;
441}
442
Harald Welte76e72ab2011-02-17 15:52:39 +0100443static void _file_output(struct log_target *target, unsigned int level,
444 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800445{
Harald Welte0083cd32010-08-25 14:55:44 +0200446 fprintf(target->tgt_file.out, "%s", log);
447 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800448}
449
Harald Welte18fc4652011-08-17 14:14:17 +0200450/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800451struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800452{
Harald Welte3ae27582010-03-26 21:24:24 +0800453 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800454 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800455
Harald Welte3ae27582010-03-26 21:24:24 +0800456 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800457 if (!target)
458 return NULL;
459
Harald Welteb43bc042011-06-27 10:29:17 +0200460 target->categories = talloc_zero_array(target,
461 struct log_category,
462 osmo_log_info->num_cat);
463 if (!target->categories) {
464 talloc_free(target);
465 return NULL;
466 }
467
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800468 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800469
470 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200471 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800472 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200473 cat->enabled = osmo_log_info->cat[i].enabled;
474 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800475 }
476
477 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800478 target->use_color = 1;
479 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200480 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800481
482 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800483 target->loglevel = 0;
484 return target;
485}
486
Harald Welte18fc4652011-08-17 14:14:17 +0200487/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800488struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800489{
Harald Weltea3b844c2010-03-27 00:04:40 +0800490/* since C89/C99 says stderr is a macro, we can safely do this! */
491#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800492 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800493
Harald Welte3ae27582010-03-26 21:24:24 +0800494 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800495 if (!target)
496 return NULL;
497
Harald Welte28222962011-02-18 20:37:04 +0100498 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200499 target->tgt_file.out = stderr;
500 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800501 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800502#else
503 return NULL;
504#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800505}
506
Harald Welte18fc4652011-08-17 14:14:17 +0200507/*! \brief Create a new file-based log target
508 * \param[in] fname File name of the new log file
509 * \returns Log target in case of success, NULL otherwise
510 */
Harald Welte3086c392010-08-25 19:10:50 +0200511struct log_target *log_target_create_file(const char *fname)
512{
513 struct log_target *target;
514
515 target = log_target_create();
516 if (!target)
517 return NULL;
518
Harald Welte28222962011-02-18 20:37:04 +0100519 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200520 target->tgt_file.out = fopen(fname, "a");
521 if (!target->tgt_file.out)
522 return NULL;
523
524 target->output = _file_output;
525
526 target->tgt_file.fname = talloc_strdup(target, fname);
527
528 return target;
529}
530
Harald Welte18fc4652011-08-17 14:14:17 +0200531/*! \brief Find a registered log target
532 * \param[in] type Log target type
533 * \param[in] fname File name
534 * \returns Log target (if found), NULL otherwise
535 */
Harald Welte28222962011-02-18 20:37:04 +0100536struct log_target *log_target_find(int type, const char *fname)
537{
538 struct log_target *tgt;
539
540 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
541 if (tgt->type != type)
542 continue;
543 if (tgt->type == LOG_TGT_TYPE_FILE) {
544 if (!strcmp(fname, tgt->tgt_file.fname))
545 return tgt;
546 } else
547 return tgt;
548 }
549 return NULL;
550}
551
Harald Welte18fc4652011-08-17 14:14:17 +0200552/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200553void log_target_destroy(struct log_target *target)
554{
555
556 /* just in case, to make sure we don't have any references */
557 log_del_target(target);
558
559 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200560/* since C89/C99 says stderr is a macro, we can safely do this! */
561#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200562 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200563 if (target->tgt_file.out != stderr)
564#endif
565 {
Harald Welte3086c392010-08-25 19:10:50 +0200566 fclose(target->tgt_file.out);
567 target->tgt_file.out = NULL;
568 }
569 }
570
571 talloc_free(target);
572}
573
Harald Welte18fc4652011-08-17 14:14:17 +0200574/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200575int log_target_file_reopen(struct log_target *target)
576{
577 fclose(target->tgt_file.out);
578
579 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
580 if (!target->tgt_file.out)
581 return -errno;
582
583 /* we assume target->output already to be set */
584
585 return 0;
586}
587
Harald Welte18fc4652011-08-17 14:14:17 +0200588/*! \brief Generates the logging command string for VTY
589 * \param[in] unused_info Deprecated parameter, no longer used!
590 */
Harald Weltece9fec32011-06-27 14:19:16 +0200591const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200592{
Harald Weltece9fec32011-06-27 14:19:16 +0200593 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100594 int len = 0, offset = 0, ret, i, rem;
595 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200596 char *str;
597
Harald Welteb43bc042011-06-27 10:29:17 +0200598 for (i = 0; i < info->num_cat; i++) {
599 if (info->cat[i].name == NULL)
600 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100601 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200602 }
Harald Welte7638af92010-05-11 16:39:22 +0200603
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100604 for (i = 0; i < LOGLEVEL_DEFS; i++)
605 size += strlen(loglevel_strs[i].str) + 1;
606
607 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200608 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200609 if (!str)
610 return NULL;
611
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200612 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100613 if (ret < 0)
614 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200615 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200616
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100617 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200618 if (info->cat[i].name) {
619 int j, name_len = strlen(info->cat[i].name)+1;
620 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100621
Harald Welteb43bc042011-06-27 10:29:17 +0200622 for (j = 0; j < name_len; j++)
623 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100624
Harald Welteb43bc042011-06-27 10:29:17 +0200625 name[name_len-1] = '\0';
626 ret = snprintf(str + offset, rem, "%s|", name+1);
627 if (ret < 0)
628 goto err;
629 OSMO_SNPRINTF_RET(ret, rem, offset, len);
630 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100631 }
632 offset--; /* to remove the trailing | */
633 rem++;
634
635 ret = snprintf(str + offset, rem, ") (");
636 if (ret < 0)
637 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200638 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100639
640 for (i = 0; i < LOGLEVEL_DEFS; i++) {
641 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
642 char loglevel_str[loglevel_str_len];
643
644 for (j = 0; j < loglevel_str_len; j++)
645 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
646
647 loglevel_str[loglevel_str_len-1] = '\0';
648 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
649 if (ret < 0)
650 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200651 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100652 }
653 offset--; /* to remove the trailing | */
654 rem++;
655
656 ret = snprintf(str + offset, rem, ")");
657 if (ret < 0)
658 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200659 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100660err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200661 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100662 return str;
663}
664
Harald Welte18fc4652011-08-17 14:14:17 +0200665/*! \brief Generates the logging command description for VTY
666 * \param[in] unused_info Deprecated parameter, no longer used!
667 */
Harald Weltece9fec32011-06-27 14:19:16 +0200668const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100669{
Harald Weltece9fec32011-06-27 14:19:16 +0200670 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100671 char *str;
672 int i, ret, len = 0, offset = 0, rem;
673 unsigned int size =
674 strlen(LOGGING_STR
675 "Set the log level for a specified category\n") + 1;
676
Harald Welteb43bc042011-06-27 10:29:17 +0200677 for (i = 0; i < info->num_cat; i++) {
678 if (info->cat[i].name == NULL)
679 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100680 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200681 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100682
683 for (i = 0; i < LOGLEVEL_DEFS; i++)
684 size += strlen(loglevel_descriptions[i]) + 1;
685
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200686 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100687 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200688 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100689 if (!str)
690 return NULL;
691
692 ret = snprintf(str + offset, rem, LOGGING_STR
693 "Set the log level for a specified category\n");
694 if (ret < 0)
695 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200696 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100697
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200698 ret = snprintf(str + offset, rem,
699 "Global setting for all subsystems\n");
700 if (ret < 0)
701 goto err;
702 OSMO_SNPRINTF_RET(ret, rem, offset, len);
703
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100704 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200705 if (info->cat[i].name == NULL)
706 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100707 ret = snprintf(str + offset, rem, "%s\n",
708 info->cat[i].description);
709 if (ret < 0)
710 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200711 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100712 }
713 for (i = 0; i < LOGLEVEL_DEFS; i++) {
714 ret = snprintf(str + offset, rem, "%s\n",
715 loglevel_descriptions[i]);
716 if (ret < 0)
717 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200718 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100719 }
720err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200721 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200722 return str;
723}
724
Harald Welte18fc4652011-08-17 14:14:17 +0200725/*! \brief Initialize the Osmocom logging core
726 * \param[in] inf Information regarding logging categories
727 * \param[in] ctx \ref talloc context for logging allocations
728 * \returns 0 in case of success, negative in case of error
729 */
Harald Welteb43bc042011-06-27 10:29:17 +0200730int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800731{
Harald Welteb43bc042011-06-27 10:29:17 +0200732 int i;
733
734 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
735 if (!tall_log_ctx)
736 return -ENOMEM;
737
738 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
739 if (!osmo_log_info)
740 return -ENOMEM;
741
742 osmo_log_info->num_cat_user = inf->num_cat;
743 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200744 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200745
746 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
747 struct log_info_cat,
748 osmo_log_info->num_cat);
749 if (!osmo_log_info->cat) {
750 talloc_free(osmo_log_info);
751 osmo_log_info = NULL;
752 return -ENOMEM;
753 }
754
755 /* copy over the user part */
756 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200757 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
758 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200759 sizeof(struct log_info_cat));
760 }
761
762 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200763 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200764 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200765 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200766 &internal_cat[i], sizeof(struct log_info_cat));
767 }
768
769 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800770}
Harald Welte18fc4652011-08-17 14:14:17 +0200771
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200772/*! @} */