blob: 214ad2ab2dd6a29d200cfba7d5de5c96ff617df7 [file] [log] [blame]
Harald Welte4a2bb9e2010-03-26 09:33:40 +08001/* Debugging/Logging support code */
2
3/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
Harald Welte18fc4652011-08-17 14:14:17 +020023/* \addtogroup logging
24 * @{
25 */
26
27/* \file logging.c */
28
Harald Welte01fd5cb2010-03-26 23:51:31 +080029#include "../config.h"
30
Harald Welte4a2bb9e2010-03-26 09:33:40 +080031#include <stdarg.h>
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010035#include <ctype.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080036
37#ifdef HAVE_STRINGS_H
Harald Welte4a2bb9e2010-03-26 09:33:40 +080038#include <strings.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080039#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +080040#include <time.h>
41#include <errno.h>
42
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010043#include <osmocom/core/talloc.h>
44#include <osmocom/core/utils.h>
45#include <osmocom/core/logging.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080046
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010047#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
48
Harald Welteb43bc042011-06-27 10:29:17 +020049struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080050
Harald Welte3ae27582010-03-26 21:24:24 +080051static struct log_context log_context;
52static void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010053LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080054
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010055#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
56
57static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
Harald Welte9b186702013-03-19 09:55:28 +010058 { 0, "EVERYTHING" },
Harald Welte4a2bb9e2010-03-26 09:33:40 +080059 { LOGL_DEBUG, "DEBUG" },
60 { LOGL_INFO, "INFO" },
61 { LOGL_NOTICE, "NOTICE" },
62 { LOGL_ERROR, "ERROR" },
63 { LOGL_FATAL, "FATAL" },
64 { 0, NULL },
65};
66
Harald Welteb43bc042011-06-27 10:29:17 +020067#define INT2IDX(x) (-1*(x)-1)
68static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
69 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
70 .name = "DLGLOBAL",
71 .description = "Library-internal global log family",
72 .loglevel = LOGL_NOTICE,
73 .enabled = 1,
74 },
root8a996b42011-09-26 11:22:21 +020075 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
76 .name = "DLLAPD",
77 .description = "LAPD in libosmogsm",
Harald Welte1f0b8c22011-06-27 10:51:37 +020078 .loglevel = LOGL_NOTICE,
79 .enabled = 1,
80 },
Harald Welte892e6212011-07-19 14:31:44 +020081 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +020082 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020083 .description = "A-bis Intput Subsystem",
84 .loglevel = LOGL_NOTICE,
85 .enabled = 1,
86 },
Harald Welte892e6212011-07-19 14:31:44 +020087 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +020088 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020089 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
90 .loglevel = LOGL_NOTICE,
91 .enabled = 1,
92 },
Harald Welte892e6212011-07-19 14:31:44 +020093 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +020094 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020095 .description = "A-bis Input Driver for Signalling",
96 .enabled = 0, .loglevel = LOGL_NOTICE,
97 },
Harald Welte892e6212011-07-19 14:31:44 +020098 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +020099 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200100 .description = "A-bis Input Driver for B-Channels (voice)",
101 .enabled = 0, .loglevel = LOGL_NOTICE,
102 },
Andreas Eversbergc626da92011-10-28 03:53:50 +0200103 [INT2IDX(DLSMS)] = {
104 .name = "DLSMS",
105 .description = "Layer3 Short Message Service (SMS)",
106 .enabled = 1, .loglevel = LOGL_NOTICE,
107 .color = "\033[1;38m",
108 },
Harald Welteb43bc042011-06-27 10:29:17 +0200109};
110
Harald Weltede6e4982012-12-06 21:25:27 +0100111/*! \brief descriptive string for each log level */
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100112/* You have to keep this in sync with the structure loglevel_strs. */
113const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
114 "Log simply everything",
115 "Log debug messages and higher levels",
116 "Log informational messages and higher levels",
117 "Log noticable messages and higher levels",
118 "Log error messages and higher levels",
119 "Log only fatal messages",
120 NULL,
121};
122
Harald Welteb43bc042011-06-27 10:29:17 +0200123/* special magic for negative (library-internal) log subsystem numbers */
124static int subsys_lib2index(int subsys)
125{
126 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
127}
128
Harald Welte18fc4652011-08-17 14:14:17 +0200129/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800130int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800131{
132 return get_string_value(loglevel_strs, lvl);
133}
134
Harald Welte18fc4652011-08-17 14:14:17 +0200135/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200136const char *log_level_str(unsigned int lvl)
137{
138 return get_value_string(loglevel_strs, lvl);
139}
140
Harald Welte18fc4652011-08-17 14:14:17 +0200141/*! \brief parse a human-readable log category into numeric form
142 * \param[in] category human-readable log category name
143 * \returns numeric category value, or -EINVAL otherwise
144 */
Harald Welte3ae27582010-03-26 21:24:24 +0800145int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800146{
147 int i;
148
Harald Welte4ebdf742010-05-19 19:54:00 +0200149 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200150 if (osmo_log_info->cat[i].name == NULL)
151 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200152 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800153 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800154 }
155
156 return -EINVAL;
157}
158
Harald Welte18fc4652011-08-17 14:14:17 +0200159/*! \brief parse the log category mask
160 * \param[in] target log target to be configured
161 * \param[in] _mask log category mask string
162 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800163 * The format can be this: category1:category2:category3
164 * or category1,2:category2,3:...
165 */
Harald Welte3ae27582010-03-26 21:24:24 +0800166void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800167{
168 int i = 0;
169 char *mask = strdup(_mask);
170 char *category_token = NULL;
171
172 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200173 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800174 target->categories[i].enabled = 0;
175
176 category_token = strtok(mask, ":");
177 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200178 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200179 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800180 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200181
182 if (!osmo_log_info->cat[i].name)
183 continue;
184
185 length = strlen(category_token);
186 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200187
188 /* Use longest length not to match subocurrences. */
189 if (cat_length > length)
190 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800191
192 if (colon)
193 length = colon - category_token;
194
Harald Welte4ebdf742010-05-19 19:54:00 +0200195 if (strncasecmp(osmo_log_info->cat[i].name,
196 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800197 int level = 0;
198
199 if (colon)
200 level = atoi(colon+1);
201
Harald Weltefaadfe22010-03-26 21:05:43 +0800202 target->categories[i].enabled = 1;
203 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800204 }
205 }
206 } while ((category_token = strtok(NULL, ":")));
207
208 free(mask);
209}
210
211static const char* color(int subsys)
212{
Harald Welte4ebdf742010-05-19 19:54:00 +0200213 if (subsys < osmo_log_info->num_cat)
214 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800215
Harald Welted788f662010-03-26 09:45:03 +0800216 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800217}
218
Harald Welte3ae27582010-03-26 21:24:24 +0800219static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200220 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100221 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800222{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800223 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200224 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800225
226 /* are we using color */
227 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800228 const char *c = color(subsys);
229 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200230 ret = snprintf(buf + offset, rem, "%s", color(subsys));
231 if (ret < 0)
232 goto err;
233 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800234 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800235 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800236 if (!cont) {
237 if (target->print_timestamp) {
238 char *timestr;
239 time_t tm;
240 tm = time(NULL);
241 timestr = ctime(&tm);
242 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200243 ret = snprintf(buf + offset, rem, "%s ", timestr);
244 if (ret < 0)
245 goto err;
246 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800247 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200248 if (target->print_filename) {
249 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
250 subsys, file, line);
251 if (ret < 0)
252 goto err;
253 OSMO_SNPRINTF_RET(ret, rem, offset, len);
254 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800255 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200256 ret = vsnprintf(buf + offset, rem, format, ap);
257 if (ret < 0)
258 goto err;
259 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800260
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200261 ret = snprintf(buf + offset, rem, "%s",
262 target->use_color ? "\033[0;m" : "");
263 if (ret < 0)
264 goto err;
265 OSMO_SNPRINTF_RET(ret, rem, offset, len);
266err:
267 buf[sizeof(buf)-1] = '\0';
268 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800269}
270
Harald Welte36c5a3e2011-08-27 14:33:19 +0200271/*! \brief vararg version of logging function */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200272void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200273 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800274{
Harald Welte3ae27582010-03-26 21:24:24 +0800275 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800276
Harald Welteb43bc042011-06-27 10:29:17 +0200277 if (subsys < 0)
278 subsys = subsys_lib2index(subsys);
279
280 if (subsys > osmo_log_info->num_cat)
281 subsys = DLGLOBAL;
282
Harald Welte28222962011-02-18 20:37:04 +0100283 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800284 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800285 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200286 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800287
288 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800289 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800290 if (!category->enabled)
291 continue;
292
293 /* Check the global log level */
294 if (tar->loglevel != 0 && level < tar->loglevel)
295 continue;
296
297 /* Check the category log level */
298 if (tar->loglevel == 0 && category->loglevel != 0 &&
299 level < category->loglevel)
300 continue;
301
302 /* Apply filters here... if that becomes messy we will
303 * need to put filters in a list and each filter will
304 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800305 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800306 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200307 else if (osmo_log_info->filter_fn)
308 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800309 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200310 if (!output)
311 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800312
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200313 /* According to the manpage, vsnprintf leaves the value of ap
314 * in undefined state. Since _output uses vsnprintf and it may
315 * be called several times, we have to pass a copy of ap. */
316 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200317 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200318 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800319 }
320}
321
Harald Weltede6e4982012-12-06 21:25:27 +0100322/*! \brief logging function used by DEBUGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200323void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800324 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800325{
326 va_list ap;
327
328 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200329 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800330 va_end(ap);
331}
332
Harald Weltede6e4982012-12-06 21:25:27 +0100333/*! \brief logging function used by LOGP() macro */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200334void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800335{
336 va_list ap;
337
338 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200339 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800340 va_end(ap);
341}
342
Harald Welte18fc4652011-08-17 14:14:17 +0200343/*! \brief Register a new log target with the logging core
344 * \param[in] target Log target to be registered
345 */
Harald Welte3ae27582010-03-26 21:24:24 +0800346void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800347{
Harald Welte28222962011-02-18 20:37:04 +0100348 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800349}
350
Harald Welte18fc4652011-08-17 14:14:17 +0200351/*! \brief Unregister a log target from the logging core
352 * \param[in] target Log target to be unregistered
353 */
Harald Welte3ae27582010-03-26 21:24:24 +0800354void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800355{
356 llist_del(&target->entry);
357}
358
Harald Welte18fc4652011-08-17 14:14:17 +0200359/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800360void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800361{
Harald Welte3ae27582010-03-26 21:24:24 +0800362 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800363}
364
Harald Welte18fc4652011-08-17 14:14:17 +0200365/*! \brief Set the logging context
366 * \param[in] ctx_nr logging context number
367 * \param[in] value value to which the context is to be set
368 *
369 * A logging context is something like the subscriber identity to which
370 * the currently processed message relates, or the BTS through which it
371 * was received. As soon as this data is known, it can be set using
372 * this function. The main use of context information is for logging
373 * filters.
374 */
Harald Welte3ae27582010-03-26 21:24:24 +0800375int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800376{
Harald Welte3ae27582010-03-26 21:24:24 +0800377 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800378 return -EINVAL;
379
Harald Welte3ae27582010-03-26 21:24:24 +0800380 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800381
382 return 0;
383}
384
Harald Welte18fc4652011-08-17 14:14:17 +0200385/*! \brief Enable the \ref LOG_FILTER_ALL log filter
386 * \param[in] target Log target to be affected
387 * \param[in] all enable (1) or disable (0) the ALL filter
388 *
389 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
390 * be printed. It acts as a wildcard. Setting it to \a 1 means there
391 * is no filtering.
392 */
Harald Welte3ae27582010-03-26 21:24:24 +0800393void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800394{
395 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800396 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800397 else
Harald Welte3ae27582010-03-26 21:24:24 +0800398 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800399}
400
Harald Welte18fc4652011-08-17 14:14:17 +0200401/*! \brief Enable or disable the use of colored output
402 * \param[in] target Log target to be affected
403 * \param[in] use_color Use color (1) or don't use color (0)
404 */
Harald Welte3ae27582010-03-26 21:24:24 +0800405void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800406{
407 target->use_color = use_color;
408}
409
Harald Welte18fc4652011-08-17 14:14:17 +0200410/*! \brief Enable or disable printing of timestamps while logging
411 * \param[in] target Log target to be affected
412 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
413 */
Harald Welte3ae27582010-03-26 21:24:24 +0800414void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800415{
416 target->print_timestamp = print_timestamp;
417}
418
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200419/*! \brief Enable or disable printing of the filename while logging
420 * \param[in] target Log target to be affected
421 * \param[in] print_filename Enable (1) or disable (0) filenames
422 */
423void log_set_print_filename(struct log_target *target, int print_filename)
424{
425 target->print_filename = print_filename;
426}
427
Harald Welte18fc4652011-08-17 14:14:17 +0200428/*! \brief Set the global log level for a given log target
429 * \param[in] target Log target to be affected
430 * \param[in] log_level New global log level
431 */
Harald Welte3ae27582010-03-26 21:24:24 +0800432void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800433{
434 target->loglevel = log_level;
435}
436
Harald Weltede6e4982012-12-06 21:25:27 +0100437/*! \brief Set a category filter on a given log target
438 * \param[in] target Log target to be affected
439 * \param[in] category Log category to be affected
440 * \param[in] enable whether to enable or disable the filter
441 * \param[in] level Log level of the filter
442 */
Harald Welte3ae27582010-03-26 21:24:24 +0800443void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800444 int enable, int level)
445{
Harald Welte4ebdf742010-05-19 19:54:00 +0200446 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800447 return;
448 target->categories[category].enabled = !!enable;
449 target->categories[category].loglevel = level;
450}
451
Harald Welte76e72ab2011-02-17 15:52:39 +0100452static void _file_output(struct log_target *target, unsigned int level,
453 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800454{
Harald Welte0083cd32010-08-25 14:55:44 +0200455 fprintf(target->tgt_file.out, "%s", log);
456 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800457}
458
Harald Welte18fc4652011-08-17 14:14:17 +0200459/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800460struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800461{
Harald Welte3ae27582010-03-26 21:24:24 +0800462 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800463 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800464
Harald Welte3ae27582010-03-26 21:24:24 +0800465 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800466 if (!target)
467 return NULL;
468
Harald Welteb43bc042011-06-27 10:29:17 +0200469 target->categories = talloc_zero_array(target,
470 struct log_category,
471 osmo_log_info->num_cat);
472 if (!target->categories) {
473 talloc_free(target);
474 return NULL;
475 }
476
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800477 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800478
479 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200480 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800481 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200482 cat->enabled = osmo_log_info->cat[i].enabled;
483 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800484 }
485
486 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800487 target->use_color = 1;
488 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200489 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800490
491 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800492 target->loglevel = 0;
493 return target;
494}
495
Harald Welte18fc4652011-08-17 14:14:17 +0200496/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800497struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800498{
Harald Weltea3b844c2010-03-27 00:04:40 +0800499/* since C89/C99 says stderr is a macro, we can safely do this! */
500#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800501 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800502
Harald Welte3ae27582010-03-26 21:24:24 +0800503 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800504 if (!target)
505 return NULL;
506
Harald Welte28222962011-02-18 20:37:04 +0100507 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200508 target->tgt_file.out = stderr;
509 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800510 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800511#else
512 return NULL;
513#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800514}
515
Harald Welte18fc4652011-08-17 14:14:17 +0200516/*! \brief Create a new file-based log target
517 * \param[in] fname File name of the new log file
518 * \returns Log target in case of success, NULL otherwise
519 */
Harald Welte3086c392010-08-25 19:10:50 +0200520struct log_target *log_target_create_file(const char *fname)
521{
522 struct log_target *target;
523
524 target = log_target_create();
525 if (!target)
526 return NULL;
527
Harald Welte28222962011-02-18 20:37:04 +0100528 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200529 target->tgt_file.out = fopen(fname, "a");
530 if (!target->tgt_file.out)
531 return NULL;
532
533 target->output = _file_output;
534
535 target->tgt_file.fname = talloc_strdup(target, fname);
536
537 return target;
538}
539
Harald Welte18fc4652011-08-17 14:14:17 +0200540/*! \brief Find a registered log target
541 * \param[in] type Log target type
542 * \param[in] fname File name
543 * \returns Log target (if found), NULL otherwise
544 */
Harald Welte28222962011-02-18 20:37:04 +0100545struct log_target *log_target_find(int type, const char *fname)
546{
547 struct log_target *tgt;
548
549 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
550 if (tgt->type != type)
551 continue;
552 if (tgt->type == LOG_TGT_TYPE_FILE) {
553 if (!strcmp(fname, tgt->tgt_file.fname))
554 return tgt;
555 } else
556 return tgt;
557 }
558 return NULL;
559}
560
Harald Welte18fc4652011-08-17 14:14:17 +0200561/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200562void log_target_destroy(struct log_target *target)
563{
564
565 /* just in case, to make sure we don't have any references */
566 log_del_target(target);
567
568 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200569/* since C89/C99 says stderr is a macro, we can safely do this! */
570#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200571 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200572 if (target->tgt_file.out != stderr)
573#endif
574 {
Harald Welte3086c392010-08-25 19:10:50 +0200575 fclose(target->tgt_file.out);
576 target->tgt_file.out = NULL;
577 }
578 }
579
580 talloc_free(target);
581}
582
Harald Welte18fc4652011-08-17 14:14:17 +0200583/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200584int log_target_file_reopen(struct log_target *target)
585{
586 fclose(target->tgt_file.out);
587
588 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
589 if (!target->tgt_file.out)
590 return -errno;
591
592 /* we assume target->output already to be set */
593
594 return 0;
595}
596
Harald Welte4de854d2013-03-18 19:01:40 +0100597/*! \brief close and re-open a log file (for log file rotation) */
598int log_targets_reopen(void)
599{
600 struct log_target *tar;
601 int rc = 0;
602
603 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
604 switch (tar->type) {
605 case LOG_TGT_TYPE_FILE:
606 if (log_target_file_reopen(tar) < 0)
607 rc = -1;
608 break;
609 default:
610 break;
611 }
612 }
613
614 return rc;
615}
616
Harald Welte18fc4652011-08-17 14:14:17 +0200617/*! \brief Generates the logging command string for VTY
618 * \param[in] unused_info Deprecated parameter, no longer used!
619 */
Harald Weltece9fec32011-06-27 14:19:16 +0200620const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200621{
Harald Weltece9fec32011-06-27 14:19:16 +0200622 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100623 int len = 0, offset = 0, ret, i, rem;
624 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200625 char *str;
626
Harald Welteb43bc042011-06-27 10:29:17 +0200627 for (i = 0; i < info->num_cat; i++) {
628 if (info->cat[i].name == NULL)
629 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100630 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200631 }
Harald Welte7638af92010-05-11 16:39:22 +0200632
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100633 for (i = 0; i < LOGLEVEL_DEFS; i++)
634 size += strlen(loglevel_strs[i].str) + 1;
635
636 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200637 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200638 if (!str)
639 return NULL;
640
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200641 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100642 if (ret < 0)
643 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200644 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200645
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100646 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200647 if (info->cat[i].name) {
648 int j, name_len = strlen(info->cat[i].name)+1;
649 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100650
Harald Welteb43bc042011-06-27 10:29:17 +0200651 for (j = 0; j < name_len; j++)
652 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100653
Harald Welteb43bc042011-06-27 10:29:17 +0200654 name[name_len-1] = '\0';
655 ret = snprintf(str + offset, rem, "%s|", name+1);
656 if (ret < 0)
657 goto err;
658 OSMO_SNPRINTF_RET(ret, rem, offset, len);
659 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100660 }
661 offset--; /* to remove the trailing | */
662 rem++;
663
664 ret = snprintf(str + offset, rem, ") (");
665 if (ret < 0)
666 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200667 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100668
669 for (i = 0; i < LOGLEVEL_DEFS; i++) {
670 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
671 char loglevel_str[loglevel_str_len];
672
673 for (j = 0; j < loglevel_str_len; j++)
674 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
675
676 loglevel_str[loglevel_str_len-1] = '\0';
677 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
678 if (ret < 0)
679 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200680 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100681 }
682 offset--; /* to remove the trailing | */
683 rem++;
684
685 ret = snprintf(str + offset, rem, ")");
686 if (ret < 0)
687 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200688 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100689err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200690 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100691 return str;
692}
693
Harald Welte18fc4652011-08-17 14:14:17 +0200694/*! \brief Generates the logging command description for VTY
695 * \param[in] unused_info Deprecated parameter, no longer used!
696 */
Harald Weltece9fec32011-06-27 14:19:16 +0200697const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100698{
Harald Weltece9fec32011-06-27 14:19:16 +0200699 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100700 char *str;
701 int i, ret, len = 0, offset = 0, rem;
702 unsigned int size =
703 strlen(LOGGING_STR
704 "Set the log level for a specified category\n") + 1;
705
Harald Welteb43bc042011-06-27 10:29:17 +0200706 for (i = 0; i < info->num_cat; i++) {
707 if (info->cat[i].name == NULL)
708 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100709 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200710 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100711
712 for (i = 0; i < LOGLEVEL_DEFS; i++)
713 size += strlen(loglevel_descriptions[i]) + 1;
714
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200715 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100716 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200717 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100718 if (!str)
719 return NULL;
720
721 ret = snprintf(str + offset, rem, LOGGING_STR
722 "Set the log level for a specified category\n");
723 if (ret < 0)
724 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200725 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100726
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200727 ret = snprintf(str + offset, rem,
728 "Global setting for all subsystems\n");
729 if (ret < 0)
730 goto err;
731 OSMO_SNPRINTF_RET(ret, rem, offset, len);
732
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100733 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200734 if (info->cat[i].name == NULL)
735 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100736 ret = snprintf(str + offset, rem, "%s\n",
737 info->cat[i].description);
738 if (ret < 0)
739 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200740 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100741 }
742 for (i = 0; i < LOGLEVEL_DEFS; i++) {
743 ret = snprintf(str + offset, rem, "%s\n",
744 loglevel_descriptions[i]);
745 if (ret < 0)
746 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200747 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100748 }
749err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200750 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200751 return str;
752}
753
Harald Welte18fc4652011-08-17 14:14:17 +0200754/*! \brief Initialize the Osmocom logging core
755 * \param[in] inf Information regarding logging categories
756 * \param[in] ctx \ref talloc context for logging allocations
757 * \returns 0 in case of success, negative in case of error
758 */
Harald Welteb43bc042011-06-27 10:29:17 +0200759int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800760{
Harald Welteb43bc042011-06-27 10:29:17 +0200761 int i;
762
763 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
764 if (!tall_log_ctx)
765 return -ENOMEM;
766
767 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
768 if (!osmo_log_info)
769 return -ENOMEM;
770
771 osmo_log_info->num_cat_user = inf->num_cat;
772 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200773 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200774
775 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
776 struct log_info_cat,
777 osmo_log_info->num_cat);
778 if (!osmo_log_info->cat) {
779 talloc_free(osmo_log_info);
780 osmo_log_info = NULL;
781 return -ENOMEM;
782 }
783
784 /* copy over the user part */
785 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200786 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
787 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200788 sizeof(struct log_info_cat));
789 }
790
791 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200792 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200793 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200794 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200795 &internal_cat[i], sizeof(struct log_info_cat));
796 }
797
798 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800799}
Harald Welte18fc4652011-08-17 14:14:17 +0200800
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200801/*! @} */