blob: 0816570e920b2ac7c64cc124c6263da5cf24336b [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) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800178 char* colon = strstr(category_token, ",");
179 int length = strlen(category_token);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200180 int cat_length = strlen(osmo_log_info->cat[i].name);
181
182 /* Use longest length not to match subocurrences. */
183 if (cat_length > length)
184 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800185
Harald Welteb43bc042011-06-27 10:29:17 +0200186 if (!osmo_log_info->cat[i].name)
187 continue;
188
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800189 if (colon)
190 length = colon - category_token;
191
Harald Welte4ebdf742010-05-19 19:54:00 +0200192 if (strncasecmp(osmo_log_info->cat[i].name,
193 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800194 int level = 0;
195
196 if (colon)
197 level = atoi(colon+1);
198
Harald Weltefaadfe22010-03-26 21:05:43 +0800199 target->categories[i].enabled = 1;
200 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800201 }
202 }
203 } while ((category_token = strtok(NULL, ":")));
204
205 free(mask);
206}
207
208static const char* color(int subsys)
209{
Harald Welte4ebdf742010-05-19 19:54:00 +0200210 if (subsys < osmo_log_info->num_cat)
211 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800212
Harald Welted788f662010-03-26 09:45:03 +0800213 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800214}
215
Harald Welte3ae27582010-03-26 21:24:24 +0800216static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200217 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100218 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800219{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800220 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200221 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800222
223 /* are we using color */
224 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800225 const char *c = color(subsys);
226 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200227 ret = snprintf(buf + offset, rem, "%s", color(subsys));
228 if (ret < 0)
229 goto err;
230 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800231 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800232 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800233 if (!cont) {
234 if (target->print_timestamp) {
235 char *timestr;
236 time_t tm;
237 tm = time(NULL);
238 timestr = ctime(&tm);
239 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200240 ret = snprintf(buf + offset, rem, "%s ", timestr);
241 if (ret < 0)
242 goto err;
243 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800244 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200245 if (target->print_filename) {
246 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
247 subsys, file, line);
248 if (ret < 0)
249 goto err;
250 OSMO_SNPRINTF_RET(ret, rem, offset, len);
251 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800252 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200253 ret = vsnprintf(buf + offset, rem, format, ap);
254 if (ret < 0)
255 goto err;
256 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800257
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200258 ret = snprintf(buf + offset, rem, "%s",
259 target->use_color ? "\033[0;m" : "");
260 if (ret < 0)
261 goto err;
262 OSMO_SNPRINTF_RET(ret, rem, offset, len);
263err:
264 buf[sizeof(buf)-1] = '\0';
265 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800266}
267
Harald Welte36c5a3e2011-08-27 14:33:19 +0200268/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200269void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200270 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800271{
Harald Welte3ae27582010-03-26 21:24:24 +0800272 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800273
Harald Welteb43bc042011-06-27 10:29:17 +0200274 if (subsys < 0)
275 subsys = subsys_lib2index(subsys);
276
277 if (subsys > osmo_log_info->num_cat)
278 subsys = DLGLOBAL;
279
Harald Welte28222962011-02-18 20:37:04 +0100280 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800281 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800282 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200283 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800284
285 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800286 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800287 if (!category->enabled)
288 continue;
289
290 /* Check the global log level */
291 if (tar->loglevel != 0 && level < tar->loglevel)
292 continue;
293
294 /* Check the category log level */
295 if (tar->loglevel == 0 && category->loglevel != 0 &&
296 level < category->loglevel)
297 continue;
298
299 /* Apply filters here... if that becomes messy we will
300 * need to put filters in a list and each filter will
301 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800302 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800303 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200304 else if (osmo_log_info->filter_fn)
305 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800306 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200307 if (!output)
308 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800309
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200310 /* According to the manpage, vsnprintf leaves the value of ap
311 * in undefined state. Since _output uses vsnprintf and it may
312 * be called several times, we have to pass a copy of ap. */
313 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200314 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200315 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800316 }
317}
318
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200319void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800320 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800321{
322 va_list ap;
323
324 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200325 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800326 va_end(ap);
327}
328
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200329void logp2(int subsys, unsigned int level, const char *file, int line, int cont, 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, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800335 va_end(ap);
336}
337
Harald Welte18fc4652011-08-17 14:14:17 +0200338/*! \brief Register a new log target with the logging core
339 * \param[in] target Log target to be registered
340 */
Harald Welte3ae27582010-03-26 21:24:24 +0800341void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800342{
Harald Welte28222962011-02-18 20:37:04 +0100343 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800344}
345
Harald Welte18fc4652011-08-17 14:14:17 +0200346/*! \brief Unregister a log target from the logging core
347 * \param[in] target Log target to be unregistered
348 */
Harald Welte3ae27582010-03-26 21:24:24 +0800349void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800350{
351 llist_del(&target->entry);
352}
353
Harald Welte18fc4652011-08-17 14:14:17 +0200354/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800355void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800356{
Harald Welte3ae27582010-03-26 21:24:24 +0800357 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800358}
359
Harald Welte18fc4652011-08-17 14:14:17 +0200360/*! \brief Set the logging context
361 * \param[in] ctx_nr logging context number
362 * \param[in] value value to which the context is to be set
363 *
364 * A logging context is something like the subscriber identity to which
365 * the currently processed message relates, or the BTS through which it
366 * was received. As soon as this data is known, it can be set using
367 * this function. The main use of context information is for logging
368 * filters.
369 */
Harald Welte3ae27582010-03-26 21:24:24 +0800370int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800371{
Harald Welte3ae27582010-03-26 21:24:24 +0800372 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800373 return -EINVAL;
374
Harald Welte3ae27582010-03-26 21:24:24 +0800375 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800376
377 return 0;
378}
379
Harald Welte18fc4652011-08-17 14:14:17 +0200380/*! \brief Enable the \ref LOG_FILTER_ALL log filter
381 * \param[in] target Log target to be affected
382 * \param[in] all enable (1) or disable (0) the ALL filter
383 *
384 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
385 * be printed. It acts as a wildcard. Setting it to \a 1 means there
386 * is no filtering.
387 */
Harald Welte3ae27582010-03-26 21:24:24 +0800388void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800389{
390 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800391 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800392 else
Harald Welte3ae27582010-03-26 21:24:24 +0800393 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800394}
395
Harald Welte18fc4652011-08-17 14:14:17 +0200396/*! \brief Enable or disable the use of colored output
397 * \param[in] target Log target to be affected
398 * \param[in] use_color Use color (1) or don't use color (0)
399 */
Harald Welte3ae27582010-03-26 21:24:24 +0800400void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800401{
402 target->use_color = use_color;
403}
404
Harald Welte18fc4652011-08-17 14:14:17 +0200405/*! \brief Enable or disable printing of timestamps while logging
406 * \param[in] target Log target to be affected
407 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
408 */
Harald Welte3ae27582010-03-26 21:24:24 +0800409void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800410{
411 target->print_timestamp = print_timestamp;
412}
413
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200414/*! \brief Enable or disable printing of the filename while logging
415 * \param[in] target Log target to be affected
416 * \param[in] print_filename Enable (1) or disable (0) filenames
417 */
418void log_set_print_filename(struct log_target *target, int print_filename)
419{
420 target->print_filename = print_filename;
421}
422
Harald Welte18fc4652011-08-17 14:14:17 +0200423/*! \brief Set the global log level for a given log target
424 * \param[in] target Log target to be affected
425 * \param[in] log_level New global log level
426 */
Harald Welte3ae27582010-03-26 21:24:24 +0800427void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800428{
429 target->loglevel = log_level;
430}
431
Harald Welte3ae27582010-03-26 21:24:24 +0800432void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800433 int enable, int level)
434{
Harald Welte4ebdf742010-05-19 19:54:00 +0200435 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800436 return;
437 target->categories[category].enabled = !!enable;
438 target->categories[category].loglevel = level;
439}
440
Harald Welte76e72ab2011-02-17 15:52:39 +0100441static void _file_output(struct log_target *target, unsigned int level,
442 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800443{
Harald Welte0083cd32010-08-25 14:55:44 +0200444 fprintf(target->tgt_file.out, "%s", log);
445 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800446}
447
Harald Welte18fc4652011-08-17 14:14:17 +0200448/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800449struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800450{
Harald Welte3ae27582010-03-26 21:24:24 +0800451 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800452 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800453
Harald Welte3ae27582010-03-26 21:24:24 +0800454 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800455 if (!target)
456 return NULL;
457
Harald Welteb43bc042011-06-27 10:29:17 +0200458 target->categories = talloc_zero_array(target,
459 struct log_category,
460 osmo_log_info->num_cat);
461 if (!target->categories) {
462 talloc_free(target);
463 return NULL;
464 }
465
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800466 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800467
468 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200469 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800470 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200471 cat->enabled = osmo_log_info->cat[i].enabled;
472 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800473 }
474
475 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800476 target->use_color = 1;
477 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200478 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800479
480 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800481 target->loglevel = 0;
482 return target;
483}
484
Harald Welte18fc4652011-08-17 14:14:17 +0200485/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800486struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800487{
Harald Weltea3b844c2010-03-27 00:04:40 +0800488/* since C89/C99 says stderr is a macro, we can safely do this! */
489#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800490 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800491
Harald Welte3ae27582010-03-26 21:24:24 +0800492 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800493 if (!target)
494 return NULL;
495
Harald Welte28222962011-02-18 20:37:04 +0100496 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200497 target->tgt_file.out = stderr;
498 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800499 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800500#else
501 return NULL;
502#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800503}
504
Harald Welte18fc4652011-08-17 14:14:17 +0200505/*! \brief Create a new file-based log target
506 * \param[in] fname File name of the new log file
507 * \returns Log target in case of success, NULL otherwise
508 */
Harald Welte3086c392010-08-25 19:10:50 +0200509struct log_target *log_target_create_file(const char *fname)
510{
511 struct log_target *target;
512
513 target = log_target_create();
514 if (!target)
515 return NULL;
516
Harald Welte28222962011-02-18 20:37:04 +0100517 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200518 target->tgt_file.out = fopen(fname, "a");
519 if (!target->tgt_file.out)
520 return NULL;
521
522 target->output = _file_output;
523
524 target->tgt_file.fname = talloc_strdup(target, fname);
525
526 return target;
527}
528
Harald Welte18fc4652011-08-17 14:14:17 +0200529/*! \brief Find a registered log target
530 * \param[in] type Log target type
531 * \param[in] fname File name
532 * \returns Log target (if found), NULL otherwise
533 */
Harald Welte28222962011-02-18 20:37:04 +0100534struct log_target *log_target_find(int type, const char *fname)
535{
536 struct log_target *tgt;
537
538 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
539 if (tgt->type != type)
540 continue;
541 if (tgt->type == LOG_TGT_TYPE_FILE) {
542 if (!strcmp(fname, tgt->tgt_file.fname))
543 return tgt;
544 } else
545 return tgt;
546 }
547 return NULL;
548}
549
Harald Welte18fc4652011-08-17 14:14:17 +0200550/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200551void log_target_destroy(struct log_target *target)
552{
553
554 /* just in case, to make sure we don't have any references */
555 log_del_target(target);
556
557 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200558/* since C89/C99 says stderr is a macro, we can safely do this! */
559#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200560 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200561 if (target->tgt_file.out != stderr)
562#endif
563 {
Harald Welte3086c392010-08-25 19:10:50 +0200564 fclose(target->tgt_file.out);
565 target->tgt_file.out = NULL;
566 }
567 }
568
569 talloc_free(target);
570}
571
Harald Welte18fc4652011-08-17 14:14:17 +0200572/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200573int log_target_file_reopen(struct log_target *target)
574{
575 fclose(target->tgt_file.out);
576
577 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
578 if (!target->tgt_file.out)
579 return -errno;
580
581 /* we assume target->output already to be set */
582
583 return 0;
584}
585
Harald Welte18fc4652011-08-17 14:14:17 +0200586/*! \brief Generates the logging command string for VTY
587 * \param[in] unused_info Deprecated parameter, no longer used!
588 */
Harald Weltece9fec32011-06-27 14:19:16 +0200589const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200590{
Harald Weltece9fec32011-06-27 14:19:16 +0200591 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100592 int len = 0, offset = 0, ret, i, rem;
593 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200594 char *str;
595
Harald Welteb43bc042011-06-27 10:29:17 +0200596 for (i = 0; i < info->num_cat; i++) {
597 if (info->cat[i].name == NULL)
598 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100599 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200600 }
Harald Welte7638af92010-05-11 16:39:22 +0200601
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100602 for (i = 0; i < LOGLEVEL_DEFS; i++)
603 size += strlen(loglevel_strs[i].str) + 1;
604
605 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200606 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200607 if (!str)
608 return NULL;
609
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200610 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100611 if (ret < 0)
612 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200613 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200614
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100615 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200616 if (info->cat[i].name) {
617 int j, name_len = strlen(info->cat[i].name)+1;
618 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100619
Harald Welteb43bc042011-06-27 10:29:17 +0200620 for (j = 0; j < name_len; j++)
621 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100622
Harald Welteb43bc042011-06-27 10:29:17 +0200623 name[name_len-1] = '\0';
624 ret = snprintf(str + offset, rem, "%s|", name+1);
625 if (ret < 0)
626 goto err;
627 OSMO_SNPRINTF_RET(ret, rem, offset, len);
628 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100629 }
630 offset--; /* to remove the trailing | */
631 rem++;
632
633 ret = snprintf(str + offset, rem, ") (");
634 if (ret < 0)
635 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200636 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100637
638 for (i = 0; i < LOGLEVEL_DEFS; i++) {
639 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
640 char loglevel_str[loglevel_str_len];
641
642 for (j = 0; j < loglevel_str_len; j++)
643 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
644
645 loglevel_str[loglevel_str_len-1] = '\0';
646 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
647 if (ret < 0)
648 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200649 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100650 }
651 offset--; /* to remove the trailing | */
652 rem++;
653
654 ret = snprintf(str + offset, rem, ")");
655 if (ret < 0)
656 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200657 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100658err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200659 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100660 return str;
661}
662
Harald Welte18fc4652011-08-17 14:14:17 +0200663/*! \brief Generates the logging command description for VTY
664 * \param[in] unused_info Deprecated parameter, no longer used!
665 */
Harald Weltece9fec32011-06-27 14:19:16 +0200666const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100667{
Harald Weltece9fec32011-06-27 14:19:16 +0200668 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100669 char *str;
670 int i, ret, len = 0, offset = 0, rem;
671 unsigned int size =
672 strlen(LOGGING_STR
673 "Set the log level for a specified category\n") + 1;
674
Harald Welteb43bc042011-06-27 10:29:17 +0200675 for (i = 0; i < info->num_cat; i++) {
676 if (info->cat[i].name == NULL)
677 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100678 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200679 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100680
681 for (i = 0; i < LOGLEVEL_DEFS; i++)
682 size += strlen(loglevel_descriptions[i]) + 1;
683
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200684 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100685 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200686 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100687 if (!str)
688 return NULL;
689
690 ret = snprintf(str + offset, rem, LOGGING_STR
691 "Set the log level for a specified category\n");
692 if (ret < 0)
693 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200694 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100695
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200696 ret = snprintf(str + offset, rem,
697 "Global setting for all subsystems\n");
698 if (ret < 0)
699 goto err;
700 OSMO_SNPRINTF_RET(ret, rem, offset, len);
701
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100702 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200703 if (info->cat[i].name == NULL)
704 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100705 ret = snprintf(str + offset, rem, "%s\n",
706 info->cat[i].description);
707 if (ret < 0)
708 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200709 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100710 }
711 for (i = 0; i < LOGLEVEL_DEFS; i++) {
712 ret = snprintf(str + offset, rem, "%s\n",
713 loglevel_descriptions[i]);
714 if (ret < 0)
715 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200716 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100717 }
718err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200719 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200720 return str;
721}
722
Harald Welte18fc4652011-08-17 14:14:17 +0200723/*! \brief Initialize the Osmocom logging core
724 * \param[in] inf Information regarding logging categories
725 * \param[in] ctx \ref talloc context for logging allocations
726 * \returns 0 in case of success, negative in case of error
727 */
Harald Welteb43bc042011-06-27 10:29:17 +0200728int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800729{
Harald Welteb43bc042011-06-27 10:29:17 +0200730 int i;
731
732 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
733 if (!tall_log_ctx)
734 return -ENOMEM;
735
736 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
737 if (!osmo_log_info)
738 return -ENOMEM;
739
740 osmo_log_info->num_cat_user = inf->num_cat;
741 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200742 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200743
744 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
745 struct log_info_cat,
746 osmo_log_info->num_cat);
747 if (!osmo_log_info->cat) {
748 talloc_free(osmo_log_info);
749 osmo_log_info = NULL;
750 return -ENOMEM;
751 }
752
753 /* copy over the user part */
754 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200755 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
756 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200757 sizeof(struct log_info_cat));
758 }
759
760 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200761 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200762 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200763 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200764 &internal_cat[i], sizeof(struct log_info_cat));
765 }
766
767 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800768}
Harald Welte18fc4652011-08-17 14:14:17 +0200769
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200770/*! @} */