1 /* Debugging/Logging support code */
3 /* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
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.
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.
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.
31 #include <osmocore/talloc.h>
32 #include <osmocore/utils.h>
33 #include <osmocore/logging.h>
35 static const struct log_info
*log_info
;
37 static struct log_context log_context
;
38 static void *tall_log_ctx
= NULL
;
39 static LLIST_HEAD(target_list
);
41 static const struct value_string loglevel_strs
[] = {
43 { LOGL_DEBUG
, "DEBUG" },
44 { LOGL_INFO
, "INFO" },
45 { LOGL_NOTICE
, "NOTICE" },
46 { LOGL_ERROR
, "ERROR" },
47 { LOGL_FATAL
, "FATAL" },
51 int log_parse_level(const char *lvl
)
53 return get_string_value(loglevel_strs
, lvl
);
56 int log_parse_category(const char *category
)
60 for (i
= 0; i
< log_info
->num_cat
; ++i
) {
61 if (!strcasecmp(log_info
->cat
[i
].name
+1, category
))
69 * Parse the category mask.
70 * The format can be this: category1:category2:category3
71 * or category1,2:category2,3:...
73 void log_parse_category_mask(struct log_target
* target
, const char *_mask
)
76 char *mask
= strdup(_mask
);
77 char *category_token
= NULL
;
79 /* Disable everything to enable it afterwards */
80 for (i
= 0; i
< ARRAY_SIZE(target
->categories
); ++i
)
81 target
->categories
[i
].enabled
= 0;
83 category_token
= strtok(mask
, ":");
85 for (i
= 0; i
< log_info
->num_cat
; ++i
) {
86 char* colon
= strstr(category_token
, ",");
87 int length
= strlen(category_token
);
90 length
= colon
- category_token
;
92 if (strncasecmp(log_info
->cat
[i
].name
, category_token
,
97 level
= atoi(colon
+1);
99 target
->categories
[i
].enabled
= 1;
100 target
->categories
[i
].loglevel
= level
;
103 } while ((category_token
= strtok(NULL
, ":")));
108 static const char* color(int subsys
)
110 if (subsys
< log_info
->num_cat
)
111 return log_info
->cat
[subsys
].color
;
116 static void _output(struct log_target
*target
, unsigned int subsys
,
117 char *file
, int line
, int cont
, const char *format
,
126 /* prepare the data */
132 /* are we using color */
133 if (target
->use_color
) {
134 const char *c
= color(subsys
);
136 snprintf(col
, sizeof(col
), "%s", color(subsys
));
137 col
[sizeof(col
)-1] = '\0';
140 vsnprintf(buf
, sizeof(buf
), format
, ap
);
141 buf
[sizeof(buf
)-1] = '\0';
144 if (target
->print_timestamp
) {
148 timestr
= ctime(&tm
);
149 timestr
[strlen(timestr
)-1] = '\0';
150 snprintf(tim
, sizeof(tim
), "%s ", timestr
);
151 tim
[sizeof(tim
)-1] = '\0';
153 snprintf(sub
, sizeof(sub
), "<%4.4x> %s:%d ", subsys
, file
, line
);
154 sub
[sizeof(sub
)-1] = '\0';
157 snprintf(final
, sizeof(final
), "%s%s%s%s\033[0;m", col
, tim
, sub
, buf
);
158 final
[sizeof(final
)-1] = '\0';
159 target
->output(target
, final
);
163 static void _logp(unsigned int subsys
, int level
, char *file
, int line
,
164 int cont
, const char *format
, va_list ap
)
166 struct log_target
*tar
;
168 llist_for_each_entry(tar
, &target_list
, entry
) {
169 struct log_category
*category
;
172 category
= &tar
->categories
[subsys
];
173 /* subsystem is not supposed to be logged */
174 if (!category
->enabled
)
177 /* Check the global log level */
178 if (tar
->loglevel
!= 0 && level
< tar
->loglevel
)
181 /* Check the category log level */
182 if (tar
->loglevel
== 0 && category
->loglevel
!= 0 &&
183 level
< category
->loglevel
)
186 /* Apply filters here... if that becomes messy we will
187 * need to put filters in a list and each filter will
188 * say stop, continue, output */
189 if ((tar
->filter_map
& LOG_FILTER_ALL
) != 0)
191 else if (log_info
->filter_fn
)
192 output
= log_info
->filter_fn(&log_context
,
196 /* FIXME: copying the va_list is an ugly
197 * workaround against a bug hidden somewhere in
198 * _output. If we do not copy here, the first
199 * call to _output() will corrupt the va_list
200 * contents, and any further _output() calls
201 * with the same va_list will segfault */
204 _output(tar
, subsys
, file
, line
, cont
, format
, bp
);
210 void logp(unsigned int subsys
, char *file
, int line
, int cont
,
211 const char *format
, ...)
215 va_start(ap
, format
);
216 _logp(subsys
, LOGL_DEBUG
, file
, line
, cont
, format
, ap
);
220 void logp2(unsigned int subsys
, unsigned int level
, char *file
, int line
, int cont
, const char *format
, ...)
224 va_start(ap
, format
);
225 _logp(subsys
, level
, file
, line
, cont
, format
, ap
);
229 static char hexd_buff
[4096];
231 char *hexdump(const unsigned char *buf
, int len
)
234 char *cur
= hexd_buff
;
237 for (i
= 0; i
< len
; i
++) {
238 int len_remain
= sizeof(hexd_buff
) - (cur
- hexd_buff
);
239 int rc
= snprintf(cur
, len_remain
, "%02x ", buf
[i
]);
244 hexd_buff
[sizeof(hexd_buff
)-1] = 0;
248 void log_add_target(struct log_target
*target
)
250 llist_add_tail(&target
->entry
, &target_list
);
253 void log_del_target(struct log_target
*target
)
255 llist_del(&target
->entry
);
258 void log_reset_context(void)
260 memset(&log_context
, 0, sizeof(log_context
));
263 int log_set_context(uint8_t ctx_nr
, void *value
)
265 if (ctx_nr
> LOG_MAX_CTX
)
268 log_context
.ctx
[ctx_nr
] = value
;
273 void log_set_all_filter(struct log_target
*target
, int all
)
276 target
->filter_map
|= LOG_FILTER_ALL
;
278 target
->filter_map
&= ~LOG_FILTER_ALL
;
281 void log_set_use_color(struct log_target
*target
, int use_color
)
283 target
->use_color
= use_color
;
286 void log_set_print_timestamp(struct log_target
*target
, int print_timestamp
)
288 target
->print_timestamp
= print_timestamp
;
291 void log_set_log_level(struct log_target
*target
, int log_level
)
293 target
->loglevel
= log_level
;
296 void log_set_category_filter(struct log_target
*target
, int category
,
297 int enable
, int level
)
299 if (category
>= log_info
->num_cat
)
301 target
->categories
[category
].enabled
= !!enable
;
302 target
->categories
[category
].loglevel
= level
;
305 static void _stderr_output(struct log_target
*target
, const char *log
)
307 fprintf(target
->tgt_stdout
.out
, "%s", log
);
308 fflush(target
->tgt_stdout
.out
);
311 struct log_target
*log_target_create(void)
313 struct log_target
*target
;
316 target
= talloc_zero(tall_log_ctx
, struct log_target
);
320 INIT_LLIST_HEAD(&target
->entry
);
322 /* initialize the per-category enabled/loglevel from defaults */
323 for (i
= 0; i
< log_info
->num_cat
; i
++) {
324 struct log_category
*cat
= &target
->categories
[i
];
325 cat
->enabled
= log_info
->cat
[i
].enabled
;
326 cat
->loglevel
= log_info
->cat
[i
].loglevel
;
329 /* global settings */
330 target
->use_color
= 1;
331 target
->print_timestamp
= 0;
333 /* global log level */
334 target
->loglevel
= 0;
338 struct log_target
*log_target_create_stderr(void)
340 struct log_target
*target
;
342 target
= log_target_create();
346 target
->tgt_stdout
.out
= stderr
;
347 target
->output
= _stderr_output
;
351 void log_init(const struct log_info
*cat
)
353 tall_log_ctx
= talloc_named_const(NULL
, 1, "logging");