blob: 25d0c4d550c3e917b1ccc404b2b00271ba916c21 [file] [log] [blame]
Damjan Marion07a38572018-01-21 06:44:18 -08001/*
2 * Copyright (c) 2018 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <stdbool.h>
17#include <vlib/vlib.h>
18#include <vlib/log.h>
19#include <syslog.h>
20
Damjan Marion07a38572018-01-21 06:44:18 -080021typedef struct
22{
23 vlib_log_level_t level;
24 vlib_log_class_t class;
25 f64 timestamp;
26 u8 *string;
27} vlib_log_entry_t;
28
29typedef struct
30{
31 u32 index;
32 u8 *name;
33 // level of log messages kept for this subclass
34 vlib_log_level_t level;
35 // level of log messages sent to syslog for this subclass
36 vlib_log_level_t syslog_level;
37 // flag saying whether this subclass is logged to syslog
Damjan Marion07a38572018-01-21 06:44:18 -080038 f64 last_event_timestamp;
39 int last_sec_count;
40 int is_throttling;
41 int rate_limit;
42} vlib_log_subclass_data_t;
43
44typedef struct
45{
46 u32 index;
47 u8 *name;
48 vlib_log_subclass_data_t *subclasses;
49} vlib_log_class_data_t;
50
51typedef struct
52{
53 vlib_log_entry_t *entries;
54 vlib_log_class_data_t *classes;
55 int size, next, count;
56
57 /* our own log class */
58 vlib_log_class_t log_class;
59
60 int default_rate_limit;
Damjan Marione78fab82018-04-18 17:03:28 +020061 int default_log_level;
62 int default_syslog_log_level;
Damjan Marion07a38572018-01-21 06:44:18 -080063 int unthrottle_time;
64 u32 indent;
65} vlib_log_main_t;
66
Damjan Marione78fab82018-04-18 17:03:28 +020067vlib_log_main_t log_main = {
68 .default_log_level = VLIB_LOG_LEVEL_NOTICE,
69 .default_syslog_log_level = VLIB_LOG_LEVEL_WARNING,
70 .unthrottle_time = 3,
71 .size = 512,
72 .default_rate_limit = 50,
73};
Damjan Marion07a38572018-01-21 06:44:18 -080074
75static int
76last_log_entry ()
77{
78 vlib_log_main_t *lm = &log_main;
79 int i;
80
81 i = lm->next - lm->count;
82
83 if (i < 0)
84 i += lm->size;
85 return i;
86}
87
88static vlib_log_class_data_t *
89get_class_data (vlib_log_class_t ci)
90{
91 vlib_log_main_t *lm = &log_main;
92 return vec_elt_at_index (lm->classes, (ci >> 16));
93}
94
95static vlib_log_subclass_data_t *
96get_subclass_data (vlib_log_class_t ci)
97{
98 vlib_log_class_data_t *c = get_class_data (ci);
99 return vec_elt_at_index (c->subclasses, (ci & 0xffff));
100}
101
102static int
103vlib_log_level_to_syslog_priority (vlib_log_level_t level)
104{
105 switch (level)
106 {
Damjan Marione78fab82018-04-18 17:03:28 +0200107#define LOG_DISABLED LOG_DEBUG
Damjan Marion07a38572018-01-21 06:44:18 -0800108#define _(n,uc,lc) \
109 case VLIB_LOG_LEVEL_##uc:\
110 return LOG_##uc;
111 foreach_vlib_log_level
112#undef _
Damjan Marione78fab82018-04-18 17:03:28 +0200113#undef LOG_DISABLED
Damjan Marion07a38572018-01-21 06:44:18 -0800114 }
115 return LOG_DEBUG;
116}
117
118u8 *
119format_vlib_log_class (u8 * s, va_list * args)
120{
121 vlib_log_class_t ci = va_arg (*args, vlib_log_class_t);
122 vlib_log_class_data_t *c = get_class_data (ci);
123 vlib_log_subclass_data_t *sc = get_subclass_data (ci);
124
125 if (sc->name)
126 return format (s, "%v/%v", c->name, sc->name);
127 else
128 return format (s, "%v", c->name, 0);
129}
130
131
132void
133vlib_log (vlib_log_level_t level, vlib_log_class_t class, char *fmt, ...)
134{
135 vlib_main_t *vm = vlib_get_main ();
136 vlib_log_main_t *lm = &log_main;
137 vlib_log_entry_t *e;
138 vlib_log_subclass_data_t *sc = get_subclass_data (class);
139 va_list va;
140 f64 t = vlib_time_now (vm);
141 f64 delta = t - sc->last_event_timestamp;
142 u8 *s = 0;
143 bool use_formatted_log_entry = true;
144
Mohsin Kazmi2f63bc92018-05-18 16:27:26 +0200145 vec_validate (lm->entries, lm->size);
Damjan Marion07a38572018-01-21 06:44:18 -0800146 /* make sure we are running on the main thread to avoid use in dataplane
147 code, for dataplane logging consider use of event-logger */
148 ASSERT (vlib_get_thread_index () == 0);
149
150 if (level > sc->level)
151 {
152 use_formatted_log_entry = false;
153 goto syslog;
154 }
155
156 if ((delta > lm->unthrottle_time) ||
157 (sc->is_throttling == 0 && (delta > 1)))
158 {
159 sc->last_event_timestamp = t;
160 sc->last_sec_count = 0;
161 sc->is_throttling = 0;
162 }
163 else
164 {
165 sc->last_sec_count++;
166 if (sc->last_sec_count > sc->rate_limit)
167 return;
168 else if (sc->last_sec_count == sc->rate_limit)
169 {
170 vec_reset_length (s);
171 s = format (0, "--- message(s) throttled ---");
172 sc->is_throttling = 1;
173 }
174 }
175
176 if (s == 0)
177 {
178 va_start (va, fmt);
179 s = va_format (s, fmt, &va);
180 va_end (va);
181 }
182
183 e = vec_elt_at_index (lm->entries, lm->next);
184 vec_free (e->string);
185 e->level = level;
186 e->class = class;
187 e->string = s;
188 e->timestamp = t;
189
190 lm->next = (lm->next + 1) % lm->size;
191 if (lm->size > lm->count)
192 lm->count++;
193
194syslog:
Damjan Marione78fab82018-04-18 17:03:28 +0200195 if (sc->syslog_level != VLIB_LOG_LEVEL_DISABLED &&
196 level <= sc->syslog_level)
Damjan Marion07a38572018-01-21 06:44:18 -0800197 {
198 u8 *tmp = format (NULL, "%U", format_vlib_log_class, class);
199 if (use_formatted_log_entry)
200 {
201 syslog (vlib_log_level_to_syslog_priority (level), "%.*s: %.*s",
202 vec_len (tmp), tmp,
203 vec_len (s) - (vec_c_string_is_terminated (s) ? 1 : 0), s);
204 }
205 else
206 {
207 tmp = format (tmp, ": ");
208 va_start (va, fmt);
209 tmp = va_format (tmp, fmt, &va);
210 va_end (va);
211 syslog (vlib_log_level_to_syslog_priority (level), "%.*s",
212 vec_len (tmp) - (vec_c_string_is_terminated (tmp) ? 1 : 0),
213 tmp);
214 }
215 vec_free (tmp);
216 }
217
218}
219
220vlib_log_class_t
221vlib_log_register_class (char *class, char *subclass)
222{
223 vlib_log_main_t *lm = &log_main;
224 vlib_log_class_data_t *c = NULL;
225 vlib_log_subclass_data_t *s;
226 vlib_log_class_data_t *tmp;
227 vec_foreach (tmp, lm->classes)
228 {
229 if (!memcmp (class, tmp->name, vec_len (tmp->name)))
230 {
231 c = tmp;
232 break;
233 }
234 }
235 if (!c)
236 {
237 vec_add2 (lm->classes, c, 1);
238 c->index = c - lm->classes;
239 c->name = format (0, "%s", class);
240 }
241
242 vec_add2 (c->subclasses, s, 1);
243 s->index = s - c->subclasses;
244 s->name = subclass ? format (0, "%s", subclass) : 0;
245 s->rate_limit = lm->default_rate_limit;
Damjan Marione78fab82018-04-18 17:03:28 +0200246 s->level = lm->default_log_level;
247 s->syslog_level = lm->default_syslog_log_level;
Damjan Marion07a38572018-01-21 06:44:18 -0800248 return (c->index << 16) | (s->index);
249}
250
251u8 *
252format_vlib_log_level (u8 * s, va_list * args)
253{
254 vlib_log_level_t i = va_arg (*args, vlib_log_level_t);
255 char *t = 0;
256
257 switch (i)
258 {
259#define _(v,uc,lc) case VLIB_LOG_LEVEL_##uc: t = #lc; break;
260 foreach_vlib_log_level
261#undef _
262 default:
263 return format (s, "unknown");
264 }
265 return format (s, "%s", t);
266}
267
268u32
269vlib_log_get_indent ()
270{
271 return log_main.indent;
272}
273
274static clib_error_t *
275vlib_log_init (vlib_main_t * vm)
276{
277 vlib_log_main_t *lm = &log_main;
Damjan Marion07a38572018-01-21 06:44:18 -0800278 vec_validate (lm->entries, lm->size);
279 lm->log_class = vlib_log_register_class ("log", 0);
280 u8 *tmp = format (NULL, "%U %-10U %-10U ", format_time_float, 0, (f64) 0,
281 format_white_space, 255, format_white_space, 255);
282 log_main.indent = vec_len (tmp);
283 vec_free (tmp);
284 return 0;
285}
286
287VLIB_INIT_FUNCTION (vlib_log_init);
288
289
290static clib_error_t *
291show_log (vlib_main_t * vm,
292 unformat_input_t * input, vlib_cli_command_t * cmd)
293{
294 clib_error_t *error = 0;
295 vlib_log_main_t *lm = &log_main;
296 vlib_log_entry_t *e;
297 int i = last_log_entry ();
298 int count = lm->count;
299
300 while (count--)
301 {
302 e = vec_elt_at_index (lm->entries, i);
Damjan Marione78fab82018-04-18 17:03:28 +0200303 vlib_cli_output (vm, "%U %-10U %-10U %v",
Damjan Marion07a38572018-01-21 06:44:18 -0800304 format_time_float, 0, e->timestamp,
305 format_vlib_log_level, e->level,
Damjan Marione78fab82018-04-18 17:03:28 +0200306 format_vlib_log_class, e->class, e->string);
Damjan Marion07a38572018-01-21 06:44:18 -0800307 i = (i + 1) % lm->size;
308 }
309
310 return error;
311}
312
313/* *INDENT-OFF* */
314VLIB_CLI_COMMAND (cli_show_log, static) = {
315 .path = "show logging",
316 .short_help = "show logging",
317 .function = show_log,
318};
319/* *INDENT-ON* */
320
321static clib_error_t *
Damjan Marione78fab82018-04-18 17:03:28 +0200322show_log_config (vlib_main_t * vm,
323 unformat_input_t * input, vlib_cli_command_t * cmd)
324{
325 clib_error_t *error = 0;
326 vlib_log_main_t *lm = &log_main;
327 vlib_log_class_data_t *c;
328 vlib_log_subclass_data_t *sc;
329
330 vlib_cli_output (vm, "%-20s %u entries", "Buffer Size:", lm->size);
331 vlib_cli_output (vm, "Defaults:\n");
332 vlib_cli_output (vm, "%-20s %U", " Log Level:",
333 format_vlib_log_level, lm->default_log_level);
334 vlib_cli_output (vm, "%-20s %U", " Syslog Log Level:",
335 format_vlib_log_level, lm->default_syslog_log_level);
336 vlib_cli_output (vm, "%-20s %u msgs/sec", " Rate Limit:",
337 lm->default_rate_limit);
338 vlib_cli_output (vm, "\n");
339 vlib_cli_output (vm, "%-22s %-14s %-14s %s",
340 "Class/Subclass", "Level", "Syslog Level", "Rate Limit");
341
342 vec_foreach (c, lm->classes)
343 {
344 vlib_cli_output (vm, "%s", c->name);
345 vec_foreach (sc, c->subclasses)
346 {
347 vlib_cli_output (vm, " %-20s %-14U %-14U %d",
348 sc->name ? (char *) sc->name : "default",
349 format_vlib_log_level, sc->level,
350 format_vlib_log_level, sc->syslog_level,
351 sc->rate_limit);
352 }
353 }
354
355 return error;
356}
357
358/* *INDENT-OFF* */
359VLIB_CLI_COMMAND (cli_show_log_config, static) = {
360 .path = "show logging configuration",
361 .short_help = "show logging configuration",
362 .function = show_log_config,
363};
364/* *INDENT-ON* */
365
366static clib_error_t *
Damjan Marion07a38572018-01-21 06:44:18 -0800367clear_log (vlib_main_t * vm,
368 unformat_input_t * input, vlib_cli_command_t * cmd)
369{
370 clib_error_t *error = 0;
371 vlib_log_main_t *lm = &log_main;
372 vlib_log_entry_t *e;
373 int i = last_log_entry ();
374 int count = lm->count;
375
376 while (count--)
377 {
378 e = vec_elt_at_index (lm->entries, i);
379 vec_free (e->string);
380 i = (i + 1) % lm->size;
381 }
382
383 lm->count = 0;
384 lm->next = 0;
385 vlib_log_info (lm->log_class, "log cleared");
386 return error;
387}
388
389/* *INDENT-OFF* */
390VLIB_CLI_COMMAND (cli_clear_log, static) = {
391 .path = "clear logging",
392 .short_help = "clear logging",
393 .function = clear_log,
394};
395/* *INDENT-ON* */
396
397static uword
398unformat_vlib_log_level (unformat_input_t * input, va_list * args)
399{
400 vlib_log_level_t *level = va_arg (*args, vlib_log_level_t *);
401 u8 *level_str = NULL;
402 uword rv = 1;
Steven526ea3e2018-04-25 11:29:00 -0700403 if (unformat (input, "%s", &level_str))
Damjan Marion07a38572018-01-21 06:44:18 -0800404 {
405#define _(v, uc, lc) \
406 const char __##uc[] = #lc; \
Steven526ea3e2018-04-25 11:29:00 -0700407 if (!strcmp ((const char *) level_str, __##uc)) \
Damjan Marion07a38572018-01-21 06:44:18 -0800408 { \
409 *level = VLIB_LOG_LEVEL_##uc; \
410 rv = 1; \
411 goto done; \
412 }
413 foreach_vlib_log_level;
414 rv = 0;
415#undef _
416 }
417done:
418 vec_free (level_str);
419 return rv;
420}
421
422static uword
423unformat_vlib_log_class (unformat_input_t * input, va_list * args)
424{
425 vlib_log_class_data_t **class = va_arg (*args, vlib_log_class_data_t **);
426 uword rv = 0;
427 u8 *class_str = NULL;
428 vlib_log_main_t *lm = &log_main;
429 if (unformat (input, "%v", &class_str))
430 {
431 vlib_log_class_data_t *cdata;
432 vec_foreach (cdata, lm->classes)
433 {
434 if (vec_is_equal (cdata->name, class_str))
435 {
436 *class = cdata;
437 rv = 1;
438 break;
439 }
440 }
441 }
442 vec_free (class_str);
443 return rv;
444}
445
446static clib_error_t *
447set_log_class (vlib_main_t * vm,
448 unformat_input_t * input, vlib_cli_command_t * cmd)
449{
450 unformat_input_t _line_input, *line_input = &_line_input;
451 clib_error_t *rv = NULL;
452 int rate_limit;
453 bool set_rate_limit = false;
454 bool set_level = false;
455 bool set_syslog_level = false;
456 vlib_log_level_t level;
457 vlib_log_level_t syslog_level;
458
459 /* Get a line of input. */
460 if (!unformat_user (input, unformat_line_input, line_input))
461 return 0;
462
463 vlib_log_class_data_t *class = NULL;
464 if (!unformat (line_input, "%U", unformat_vlib_log_class, &class))
465 {
466 return clib_error_return (0, "unknown log class `%U'",
467 format_unformat_error, line_input);
468 }
469 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
470 {
471 if (unformat (line_input, "rate-limit %d", &rate_limit))
472 {
473 set_rate_limit = true;
474 }
475 else
476 if (unformat
477 (line_input, "level %U", unformat_vlib_log_level, &level))
478 {
479 set_level = true;
480 }
481 else
482 if (unformat
483 (line_input, "syslog-level %U", unformat_vlib_log_level,
484 &syslog_level))
485 {
486 set_syslog_level = true;
487 }
488 else
489 {
490 return clib_error_return (0, "unknown input `%U'",
491 format_unformat_error, line_input);
492 }
493 }
494
495 if (set_level)
496 {
497 vlib_log_subclass_data_t *subclass;
498 vec_foreach (subclass, class->subclasses)
499 {
500 subclass->level = level;
501 }
502 }
503 if (set_syslog_level)
504 {
505 vlib_log_subclass_data_t *subclass;
506 vec_foreach (subclass, class->subclasses)
507 {
508 subclass->syslog_level = syslog_level;
Damjan Marion07a38572018-01-21 06:44:18 -0800509 }
510 }
511 if (set_rate_limit)
512 {
513 vlib_log_subclass_data_t *subclass;
514 vec_foreach (subclass, class->subclasses)
515 {
516 subclass->rate_limit = rate_limit;
517 }
518 }
519
520 return rv;
521}
522
523/* *INDENT-OFF* */
524VLIB_CLI_COMMAND (cli_set_log, static) = {
525 .path = "set logging class",
526 .short_help = "set loggging class <class> [rate-limit <int>] "
527 "[level <level>] [syslog-level <level>]",
528 .function = set_log_class,
529};
530/* *INDENT-ON* */
531
532static clib_error_t *
533set_log_unth_time (vlib_main_t * vm,
534 unformat_input_t * input, vlib_cli_command_t * cmd)
535{
536 unformat_input_t _line_input, *line_input = &_line_input;
537 clib_error_t *rv = NULL;
538 int unthrottle_time;
539 vlib_log_main_t *lm = &log_main;
540
541 /* Get a line of input. */
542 if (!unformat_user (input, unformat_line_input, line_input))
543 return 0;
544
545 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
546 {
547 if (unformat (line_input, "%d", &unthrottle_time))
548 lm->unthrottle_time = unthrottle_time;
549 else
550 return clib_error_return (0, "unknown input `%U'",
551 format_unformat_error, line_input);
552 }
553
554 return rv;
555}
556
557/* *INDENT-OFF* */
558VLIB_CLI_COMMAND (cli_set_log_params, static) = {
559 .path = "set logging unthrottle-time",
560 .short_help = "set logging unthrottle-time <int>",
561 .function = set_log_unth_time,
562};
563/* *INDENT-ON* */
564
565static clib_error_t *
566set_log_size (vlib_main_t * vm,
567 unformat_input_t * input, vlib_cli_command_t * cmd)
568{
569 unformat_input_t _line_input, *line_input = &_line_input;
570 clib_error_t *rv = NULL;
571 int size;
572 vlib_log_main_t *lm = &log_main;
573
574 /* Get a line of input. */
575 if (!unformat_user (input, unformat_line_input, line_input))
576 return 0;
577
578 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
579 {
580 if (unformat (line_input, "%d", &size))
581 {
582 lm->size = size;
583 vec_validate (lm->entries, lm->size);
584 }
585 else
586 return clib_error_return (0, "unknown input `%U'",
587 format_unformat_error, line_input);
588 }
589
590 return rv;
591}
592
593/* *INDENT-OFF* */
594VLIB_CLI_COMMAND (cli_set_log_size, static) = {
595 .path = "set logging size",
596 .short_help = "set logging size <int>",
597 .function = set_log_size,
598};
599/* *INDENT-ON* */
600
601static uword
602unformat_vlib_log_subclass (unformat_input_t * input, va_list * args)
603{
604 vlib_log_class_data_t *class = va_arg (*args, vlib_log_class_data_t *);
605 vlib_log_subclass_data_t **subclass =
606 va_arg (*args, vlib_log_subclass_data_t **);
607 uword rv = 0;
608 u8 *subclass_str = NULL;
609 if (unformat (input, "%v", &subclass_str))
610 {
611 vlib_log_subclass_data_t *scdata;
612 vec_foreach (scdata, class->subclasses)
613 {
614 if (vec_is_equal (scdata->name, subclass_str))
615 {
616 rv = 1;
617 *subclass = scdata;
618 break;
619 }
620 }
621 }
622 vec_free (subclass_str);
623 return rv;
624}
625
626static clib_error_t *
627test_log_class_subclass (vlib_main_t * vm,
628 unformat_input_t * input, vlib_cli_command_t * cmd)
629{
630 unformat_input_t _line_input, *line_input = &_line_input;
631 /* Get a line of input. */
632 if (!unformat_user (input, unformat_line_input, line_input))
633 return 0;
634
635 vlib_log_class_data_t *class = NULL;
636 vlib_log_subclass_data_t *subclass = NULL;
637 vlib_log_level_t level;
638 if (unformat (line_input, "%U", unformat_vlib_log_level, &level))
639 {
640 if (unformat (line_input, "%U", unformat_vlib_log_class, &class))
641 {
642 if (unformat
643 (line_input, "%U", unformat_vlib_log_subclass, class,
644 &subclass))
645 {
646 vlib_log (level,
647 (class->index << 16) | (subclass->index), "%U",
648 format_unformat_input, line_input);
649 }
650 else
651 {
652 return clib_error_return (0,
653 "unknown log subclass near beginning of `%U'",
654 format_unformat_error, line_input);
655 }
656 }
657 else
658 {
659 return clib_error_return (0,
660 "unknown log class near beginning of `%U'",
661 format_unformat_error, line_input);
662 }
663 }
664 else
665 {
666 return clib_error_return (0, "unknown log level near beginning of `%U'",
667 format_unformat_error, line_input);
668 }
669 return 0;
670}
671
672/* *INDENT-OFF* */
673VLIB_CLI_COMMAND (cli_test_log, static) = {
674 .path = "test log",
675 .short_help = "test log <class> <subclass> <level> <message",
676 .function = test_log_class_subclass,
677};
678/* *INDENT-ON* */
679
680static clib_error_t *
681log_config (vlib_main_t * vm, unformat_input_t * input)
682{
683 vlib_log_main_t *lm = &log_main;
Damjan Marion07a38572018-01-21 06:44:18 -0800684
685 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
686 {
Damjan Marione78fab82018-04-18 17:03:28 +0200687 if (unformat (input, "size %d", &lm->size))
688 vec_validate (lm->entries, lm->size);
Damjan Marion07a38572018-01-21 06:44:18 -0800689 else if (unformat (input, "unthrottle-time %d", &lm->unthrottle_time))
Damjan Marione78fab82018-04-18 17:03:28 +0200690 ;
691 else if (unformat (input, "default-log-level %U",
692 unformat_vlib_log_level, &lm->default_log_level))
693 ;
694 else if (unformat (input, "default-syslog-log-level %U",
695 unformat_vlib_log_level,
696 &lm->default_syslog_log_level))
697 ;
Damjan Marion07a38572018-01-21 06:44:18 -0800698 else
699 {
700 return unformat_parse_error (input);
701 }
702 }
703
704 return 0;
705}
706
707VLIB_EARLY_CONFIG_FUNCTION (log_config, "logging");
708
709/*
710 * fd.io coding-style-patch-verification: ON
711 *
712 * Local Variables:
713 * eval: (c-set-style "gnu")
714 * End:
715 */