blob: db04cf7e6ee09712a9a442349918832118c1eccb [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file logging.c
2 * Debugging/Logging support code. */
3/*
4 * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
Harald Welte4a2bb9e2010-03-26 09:33:40 +08005 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
9 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +080010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
Harald Weltef01b2382017-10-16 13:55:34 +020026/*! \addtogroup logging
Harald Welte18fc4652011-08-17 14:14:17 +020027 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020028 * libosmocore Logging sub-system
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020029 *
30 * \file logging.c */
Harald Welte18fc4652011-08-17 14:14:17 +020031
Harald Welte01fd5cb2010-03-26 23:51:31 +080032#include "../config.h"
33
Harald Welte4a2bb9e2010-03-26 09:33:40 +080034#include <stdarg.h>
35#include <stdlib.h>
36#include <stdio.h>
37#include <string.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080038
39#ifdef HAVE_STRINGS_H
Harald Welte4a2bb9e2010-03-26 09:33:40 +080040#include <strings.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080041#endif
Vadim Yanitskiy04f42712020-09-09 04:47:25 +070042
43#ifdef HAVE_SYSLOG_H
44#include <syslog.h>
45#endif
46
Harald Welte4a2bb9e2010-03-26 09:33:40 +080047#include <time.h>
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +010048#include <sys/time.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080049#include <errno.h>
Pau Espin Pedrold12f6982019-09-17 18:38:58 +020050#include <pthread.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080051
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010052#include <osmocom/core/talloc.h>
53#include <osmocom/core/utils.h>
54#include <osmocom/core/logging.h>
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +020055#include <osmocom/core/timer.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080056
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010057#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
58
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +010059osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx),
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +010060 enum_logging_ctx_items_fit_in_struct_log_context);
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +010061osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data),
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +010062 enum_logging_filters_fit_in_log_target_filter_data);
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +010063osmo_static_assert(_LOG_FLT_COUNT <= 8*sizeof(((struct log_target*)NULL)->filter_map),
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +010064 enum_logging_filters_fit_in_log_target_filter_map);
65
Harald Welteb43bc042011-06-27 10:29:17 +020066struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080067
Harald Welte3ae27582010-03-26 21:24:24 +080068static struct log_context log_context;
Neels Hofmeyrba0762d2018-09-10 13:56:03 +020069void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010070LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080071
Pau Espin Pedrold12f6982019-09-17 18:38:58 +020072#if (!EMBEDDED)
73/*! This mutex must be held while using osmo_log_target_list or any of its
74 log_targets in a multithread program. Prevents race conditions between threads
75 like producing unordered timestamps or VTY deleting a target while another
76 thread is writing to it */
77static pthread_mutex_t osmo_log_tgt_mutex;
78static bool osmo_log_tgt_mutex_on = false;
79
80/*! Enable multithread support (mutex) in libosmocore logging system.
81 * Must be called by processes willing to use logging subsystem from several
82 * threads. Once enabled, it's not possible to disable it again.
83 */
84void log_enable_multithread(void) {
85 if (osmo_log_tgt_mutex_on)
86 return;
87 pthread_mutex_init(&osmo_log_tgt_mutex, NULL);
88 osmo_log_tgt_mutex_on = true;
89}
90
91/*! Acquire the osmo_log_tgt_mutex. Don't use this function directly, always use
92 * macro log_tgt_mutex_lock() instead.
93 */
94void log_tgt_mutex_lock_impl(void) {
95 /* These lines are useful to debug scenarios where there's only 1 thread
96 and a double lock appears, for instance during startup and some
97 unlock() missing somewhere:
98 if (osmo_log_tgt_mutex_on && pthread_mutex_trylock(&osmo_log_tgt_mutex) != 0)
99 osmo_panic("acquiring already locked mutex!\n");
100 return;
101 */
102
103 if (osmo_log_tgt_mutex_on)
104 pthread_mutex_lock(&osmo_log_tgt_mutex);
105}
106
107/*! Release the osmo_log_tgt_mutex. Don't use this function directly, always use
108 * macro log_tgt_mutex_unlock() instead.
109 */
110void log_tgt_mutex_unlock_impl(void) {
111 if (osmo_log_tgt_mutex_on)
112 pthread_mutex_unlock(&osmo_log_tgt_mutex);
113}
114
115#else /* if (!EMBEDDED) */
116#pragma message ("logging multithread support disabled in embedded build")
117void log_enable_multithread(void) {}
118void log_tgt_mutex_lock_impl(void) {}
119void log_tgt_mutex_unlock_impl(void) {}
120#endif /* if (!EMBEDDED) */
121
Neels Hofmeyr098038a2018-09-11 23:49:13 +0200122const struct value_string loglevel_strs[] = {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800123 { LOGL_DEBUG, "DEBUG" },
124 { LOGL_INFO, "INFO" },
125 { LOGL_NOTICE, "NOTICE" },
126 { LOGL_ERROR, "ERROR" },
127 { LOGL_FATAL, "FATAL" },
128 { 0, NULL },
129};
130
Harald Welteb43bc042011-06-27 10:29:17 +0200131#define INT2IDX(x) (-1*(x)-1)
132static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
133 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
134 .name = "DLGLOBAL",
135 .description = "Library-internal global log family",
136 .loglevel = LOGL_NOTICE,
137 .enabled = 1,
138 },
root8a996b42011-09-26 11:22:21 +0200139 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
140 .name = "DLLAPD",
141 .description = "LAPD in libosmogsm",
Harald Welte1f0b8c22011-06-27 10:51:37 +0200142 .loglevel = LOGL_NOTICE,
143 .enabled = 1,
144 },
Harald Welte892e6212011-07-19 14:31:44 +0200145 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200146 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200147 .description = "A-bis Intput Subsystem",
148 .loglevel = LOGL_NOTICE,
149 .enabled = 1,
150 },
Harald Welte892e6212011-07-19 14:31:44 +0200151 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200152 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200153 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
154 .loglevel = LOGL_NOTICE,
155 .enabled = 1,
156 },
Harald Welte892e6212011-07-19 14:31:44 +0200157 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200158 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200159 .description = "A-bis Input Driver for Signalling",
160 .enabled = 0, .loglevel = LOGL_NOTICE,
161 },
Harald Welte892e6212011-07-19 14:31:44 +0200162 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200163 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200164 .description = "A-bis Input Driver for B-Channels (voice)",
165 .enabled = 0, .loglevel = LOGL_NOTICE,
166 },
Andreas Eversbergc626da92011-10-28 03:53:50 +0200167 [INT2IDX(DLSMS)] = {
168 .name = "DLSMS",
169 .description = "Layer3 Short Message Service (SMS)",
170 .enabled = 1, .loglevel = LOGL_NOTICE,
Neels Hofmeyrc5b71752019-11-20 04:50:52 +0100171 .color = OSMO_LOGCOLOR_BRIGHTWHITE,
Andreas Eversbergc626da92011-10-28 03:53:50 +0200172 },
Harald Welte7fd0c832014-08-20 19:58:13 +0200173 [INT2IDX(DLCTRL)] = {
174 .name = "DLCTRL",
175 .description = "Control Interface",
176 .enabled = 1, .loglevel = LOGL_NOTICE,
177 },
Holger Hans Peter Freythera5dc19d2014-12-04 14:35:21 +0100178 [INT2IDX(DLGTP)] = {
179 .name = "DLGTP",
180 .description = "GPRS GTP library",
181 .enabled = 1, .loglevel = LOGL_NOTICE,
182 },
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100183 [INT2IDX(DLSTATS)] = {
184 .name = "DLSTATS",
185 .description = "Statistics messages and logging",
186 .enabled = 1, .loglevel = LOGL_NOTICE,
187 },
Neels Hofmeyr9795cf12016-12-10 17:01:06 +0100188 [INT2IDX(DLGSUP)] = {
189 .name = "DLGSUP",
190 .description = "Generic Subscriber Update Protocol",
191 .enabled = 1, .loglevel = LOGL_NOTICE,
192 },
Harald Weltec0f00072016-04-27 18:32:35 +0200193 [INT2IDX(DLOAP)] = {
194 .name = "DLOAP",
195 .description = "Osmocom Authentication Protocol",
196 .enabled = 1, .loglevel = LOGL_NOTICE,
197 },
Harald Welte059c4042017-04-03 22:20:49 +0200198 [INT2IDX(DLSS7)] = {
199 .name = "DLSS7",
200 .description = "libosmo-sigtran Signalling System 7",
201 .enabled = 1, .loglevel = LOGL_NOTICE,
202 },
203 [INT2IDX(DLSCCP)] = {
204 .name = "DLSCCP",
205 .description = "libosmo-sigtran SCCP Implementation",
206 .enabled = 1, .loglevel = LOGL_NOTICE,
207 },
208 [INT2IDX(DLSUA)] = {
209 .name = "DLSUA",
210 .description = "libosmo-sigtran SCCP User Adaptation",
211 .enabled = 1, .loglevel = LOGL_NOTICE,
212 },
213 [INT2IDX(DLM3UA)] = {
214 .name = "DLM3UA",
215 .description = "libosmo-sigtran MTP3 User Adaptation",
216 .enabled = 1, .loglevel = LOGL_NOTICE,
217 },
Neels Hofmeyra7ccf612017-07-11 18:43:09 +0200218 [INT2IDX(DLMGCP)] = {
219 .name = "DLMGCP",
220 .description = "libosmo-mgcp Media Gateway Control Protocol",
221 .enabled = 1, .loglevel = LOGL_NOTICE,
222 },
Pau Espin Pedrol8fd85572018-02-27 19:43:10 +0100223 [INT2IDX(DLJIBUF)] = {
224 .name = "DLJIBUF",
225 .description = "libosmo-netif Jitter Buffer",
226 .enabled = 1, .loglevel = LOGL_NOTICE,
227 },
Max450f5ac2019-02-14 19:12:03 +0100228 [INT2IDX(DLRSPRO)] = {
229 .name = "DLRSPRO",
230 .description = "Remote SIM protocol",
231 .enabled = 1, .loglevel = LOGL_NOTICE,
232 },
Harald Welteb43bc042011-06-27 10:29:17 +0200233};
234
Neels Hofmeyrba0762d2018-09-10 13:56:03 +0200235void assert_loginfo(const char *src)
Harald Welte18a7d812017-03-16 23:54:55 +0100236{
237 if (!osmo_log_info) {
238 fprintf(stderr, "ERROR: osmo_log_info == NULL! "
Max68bf16a2018-01-10 17:00:43 +0100239 "You must call log_init() before using logging in %s()!\n", src);
Harald Welte18a7d812017-03-16 23:54:55 +0100240 OSMO_ASSERT(osmo_log_info);
241 }
242}
243
Harald Welteb43bc042011-06-27 10:29:17 +0200244/* special magic for negative (library-internal) log subsystem numbers */
245static int subsys_lib2index(int subsys)
246{
247 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
248}
249
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200250/*! Parse a human-readable log level into a numeric value
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +0700251 * \param[in] lvl zero-terminated string containing log level name
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200252 * \returns numeric log level
253 */
Harald Welte3ae27582010-03-26 21:24:24 +0800254int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800255{
256 return get_string_value(loglevel_strs, lvl);
257}
258
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200259/*! convert a numeric log level into human-readable string
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +0700260 * \param[in] lvl numeric log level
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200261 * \returns zero-terminated string (log level name)
262 */
Harald Welte9ac22252010-05-11 11:19:40 +0200263const char *log_level_str(unsigned int lvl)
264{
265 return get_value_string(loglevel_strs, lvl);
266}
267
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200268/*! parse a human-readable log category into numeric form
Harald Welte18fc4652011-08-17 14:14:17 +0200269 * \param[in] category human-readable log category name
270 * \returns numeric category value, or -EINVAL otherwise
271 */
Harald Welte3ae27582010-03-26 21:24:24 +0800272int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800273{
274 int i;
275
Max68bf16a2018-01-10 17:00:43 +0100276 assert_loginfo(__func__);
Harald Welte18a7d812017-03-16 23:54:55 +0100277
Harald Welte4ebdf742010-05-19 19:54:00 +0200278 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200279 if (osmo_log_info->cat[i].name == NULL)
280 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200281 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800282 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800283 }
284
285 return -EINVAL;
286}
287
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200288/*! parse the log category mask
Harald Welte18fc4652011-08-17 14:14:17 +0200289 * \param[in] target log target to be configured
290 * \param[in] _mask log category mask string
291 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800292 * The format can be this: category1:category2:category3
293 * or category1,2:category2,3:...
294 */
Harald Welte3ae27582010-03-26 21:24:24 +0800295void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800296{
297 int i = 0;
298 char *mask = strdup(_mask);
299 char *category_token = NULL;
300
Max68bf16a2018-01-10 17:00:43 +0100301 assert_loginfo(__func__);
Harald Welte18a7d812017-03-16 23:54:55 +0100302
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800303 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200304 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800305 target->categories[i].enabled = 0;
306
307 category_token = strtok(mask, ":");
Neels Hofmeyrda1b20c2016-04-14 15:12:16 +0200308 OSMO_ASSERT(category_token);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800309 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200310 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200311 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800312 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200313
314 if (!osmo_log_info->cat[i].name)
315 continue;
316
317 length = strlen(category_token);
318 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200319
320 /* Use longest length not to match subocurrences. */
321 if (cat_length > length)
322 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800323
324 if (colon)
325 length = colon - category_token;
326
Harald Welte4ebdf742010-05-19 19:54:00 +0200327 if (strncasecmp(osmo_log_info->cat[i].name,
328 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800329 int level = 0;
330
331 if (colon)
332 level = atoi(colon+1);
333
Harald Weltefaadfe22010-03-26 21:05:43 +0800334 target->categories[i].enabled = 1;
335 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800336 }
337 }
338 } while ((category_token = strtok(NULL, ":")));
339
340 free(mask);
341}
342
343static const char* color(int subsys)
344{
Harald Welte4ebdf742010-05-19 19:54:00 +0200345 if (subsys < osmo_log_info->num_cat)
346 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800347
Harald Welted788f662010-03-26 09:45:03 +0800348 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800349}
350
Neels Hofmeyrf3fa3692018-01-16 02:56:01 +0100351static const struct value_string level_colors[] = {
Neels Hofmeyrf2644ae2019-11-20 04:00:29 +0100352 { LOGL_DEBUG, OSMO_LOGCOLOR_BLUE },
353 { LOGL_INFO, OSMO_LOGCOLOR_GREEN },
354 { LOGL_NOTICE, OSMO_LOGCOLOR_YELLOW },
355 { LOGL_ERROR, OSMO_LOGCOLOR_RED },
356 { LOGL_FATAL, OSMO_LOGCOLOR_RED },
Neels Hofmeyrf3fa3692018-01-16 02:56:01 +0100357 { 0, NULL }
358};
359
360static const char *level_color(int level)
361{
362 const char *c = get_value_string_or_null(level_colors, level);
363 if (!c)
364 return get_value_string(level_colors, LOGL_FATAL);
365 return c;
366}
367
Harald Welteaa00f992016-12-02 15:30:02 +0100368const char* log_category_name(int subsys)
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100369{
370 if (subsys < osmo_log_info->num_cat)
371 return osmo_log_info->cat[subsys].name;
372
373 return NULL;
374}
375
Neels Hofmeyr0e2a9432018-01-16 02:49:48 +0100376static const char *const_basename(const char *path)
377{
378 const char *bn = strrchr(path, '/');
379 if (!bn || !bn[1])
380 return path;
381 return bn + 1;
382}
383
Harald Welte3ae27582010-03-26 21:24:24 +0800384static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200385 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100386 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800387{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800388 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200389 int ret, len = 0, offset = 0, rem = sizeof(buf);
Neels Hofmeyrf3fa3692018-01-16 02:56:01 +0100390 const char *c_subsys = NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800391
392 /* are we using color */
393 if (target->use_color) {
Neels Hofmeyrf3fa3692018-01-16 02:56:01 +0100394 c_subsys = color(subsys);
395 if (c_subsys) {
Neels Hofmeyr5e518b52018-01-17 13:20:02 +0100396 ret = snprintf(buf + offset, rem, "%s", c_subsys);
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200397 if (ret < 0)
398 goto err;
399 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800400 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800401 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800402 if (!cont) {
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100403 if (target->print_ext_timestamp) {
Harald Welte14c4c492018-06-28 08:28:52 +0200404#ifdef HAVE_LOCALTIME_R
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100405 struct tm tm;
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100406 struct timeval tv;
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200407 osmo_gettimeofday(&tv, NULL);
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100408 localtime_r(&tv.tv_sec, &tm);
409 ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ",
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100410 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100411 tm.tm_hour, tm.tm_min, tm.tm_sec,
412 (int)(tv.tv_usec / 1000));
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100413 if (ret < 0)
414 goto err;
415 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte14c4c492018-06-28 08:28:52 +0200416#endif
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100417 } else if (target->print_timestamp) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800418 time_t tm;
Pau Espin Pedrol3aef2382019-06-12 18:50:29 +0200419 if ((tm = time(NULL)) == (time_t) -1)
420 goto err;
Pau Espin Pedrolcc794e92019-06-12 16:22:53 +0200421 /* Get human-readable representation of time.
422 man ctime: we need at least 26 bytes in buf */
423 if (rem < 26 || !ctime_r(&tm, buf + offset))
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200424 goto err;
Pau Espin Pedrolcc794e92019-06-12 16:22:53 +0200425 ret = strlen(buf + offset);
426 if (ret <= 0)
427 goto err;
428 /* Get rid of useless final '\n' added by ctime_r. We want a space instead. */
429 buf[offset + ret - 1] = ' ';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200430 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800431 }
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100432 if (target->print_category) {
Neels Hofmeyre6534722018-01-16 03:02:06 +0100433 ret = snprintf(buf + offset, rem, "%s%s%s%s ",
434 target->use_color ? level_color(level) : "",
435 log_category_name(subsys),
Neels Hofmeyrf2644ae2019-11-20 04:00:29 +0100436 target->use_color ? OSMO_LOGCOLOR_END : "",
Neels Hofmeyre6534722018-01-16 03:02:06 +0100437 c_subsys ? c_subsys : "");
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100438 if (ret < 0)
439 goto err;
440 OSMO_SNPRINTF_RET(ret, rem, offset, len);
441 }
Neels Hofmeyr886e5482018-01-16 01:49:37 +0100442 if (target->print_level) {
Neels Hofmeyrf3fa3692018-01-16 02:56:01 +0100443 ret = snprintf(buf + offset, rem, "%s%s%s%s ",
444 target->use_color ? level_color(level) : "",
445 log_level_str(level),
Neels Hofmeyrf2644ae2019-11-20 04:00:29 +0100446 target->use_color ? OSMO_LOGCOLOR_END : "",
Neels Hofmeyrf3fa3692018-01-16 02:56:01 +0100447 c_subsys ? c_subsys : "");
Neels Hofmeyr886e5482018-01-16 01:49:37 +0100448 if (ret < 0)
449 goto err;
450 OSMO_SNPRINTF_RET(ret, rem, offset, len);
451 }
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100452 if (target->print_category_hex) {
453 ret = snprintf(buf + offset, rem, "<%4.4x> ", subsys);
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200454 if (ret < 0)
455 goto err;
456 OSMO_SNPRINTF_RET(ret, rem, offset, len);
457 }
Neels Hofmeyr77ae45d2018-08-27 20:32:36 +0200458
459 if (target->print_filename_pos == LOG_FILENAME_POS_HEADER_END) {
460 switch (target->print_filename2) {
461 case LOG_FILENAME_NONE:
462 break;
463 case LOG_FILENAME_PATH:
464 ret = snprintf(buf + offset, rem, "%s:%d ", file, line);
465 if (ret < 0)
466 goto err;
467 OSMO_SNPRINTF_RET(ret, rem, offset, len);
468 break;
469 case LOG_FILENAME_BASENAME:
470 ret = snprintf(buf + offset, rem, "%s:%d ", const_basename(file), line);
471 if (ret < 0)
472 goto err;
473 OSMO_SNPRINTF_RET(ret, rem, offset, len);
474 break;
475 }
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100476 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800477 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200478 ret = vsnprintf(buf + offset, rem, format, ap);
479 if (ret < 0)
480 goto err;
481 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800482
Neels Hofmeyr77ae45d2018-08-27 20:32:36 +0200483 /* For LOG_FILENAME_POS_LAST, print the source file info only when the caller ended the log
484 * message in '\n'. If so, nip the last '\n' away, insert the source file info and re-append an
485 * '\n'. All this to allow LOGP("start..."); LOGPC("...end\n") constructs. */
486 if (target->print_filename_pos == LOG_FILENAME_POS_LINE_END
487 && offset > 0 && buf[offset-1] == '\n') {
488 switch (target->print_filename2) {
489 case LOG_FILENAME_NONE:
490 break;
491 case LOG_FILENAME_PATH:
492 offset --;
493 ret = snprintf(buf + offset, rem, " (%s:%d)\n", file, line);
494 if (ret < 0)
495 goto err;
496 OSMO_SNPRINTF_RET(ret, rem, offset, len);
497 break;
498 case LOG_FILENAME_BASENAME:
499 offset --;
500 ret = snprintf(buf + offset, rem, " (%s:%d)\n", const_basename(file), line);
501 if (ret < 0)
502 goto err;
503 OSMO_SNPRINTF_RET(ret, rem, offset, len);
504 break;
505 }
506 }
507
Neels Hofmeyrc4759882018-01-16 02:10:48 +0100508 if (target->use_color) {
Neels Hofmeyrf2644ae2019-11-20 04:00:29 +0100509 ret = snprintf(buf + offset, rem, OSMO_LOGCOLOR_END);
Neels Hofmeyrc4759882018-01-16 02:10:48 +0100510 if (ret < 0)
511 goto err;
512 OSMO_SNPRINTF_RET(ret, rem, offset, len);
513 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200514err:
515 buf[sizeof(buf)-1] = '\0';
516 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800517}
518
Neels Hofmeyr42240de2016-12-12 15:13:56 +0100519/* Catch internal logging category indexes as well as out-of-bounds indexes.
520 * For internal categories, the ID is negative starting with -1; and internal
521 * logging categories are added behind the user categories. For out-of-bounds
522 * indexes, return the index of DLGLOBAL. The returned category index is
523 * guaranteed to exist in osmo_log_info, otherwise the program would abort,
524 * which should never happen unless even the DLGLOBAL category is missing. */
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100525static inline int map_subsys(int subsys)
526{
Neels Hofmeyr74802262016-12-12 16:00:24 +0100527 /* Note: comparing signed and unsigned integers */
528
529 if (subsys > 0 && ((unsigned int)subsys) >= osmo_log_info->num_cat_user)
530 subsys = DLGLOBAL;
531
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100532 if (subsys < 0)
533 subsys = subsys_lib2index(subsys);
534
Neels Hofmeyrca135742016-12-12 14:18:54 +0100535 if (subsys < 0 || subsys >= osmo_log_info->num_cat)
Neels Hofmeyr42240de2016-12-12 15:13:56 +0100536 subsys = subsys_lib2index(DLGLOBAL);
537
Neels Hofmeyrca135742016-12-12 14:18:54 +0100538 OSMO_ASSERT(!(subsys < 0 || subsys >= osmo_log_info->num_cat));
539
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100540 return subsys;
541}
542
Maxc65c5b42017-03-15 13:20:23 +0100543static inline bool should_log_to_target(struct log_target *tar, int subsys,
544 int level)
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100545{
546 struct log_category *category;
547
548 category = &tar->categories[subsys];
549
550 /* subsystem is not supposed to be logged */
551 if (!category->enabled)
Maxc65c5b42017-03-15 13:20:23 +0100552 return false;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100553
554 /* Check the global log level */
555 if (tar->loglevel != 0 && level < tar->loglevel)
Maxc65c5b42017-03-15 13:20:23 +0100556 return false;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100557
558 /* Check the category log level */
559 if (tar->loglevel == 0 && category->loglevel != 0 &&
560 level < category->loglevel)
Maxc65c5b42017-03-15 13:20:23 +0100561 return false;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100562
Holger Hans Peter Freyther79599ac2016-01-15 16:49:06 +0100563 /* Apply filters here... if that becomes messy we will
564 * need to put filters in a list and each filter will
565 * say stop, continue, output */
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100566 if ((tar->filter_map & (1 << LOG_FLT_ALL)) != 0)
Maxc65c5b42017-03-15 13:20:23 +0100567 return true;
Holger Hans Peter Freyther79599ac2016-01-15 16:49:06 +0100568
569 if (osmo_log_info->filter_fn)
570 return osmo_log_info->filter_fn(&log_context, tar);
571
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100572 /* TODO: Check the filter/selector too? */
Maxc65c5b42017-03-15 13:20:23 +0100573 return true;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100574}
575
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200576/*! vararg version of logging function
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200577 * \param[in] subsys Logging sub-system
578 * \param[in] level Log level
579 * \param[in] file name of source code file
580 * \param[in] cont continuation (1) or new line (0)
581 * \param[in] format format string
582 * \param[in] ap vararg-list containing format string arguments
583 */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200584void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200585 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800586{
Harald Welte3ae27582010-03-26 21:24:24 +0800587 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800588
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100589 subsys = map_subsys(subsys);
Harald Welteb43bc042011-06-27 10:29:17 +0200590
Pau Espin Pedrold12f6982019-09-17 18:38:58 +0200591 log_tgt_mutex_lock();
592
Harald Welte28222962011-02-18 20:37:04 +0100593 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200594 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800595
Maxc65c5b42017-03-15 13:20:23 +0100596 if (!should_log_to_target(tar, subsys, level))
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800597 continue;
598
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200599 /* According to the manpage, vsnprintf leaves the value of ap
600 * in undefined state. Since _output uses vsnprintf and it may
601 * be called several times, we have to pass a copy of ap. */
602 va_copy(bp, ap);
Harald Welted7c0a372016-12-02 13:52:59 +0100603 if (tar->raw_output)
604 tar->raw_output(tar, subsys, level, file, line, cont, format, bp);
605 else
606 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200607 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800608 }
Pau Espin Pedrold12f6982019-09-17 18:38:58 +0200609
610 log_tgt_mutex_unlock();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800611}
612
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200613/*! logging function used by DEBUGP() macro
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200614 * \param[in] subsys Logging sub-system
615 * \param[in] file name of source code file
616 * \param[in] cont continuation (1) or new line (0)
617 * \param[in] format format string
618 */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200619void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800620 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800621{
622 va_list ap;
623
624 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200625 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800626 va_end(ap);
627}
628
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200629/*! logging function used by LOGP() macro
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200630 * \param[in] subsys Logging sub-system
631 * \param[in] level Log level
632 * \param[in] file name of source code file
633 * \param[in] cont continuation (1) or new line (0)
634 * \param[in] format format string
635 */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200636void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800637{
638 va_list ap;
639
640 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200641 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800642 va_end(ap);
643}
644
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200645/*! Register a new log target with the logging core
Harald Welte18fc4652011-08-17 14:14:17 +0200646 * \param[in] target Log target to be registered
647 */
Harald Welte3ae27582010-03-26 21:24:24 +0800648void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800649{
Harald Welte28222962011-02-18 20:37:04 +0100650 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800651}
652
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200653/*! Unregister a log target from the logging core
Harald Welte18fc4652011-08-17 14:14:17 +0200654 * \param[in] target Log target to be unregistered
655 */
Harald Welte3ae27582010-03-26 21:24:24 +0800656void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800657{
658 llist_del(&target->entry);
659}
660
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200661/*! Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800662void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800663{
Harald Welte3ae27582010-03-26 21:24:24 +0800664 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800665}
666
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200667/*! Set the logging context
Harald Welte18fc4652011-08-17 14:14:17 +0200668 * \param[in] ctx_nr logging context number
669 * \param[in] value value to which the context is to be set
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200670 * \returns 0 in case of success; negative otherwise
Harald Welte18fc4652011-08-17 14:14:17 +0200671 *
672 * A logging context is something like the subscriber identity to which
673 * the currently processed message relates, or the BTS through which it
674 * was received. As soon as this data is known, it can be set using
675 * this function. The main use of context information is for logging
676 * filters.
677 */
Harald Welte3ae27582010-03-26 21:24:24 +0800678int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800679{
Harald Welte3ae27582010-03-26 21:24:24 +0800680 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800681 return -EINVAL;
682
Harald Welte3ae27582010-03-26 21:24:24 +0800683 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800684
685 return 0;
686}
687
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200688/*! Enable the \ref LOG_FLT_ALL log filter
Harald Welte18fc4652011-08-17 14:14:17 +0200689 * \param[in] target Log target to be affected
690 * \param[in] all enable (1) or disable (0) the ALL filter
691 *
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100692 * When the \ref LOG_FLT_ALL filter is enabled, all log messages will be
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +0100693 * printed. It acts as a wildcard. Setting it to \a 1 means there is no
694 * filtering.
Harald Welte18fc4652011-08-17 14:14:17 +0200695 */
Harald Welte3ae27582010-03-26 21:24:24 +0800696void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800697{
698 if (all)
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100699 target->filter_map |= (1 << LOG_FLT_ALL);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800700 else
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100701 target->filter_map &= ~(1 << LOG_FLT_ALL);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800702}
703
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200704/*! Enable or disable the use of colored output
Harald Welte18fc4652011-08-17 14:14:17 +0200705 * \param[in] target Log target to be affected
706 * \param[in] use_color Use color (1) or don't use color (0)
707 */
Harald Welte3ae27582010-03-26 21:24:24 +0800708void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800709{
710 target->use_color = use_color;
711}
712
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200713/*! Enable or disable printing of timestamps while logging
Harald Welte18fc4652011-08-17 14:14:17 +0200714 * \param[in] target Log target to be affected
715 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
716 */
Harald Welte3ae27582010-03-26 21:24:24 +0800717void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800718{
719 target->print_timestamp = print_timestamp;
720}
721
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200722/*! Enable or disable printing of extended timestamps while logging
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100723 * \param[in] target Log target to be affected
724 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
725 *
726 * When both timestamp and extended timestamp is enabled then only
727 * the extended timestamp will be used. The format of the timestamp
728 * is YYYYMMDDhhmmssnnn.
729 */
730void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp)
731{
732 target->print_ext_timestamp = print_timestamp;
733}
734
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100735/*! Use log_set_print_filename2() instead.
736 * Call log_set_print_filename2() with LOG_FILENAME_PATH or LOG_FILENAME_NONE, *as well as* call
737 * log_set_print_category_hex() with the argument passed to this function. This is to mirror legacy
738 * behavior, which combined the category in hex with the filename. For example, if the category-hex
739 * output were no longer affected by log_set_print_filename(), many unit tests (in libosmocore as well as
740 * dependent projects) would fail since they expect the category to disappear along with the filename.
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200741 * \param[in] target Log target to be affected
742 * \param[in] print_filename Enable (1) or disable (0) filenames
743 */
744void log_set_print_filename(struct log_target *target, int print_filename)
745{
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100746 log_set_print_filename2(target, print_filename ? LOG_FILENAME_PATH : LOG_FILENAME_NONE);
747 log_set_print_category_hex(target, print_filename);
748}
749
750/*! Enable or disable printing of the filename while logging.
751 * \param[in] target Log target to be affected.
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +0700752 * \param[in] lft An LOG_FILENAME_* enum value.
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100753 * LOG_FILENAME_NONE omits the source file and line information from logs.
754 * LOG_FILENAME_PATH prints the entire source file path as passed to LOGP macros.
755 */
756void log_set_print_filename2(struct log_target *target, enum log_filename_type lft)
757{
758 target->print_filename2 = lft;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200759}
760
Neels Hofmeyr77ae45d2018-08-27 20:32:36 +0200761/*! Set the position where on a log line the source file info should be logged.
762 * \param[in] target Log target to be affected.
763 * \param[in] pos A LOG_FILENAME_POS_* enum value.
764 * LOG_FILENAME_POS_DEFAULT logs just before the caller supplied log message.
765 * LOG_FILENAME_POS_LAST logs only at the end of a log line, where the caller issued an '\n' to end the
766 */
767void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos)
768{
769 target->print_filename_pos = pos;
770}
771
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200772/*! Enable or disable printing of the category name
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100773 * \param[in] target Log target to be affected
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +0700774 * \param[in] print_category Enable (1) or disable (0) filenames
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100775 *
776 * Print the category/subsys name in front of every log message.
777 */
778void log_set_print_category(struct log_target *target, int print_category)
779{
780 target->print_category = print_category;
781}
782
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100783/*! Enable or disable printing of the category number in hex ('<000b>').
784 * \param[in] target Log target to be affected.
785 * \param[in] print_category_hex Enable (1) or disable (0) hex category.
786 */
787void log_set_print_category_hex(struct log_target *target, int print_category_hex)
788{
789 target->print_category_hex = print_category_hex;
790}
791
Neels Hofmeyr886e5482018-01-16 01:49:37 +0100792/*! Enable or disable printing of the log level name.
793 * \param[in] target Log target to be affected
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +0700794 * \param[in] print_level Enable (1) or disable (0) log level name
Neels Hofmeyr886e5482018-01-16 01:49:37 +0100795 *
796 * Print the log level name in front of every log message.
797 */
798void log_set_print_level(struct log_target *target, int print_level)
799{
800 target->print_level = (bool)print_level;
801}
802
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200803/*! Set the global log level for a given log target
Harald Welte18fc4652011-08-17 14:14:17 +0200804 * \param[in] target Log target to be affected
805 * \param[in] log_level New global log level
806 */
Harald Welte3ae27582010-03-26 21:24:24 +0800807void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800808{
809 target->loglevel = log_level;
810}
811
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200812/*! Set a category filter on a given log target
Harald Weltede6e4982012-12-06 21:25:27 +0100813 * \param[in] target Log target to be affected
814 * \param[in] category Log category to be affected
815 * \param[in] enable whether to enable or disable the filter
816 * \param[in] level Log level of the filter
817 */
Harald Welte3ae27582010-03-26 21:24:24 +0800818void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800819 int enable, int level)
820{
Neels Hofmeyr886d6fd2016-12-12 13:49:03 +0100821 if (!target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800822 return;
Neels Hofmeyr886d6fd2016-12-12 13:49:03 +0100823 category = map_subsys(category);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800824 target->categories[category].enabled = !!enable;
825 target->categories[category].loglevel = level;
826}
827
Harald Welte44c0f632017-01-15 17:58:29 +0100828#if (!EMBEDDED)
Harald Welte76e72ab2011-02-17 15:52:39 +0100829static void _file_output(struct log_target *target, unsigned int level,
830 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800831{
Harald Welte0083cd32010-08-25 14:55:44 +0200832 fprintf(target->tgt_file.out, "%s", log);
833 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800834}
Harald Welte44c0f632017-01-15 17:58:29 +0100835#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800836
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200837/*! Create a new log target skeleton
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200838 * \returns dynamically-allocated log target
839 * This funcition allocates a \ref log_target and initializes it
840 * with some default values. The newly created target is not
841 * registered yet.
842 */
Harald Welte3ae27582010-03-26 21:24:24 +0800843struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800844{
Harald Welte3ae27582010-03-26 21:24:24 +0800845 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800846 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800847
Max68bf16a2018-01-10 17:00:43 +0100848 assert_loginfo(__func__);
Harald Welte18a7d812017-03-16 23:54:55 +0100849
Harald Welte3ae27582010-03-26 21:24:24 +0800850 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800851 if (!target)
852 return NULL;
853
Pau Espin Pedrol9d4a36e2018-07-26 11:55:33 +0200854 target->categories = talloc_zero_array(target,
Harald Welteb43bc042011-06-27 10:29:17 +0200855 struct log_category,
856 osmo_log_info->num_cat);
857 if (!target->categories) {
858 talloc_free(target);
859 return NULL;
860 }
861
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800862 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800863
864 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200865 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800866 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200867 cat->enabled = osmo_log_info->cat[i].enabled;
868 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800869 }
870
871 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800872 target->use_color = 1;
873 target->print_timestamp = 0;
Neels Hofmeyrbd7bd392018-01-16 01:52:29 +0100874 target->print_filename2 = LOG_FILENAME_PATH;
875 target->print_category_hex = true;
Harald Weltecc6313c2010-03-26 22:04:03 +0800876
877 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800878 target->loglevel = 0;
879 return target;
880}
881
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200882/*! Create the STDERR log target
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200883 * \returns dynamically-allocated \ref log_target for STDERR */
Harald Welte3ae27582010-03-26 21:24:24 +0800884struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800885{
Harald Weltea3b844c2010-03-27 00:04:40 +0800886/* since C89/C99 says stderr is a macro, we can safely do this! */
Harald Welteb93ce5a2017-05-15 10:58:15 +0200887#if !EMBEDDED && defined(stderr)
Harald Welte3ae27582010-03-26 21:24:24 +0800888 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800889
Harald Welte3ae27582010-03-26 21:24:24 +0800890 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800891 if (!target)
892 return NULL;
893
Harald Welte28222962011-02-18 20:37:04 +0100894 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200895 target->tgt_file.out = stderr;
896 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800897 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800898#else
899 return NULL;
900#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800901}
902
Harald Welte44c0f632017-01-15 17:58:29 +0100903#if (!EMBEDDED)
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200904/*! Create a new file-based log target
Harald Welte18fc4652011-08-17 14:14:17 +0200905 * \param[in] fname File name of the new log file
906 * \returns Log target in case of success, NULL otherwise
907 */
Harald Welte3086c392010-08-25 19:10:50 +0200908struct log_target *log_target_create_file(const char *fname)
909{
910 struct log_target *target;
911
912 target = log_target_create();
913 if (!target)
914 return NULL;
915
Harald Welte28222962011-02-18 20:37:04 +0100916 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200917 target->tgt_file.out = fopen(fname, "a");
Vadim Yanitskiyb89114b2020-09-09 04:51:04 +0700918 if (!target->tgt_file.out) {
919 log_target_destroy(target);
Harald Welte3086c392010-08-25 19:10:50 +0200920 return NULL;
Vadim Yanitskiyb89114b2020-09-09 04:51:04 +0700921 }
Harald Welte3086c392010-08-25 19:10:50 +0200922
923 target->output = _file_output;
924
925 target->tgt_file.fname = talloc_strdup(target, fname);
926
927 return target;
928}
Harald Welte44c0f632017-01-15 17:58:29 +0100929#endif
Harald Welte3086c392010-08-25 19:10:50 +0200930
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200931/*! Find a registered log target
Harald Welte18fc4652011-08-17 14:14:17 +0200932 * \param[in] type Log target type
933 * \param[in] fname File name
934 * \returns Log target (if found), NULL otherwise
Pau Espin Pedrold12f6982019-09-17 18:38:58 +0200935 * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
Harald Welte18fc4652011-08-17 14:14:17 +0200936 */
Harald Welte28222962011-02-18 20:37:04 +0100937struct log_target *log_target_find(int type, const char *fname)
938{
939 struct log_target *tgt;
940
941 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
942 if (tgt->type != type)
943 continue;
Maxc90f40a2018-01-11 10:52:28 +0100944 switch (tgt->type) {
945 case LOG_TGT_TYPE_FILE:
Harald Welte28222962011-02-18 20:37:04 +0100946 if (!strcmp(fname, tgt->tgt_file.fname))
947 return tgt;
Maxc90f40a2018-01-11 10:52:28 +0100948 break;
949 case LOG_TGT_TYPE_GSMTAP:
950 if (!strcmp(fname, tgt->tgt_gsmtap.hostname))
951 return tgt;
952 break;
953 default:
Harald Welte28222962011-02-18 20:37:04 +0100954 return tgt;
Maxc90f40a2018-01-11 10:52:28 +0100955 }
Harald Welte28222962011-02-18 20:37:04 +0100956 }
957 return NULL;
958}
959
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200960/*! Unregister, close and delete a log target
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +0700961 * \param[in] target log target to unregister, close and delete */
Harald Welte3086c392010-08-25 19:10:50 +0200962void log_target_destroy(struct log_target *target)
963{
964
965 /* just in case, to make sure we don't have any references */
966 log_del_target(target);
967
Harald Welte44c0f632017-01-15 17:58:29 +0100968#if (!EMBEDDED)
Vadim Yanitskiy744236b2020-09-09 04:42:22 +0700969 switch (target->type) {
970 case LOG_TGT_TYPE_FILE:
Vadim Yanitskiyb89114b2020-09-09 04:51:04 +0700971 if (target->tgt_file.out == NULL)
972 break;
Vadim Yanitskiy744236b2020-09-09 04:42:22 +0700973 fclose(target->tgt_file.out);
974 target->tgt_file.out = NULL;
975 break;
Vadim Yanitskiy04f42712020-09-09 04:47:25 +0700976#ifdef HAVE_SYSLOG_H
977 case LOG_TGT_TYPE_SYSLOG:
978 closelog();
979 break;
980#endif /* HAVE_SYSLOG_H */
Vadim Yanitskiy744236b2020-09-09 04:42:22 +0700981 default:
982 /* make GCC happy */
983 break;
Harald Welte3086c392010-08-25 19:10:50 +0200984 }
Harald Welte44c0f632017-01-15 17:58:29 +0100985#endif
Harald Welte3086c392010-08-25 19:10:50 +0200986
987 talloc_free(target);
988}
989
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200990/*! close and re-open a log file (for log file rotation)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200991 * \param[in] target log target to re-open
992 * \returns 0 in case of success; negative otherwise */
Harald Welte3086c392010-08-25 19:10:50 +0200993int log_target_file_reopen(struct log_target *target)
994{
995 fclose(target->tgt_file.out);
996
997 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
998 if (!target->tgt_file.out)
999 return -errno;
1000
1001 /* we assume target->output already to be set */
1002
1003 return 0;
1004}
1005
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001006/*! close and re-open all log files (for log file rotation)
Harald Welte2d2e2cc2016-04-25 12:11:20 +02001007 * \returns 0 in case of success; negative otherwise */
Harald Welte4de854d2013-03-18 19:01:40 +01001008int log_targets_reopen(void)
1009{
1010 struct log_target *tar;
1011 int rc = 0;
1012
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001013 log_tgt_mutex_lock();
1014
Harald Welte4de854d2013-03-18 19:01:40 +01001015 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
1016 switch (tar->type) {
1017 case LOG_TGT_TYPE_FILE:
1018 if (log_target_file_reopen(tar) < 0)
1019 rc = -1;
1020 break;
1021 default:
1022 break;
1023 }
1024 }
1025
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001026 log_tgt_mutex_unlock();
1027
Harald Welte4de854d2013-03-18 19:01:40 +01001028 return rc;
1029}
1030
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001031/*! Initialize the Osmocom logging core
Max72dfd432018-12-04 11:24:18 +01001032 * \param[in] inf Information regarding logging categories, could be NULL
Vadim Yanitskiy73e66b32019-03-25 21:24:20 +07001033 * \param[in] ctx talloc context for logging allocations
Harald Welte18fc4652011-08-17 14:14:17 +02001034 * \returns 0 in case of success, negative in case of error
Max72dfd432018-12-04 11:24:18 +01001035 *
1036 * If inf is NULL then only library-internal categories are initialized.
Harald Welte18fc4652011-08-17 14:14:17 +02001037 */
Harald Welteb43bc042011-06-27 10:29:17 +02001038int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +08001039{
Harald Welteb43bc042011-06-27 10:29:17 +02001040 int i;
Philipp Maierdcad1c52020-03-25 11:25:59 +01001041 struct log_info_cat *cat_ptr;
Harald Welteb43bc042011-06-27 10:29:17 +02001042
Philipp Maierdc02c062020-05-12 17:51:25 +02001043 /* Ensure that log_init is not called multiple times */
1044 OSMO_ASSERT(tall_log_ctx == NULL)
1045
Harald Welteb43bc042011-06-27 10:29:17 +02001046 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
1047 if (!tall_log_ctx)
1048 return -ENOMEM;
1049
1050 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
1051 if (!osmo_log_info)
1052 return -ENOMEM;
1053
Max72dfd432018-12-04 11:24:18 +01001054 osmo_log_info->num_cat = ARRAY_SIZE(internal_cat);
1055
1056 if (inf) {
1057 osmo_log_info->filter_fn = inf->filter_fn;
1058 osmo_log_info->num_cat_user = inf->num_cat;
1059 osmo_log_info->num_cat += inf->num_cat;
1060 }
Harald Welteb43bc042011-06-27 10:29:17 +02001061
Philipp Maierdcad1c52020-03-25 11:25:59 +01001062 cat_ptr = talloc_zero_array(osmo_log_info, struct log_info_cat,
1063 osmo_log_info->num_cat);
1064 if (!cat_ptr) {
Harald Welteb43bc042011-06-27 10:29:17 +02001065 talloc_free(osmo_log_info);
1066 osmo_log_info = NULL;
1067 return -ENOMEM;
1068 }
1069
Philipp Maierdcad1c52020-03-25 11:25:59 +01001070 /* copy over the user part and sanitize loglevel */
1071 if (inf) {
Max72dfd432018-12-04 11:24:18 +01001072 for (i = 0; i < inf->num_cat; i++) {
Philipp Maierdcad1c52020-03-25 11:25:59 +01001073 memcpy(&cat_ptr[i], &inf->cat[i],
1074 sizeof(struct log_info_cat));
1075
1076 /* Make sure that the loglevel is set to NOTICE in case
1077 * no loglevel has been preset. */
1078 if (!cat_ptr[i].loglevel) {
1079 cat_ptr[i].loglevel = LOGL_NOTICE;
1080 }
Max72dfd432018-12-04 11:24:18 +01001081 }
Harald Welteb43bc042011-06-27 10:29:17 +02001082 }
1083
1084 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +02001085 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +02001086 unsigned int cn = osmo_log_info->num_cat_user + i;
Philipp Maierdcad1c52020-03-25 11:25:59 +01001087 memcpy(&cat_ptr[cn], &internal_cat[i], sizeof(struct log_info_cat));
Harald Welte9fe16522011-06-27 14:00:03 +02001088 }
1089
Philipp Maierdcad1c52020-03-25 11:25:59 +01001090 osmo_log_info->cat = cat_ptr;
1091
Harald Welte9fe16522011-06-27 14:00:03 +02001092 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +08001093}
Harald Welte18fc4652011-08-17 14:14:17 +02001094
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001095/* De-initialize the Osmocom logging core
Harald Welte69e6c3c2016-04-20 10:41:27 +02001096 * This function destroys all targets and releases associated memory */
1097void log_fini(void)
1098{
1099 struct log_target *tar, *tar2;
1100
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001101 log_tgt_mutex_lock();
1102
Harald Welte69e6c3c2016-04-20 10:41:27 +02001103 llist_for_each_entry_safe(tar, tar2, &osmo_log_target_list, entry)
1104 log_target_destroy(tar);
1105
1106 talloc_free(osmo_log_info);
1107 osmo_log_info = NULL;
1108 talloc_free(tall_log_ctx);
1109 tall_log_ctx = NULL;
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001110
1111 log_tgt_mutex_unlock();
Harald Welte69e6c3c2016-04-20 10:41:27 +02001112}
1113
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001114/*! Check whether a log entry will be generated.
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001115 * \returns != 0 if a log entry might get generated by at least one target */
1116int log_check_level(int subsys, unsigned int level)
1117{
1118 struct log_target *tar;
1119
Max68bf16a2018-01-10 17:00:43 +01001120 assert_loginfo(__func__);
Harald Welte18a7d812017-03-16 23:54:55 +01001121
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +01001122 subsys = map_subsys(subsys);
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001123
1124 /* TODO: The following could/should be cached (update on config) */
1125
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001126 log_tgt_mutex_lock();
1127
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001128 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Maxc65c5b42017-03-15 13:20:23 +01001129 if (!should_log_to_target(tar, subsys, level))
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001130 continue;
1131
1132 /* This might get logged (ignoring filters) */
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001133 log_tgt_mutex_unlock();
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001134 return 1;
1135 }
1136
1137 /* We are sure, that this will not be logged. */
Pau Espin Pedrold12f6982019-09-17 18:38:58 +02001138 log_tgt_mutex_unlock();
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001139 return 0;
1140}
1141
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001142/*! @} */