vlib: add VLIB_REGISTER_LOG_CLASS macro
[vpp.git] / src / vlib / log.c
1 /*
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 <vlib/unix/unix.h>
20 #include <syslog.h>
21
22 vlib_log_main_t log_main = {
23   .default_log_level = VLIB_LOG_LEVEL_NOTICE,
24   .default_syslog_log_level = VLIB_LOG_LEVEL_WARNING,
25   .unthrottle_time = 3,
26   .size = 512,
27   .default_rate_limit = 50,
28 };
29
30 /* *INDENT-OFF* */
31 VLIB_REGISTER_LOG_CLASS (log_log, static) = {
32   .class_name = "log",
33 };
34 /* *INDENT-ON* */
35
36 static const int colors[] = {
37   [VLIB_LOG_LEVEL_EMERG] = 1,   /* red */
38   [VLIB_LOG_LEVEL_ALERT] = 1,   /* red */
39   [VLIB_LOG_LEVEL_CRIT] = 1,    /* red */
40   [VLIB_LOG_LEVEL_ERR] = 1,     /* red */
41   [VLIB_LOG_LEVEL_WARNING] = 3, /* yellow */
42   [VLIB_LOG_LEVEL_NOTICE] = 2,  /* green */
43   [VLIB_LOG_LEVEL_INFO] = 4,    /* blue */
44   [VLIB_LOG_LEVEL_DEBUG] = 6,   /* cyan */
45 };
46
47 static const int log_level_to_syslog_priority[] = {
48   [VLIB_LOG_LEVEL_EMERG] = LOG_EMERG,
49   [VLIB_LOG_LEVEL_ALERT] = LOG_ALERT,
50   [VLIB_LOG_LEVEL_CRIT] = LOG_CRIT,
51   [VLIB_LOG_LEVEL_ERR] = LOG_ERR,
52   [VLIB_LOG_LEVEL_WARNING] = LOG_WARNING,
53   [VLIB_LOG_LEVEL_NOTICE] = LOG_NOTICE,
54   [VLIB_LOG_LEVEL_INFO] = LOG_INFO,
55   [VLIB_LOG_LEVEL_DEBUG] = LOG_DEBUG,
56   [VLIB_LOG_LEVEL_DISABLED] = LOG_DEBUG,
57 };
58
59 int
60 last_log_entry ()
61 {
62   vlib_log_main_t *lm = &log_main;
63   int i;
64
65   i = lm->next - lm->count;
66
67   if (i < 0)
68     i += lm->size;
69   return i;
70 }
71
72 static vlib_log_class_data_t *
73 get_class_data (vlib_log_class_t ci)
74 {
75   vlib_log_main_t *lm = &log_main;
76   return vec_elt_at_index (lm->classes, (ci >> 16));
77 }
78
79 static vlib_log_subclass_data_t *
80 get_subclass_data (vlib_log_class_t ci)
81 {
82   vlib_log_class_data_t *c = get_class_data (ci);
83   return vec_elt_at_index (c->subclasses, (ci & 0xffff));
84 }
85
86 u8 *
87 format_vlib_log_class (u8 * s, va_list * args)
88 {
89   vlib_log_class_t ci = va_arg (*args, vlib_log_class_t);
90   vlib_log_class_data_t *c = get_class_data (ci);
91   vlib_log_subclass_data_t *sc = get_subclass_data (ci);
92
93   if (sc->name)
94     return format (s, "%v/%v", c->name, sc->name);
95   else
96     return format (s, "%v", c->name, 0);
97 }
98
99 u8 *
100 format_indent (u8 * s, va_list * args)
101 {
102   u8 *v = va_arg (*args, u8 *);
103   u32 indent = va_arg (*args, u32);
104   u8 *c;
105
106   /* *INDENT-OFF* */
107   vec_foreach (c, v)
108     {
109       vec_add (s, c, 1);
110       if (c[0] == '\n')
111         for (u32 i = 0; i < indent; i++)
112           vec_add1 (s, (u8) ' ');
113     }
114   /* *INDENT-ON* */
115   return s;
116 }
117
118 static int
119 log_level_is_enabled (vlib_log_level_t level, vlib_log_level_t configured)
120 {
121   if (configured == VLIB_LOG_LEVEL_DISABLED)
122     return 0;
123   if (level > configured)
124     return 0;
125   return 1;
126 }
127
128 void
129 vlib_log (vlib_log_level_t level, vlib_log_class_t class, char *fmt, ...)
130 {
131   vlib_main_t *vm = vlib_get_main ();
132   vlib_log_main_t *lm = &log_main;
133   vlib_log_entry_t *e;
134   vlib_log_subclass_data_t *sc = get_subclass_data (class);
135   va_list va;
136   f64 t = vlib_time_now (vm);
137   f64 delta = t - sc->last_event_timestamp;
138   int log_enabled = log_level_is_enabled (level, sc->level);
139   int syslog_enabled = log_level_is_enabled (level, sc->syslog_level);
140   u8 *s = 0;
141
142   /* make sure we are running on the main thread to avoid use in dataplane
143      code, for dataplane logging consider use of event-logger */
144   ASSERT (vlib_get_thread_index () == 0);
145
146   if ((log_enabled || syslog_enabled) == 0)
147     return;
148
149   vec_validate (lm->entries, lm->size);
150
151   if ((delta > lm->unthrottle_time) ||
152       (sc->is_throttling == 0 && (delta > 1)))
153     {
154       sc->last_event_timestamp = t;
155       sc->last_sec_count = 0;
156       sc->is_throttling = 0;
157     }
158   else
159     {
160       sc->last_sec_count++;
161       if (sc->last_sec_count > sc->rate_limit)
162         return;
163       else if (sc->last_sec_count == sc->rate_limit)
164         {
165           vec_reset_length (s);
166           s = format (s, "--- message(s) throttled ---");
167           sc->is_throttling = 1;
168         }
169     }
170
171   if (s == 0)
172     {
173       va_start (va, fmt);
174       s = va_format (s, fmt, &va);
175       va_end (va);
176     }
177
178   if (syslog_enabled)
179     {
180       u8 *l = 0;
181       if (unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG))
182         {
183           int indent = 0;
184           int with_colors = (unix_main.flags & UNIX_FLAG_NOCOLOR) == 0;
185           u8 *fmt;
186           if (with_colors)
187             {
188               l = format (l, "\x1b[%um", 90 + colors[level]);
189               indent = vec_len (l);
190             }
191           fmt = format (0, "%%-%uU [%%-6U]: ", lm->max_class_name_length);
192           vec_terminate_c_string (fmt);
193           l = format (l, (char *) fmt, format_vlib_log_class, class,
194                       format_vlib_log_level, level);
195           vec_free (fmt);
196           indent = vec_len (l) - indent;
197           if (with_colors)
198             l = format (l, "\x1b[0m");
199           l = format (l, "%U", format_indent, s, indent);
200           fformat (stderr, "%v\n", l);
201           fflush (stderr);
202         }
203       else
204         {
205           l = format (l, "%U", format_vlib_log_class, class);
206           int prio = log_level_to_syslog_priority[level];
207           int is_term = vec_c_string_is_terminated (l) ? 1 : 0;
208
209           syslog (prio, "%.*s: %.*s", (int) vec_len (l), l,
210                   (int) vec_len (s) - is_term, s);
211         }
212       vec_free (l);
213     }
214
215   if (log_enabled)
216     {
217       e = vec_elt_at_index (lm->entries, lm->next);
218       vec_free (e->string);
219       e->level = level;
220       e->class = class;
221       e->string = s;
222       e->timestamp = t;
223       s = 0;
224
225       lm->next = (lm->next + 1) % lm->size;
226       if (lm->size > lm->count)
227         lm->count++;
228     }
229
230   vec_free (s);
231 }
232
233 static vlib_log_class_t
234 vlib_log_register_class_internal (char *class, char *subclass, u32 limit)
235 {
236   vlib_log_main_t *lm = &log_main;
237   vlib_log_class_data_t *c = NULL;
238   vlib_log_subclass_data_t *s;
239   vlib_log_class_data_t *tmp;
240   vlib_log_class_config_t *cc = 0, *scc = 0;
241   uword *p;
242   u8 *str;
243   u32 length = 0;
244
245   if ((p = hash_get_mem (lm->config_index_by_name, class)))
246     cc = vec_elt_at_index (lm->configs, p[0]);
247
248   str = format (0, "%s/%s%c", class, subclass, 0);
249   if ((p = hash_get_mem (lm->config_index_by_name, (char *) str)))
250     scc = vec_elt_at_index (lm->configs, p[0]);
251   vec_free (str);
252
253   vec_foreach (tmp, lm->classes)
254   {
255     if (vec_len (tmp->name) != strlen (class))
256       continue;
257     if (!memcmp (class, tmp->name, vec_len (tmp->name)))
258       {
259         c = tmp;
260         break;
261       }
262   }
263   if (!c)
264     {
265       vec_add2 (lm->classes, c, 1);
266       c->index = c - lm->classes;
267       c->name = format (0, "%s", class);
268     }
269   length = vec_len (c->name);
270
271   vec_add2 (c->subclasses, s, 1);
272   s->index = s - c->subclasses;
273   s->name = subclass ? format (0, "%s", subclass) : 0;
274
275   if (scc && scc->rate_limit != ~0)
276     s->rate_limit = scc->rate_limit;
277   else if (cc && cc->rate_limit != ~0)
278     s->rate_limit = cc->rate_limit;
279   else if (limit)
280     s->rate_limit = limit;
281   else
282     s->rate_limit = lm->default_rate_limit;
283
284   if (scc && scc->level != ~0)
285     s->level = scc->level;
286   else if (cc && cc->level != ~0)
287     s->level = cc->level;
288   else
289     s->level = lm->default_log_level;
290
291   if (scc && scc->syslog_level != ~0)
292     s->syslog_level = scc->syslog_level;
293   else if (cc && cc->syslog_level != ~0)
294     s->syslog_level = cc->syslog_level;
295   else
296     s->syslog_level = lm->default_syslog_log_level;
297
298   if (subclass)
299     length += 1 + vec_len (s->name);
300   if (length > lm->max_class_name_length)
301     lm->max_class_name_length = length;
302   return (c->index << 16) | (s->index);
303 }
304
305 vlib_log_class_t
306 vlib_log_register_class (char *class, char *subclass)
307 {
308   return vlib_log_register_class_internal (class, subclass,
309                                            0 /* default rate limit */ );
310 }
311
312 vlib_log_class_t
313 vlib_log_register_class_rate_limit (char *class, char *subclass, u32 limit)
314 {
315   return vlib_log_register_class_internal (class, subclass, limit);
316 }
317
318
319 u8 *
320 format_vlib_log_level (u8 * s, va_list * args)
321 {
322   vlib_log_level_t i = va_arg (*args, vlib_log_level_t);
323   char *t = 0;
324
325   switch (i)
326     {
327 #define _(uc,lc) case VLIB_LOG_LEVEL_##uc: t = #lc; break;
328       foreach_vlib_log_level
329 #undef _
330     default:
331       return format (s, "unknown");
332     }
333   return format (s, "%s", t);
334 }
335
336 static clib_error_t *
337 vlib_log_init (vlib_main_t * vm)
338 {
339   vlib_log_main_t *lm = &log_main;
340   vlib_log_class_registration_t *r = lm->registrations;
341
342   gettimeofday (&lm->time_zero_timeval, 0);
343   lm->time_zero = vlib_time_now (vm);
344
345   vec_validate (lm->entries, lm->size);
346
347   while (r)
348     {
349       r->class = vlib_log_register_class (r->class_name, r->subclass_name);
350       if (r->default_level)
351         get_subclass_data (r->class)->level = r->default_level;
352       if (r->default_syslog_level)
353         get_subclass_data (r->class)->syslog_level = r->default_syslog_level;
354       r = r->next;
355     }
356
357   r = lm->registrations;
358   while (r)
359     {
360       vlib_log_debug (r->class, "initialized");
361       r = r->next;
362     }
363   return 0;
364 }
365
366 VLIB_INIT_FUNCTION (vlib_log_init);
367
368
369 static clib_error_t *
370 show_log (vlib_main_t * vm,
371           unformat_input_t * input, vlib_cli_command_t * cmd)
372 {
373   clib_error_t *error = 0;
374   vlib_log_main_t *lm = &log_main;
375   vlib_log_entry_t *e;
376   int i = last_log_entry ();
377   int count = lm->count;
378   f64 time_offset;
379
380   time_offset = (f64) lm->time_zero_timeval.tv_sec
381     + (((f64) lm->time_zero_timeval.tv_usec) * 1e-6) - lm->time_zero;
382
383   while (count--)
384     {
385       e = vec_elt_at_index (lm->entries, i);
386       vlib_cli_output (vm, "%U %-10U %-14U %v",
387                        format_time_float, 0, e->timestamp + time_offset,
388                        format_vlib_log_level, e->level,
389                        format_vlib_log_class, e->class, e->string);
390       i = (i + 1) % lm->size;
391     }
392
393   return error;
394 }
395
396 /* *INDENT-OFF* */
397 VLIB_CLI_COMMAND (cli_show_log, static) = {
398   .path = "show logging",
399   .short_help = "show logging",
400   .function = show_log,
401 };
402 /* *INDENT-ON* */
403
404 static clib_error_t *
405 show_log_config (vlib_main_t * vm,
406                  unformat_input_t * input, vlib_cli_command_t * cmd)
407 {
408   clib_error_t *error = 0;
409   vlib_log_main_t *lm = &log_main;
410   vlib_log_class_data_t *c;
411   vlib_log_subclass_data_t *sc;
412
413   vlib_cli_output (vm, "%-20s %u entries", "Buffer Size:", lm->size);
414   vlib_cli_output (vm, "Defaults:\n");
415   vlib_cli_output (vm, "%-20s %U", "  Log Level:",
416                    format_vlib_log_level, lm->default_log_level);
417   vlib_cli_output (vm, "%-20s %U", "  Syslog Log Level:",
418                    format_vlib_log_level, lm->default_syslog_log_level);
419   vlib_cli_output (vm, "%-20s %u msgs/sec", "  Rate Limit:",
420                    lm->default_rate_limit);
421   vlib_cli_output (vm, "\n");
422   vlib_cli_output (vm, "%-22s %-14s %-14s %s",
423                    "Class/Subclass", "Level", "Syslog Level", "Rate Limit");
424
425
426   u8 *defstr = format (0, "default");
427   vec_foreach (c, lm->classes)
428   {
429     vlib_cli_output (vm, "%v", c->name);
430     vec_foreach (sc, c->subclasses)
431     {
432       vlib_cli_output (vm, "  %-20v %-14U %-14U %d",
433                        sc->name ? sc->name : defstr,
434                        format_vlib_log_level, sc->level,
435                        format_vlib_log_level, sc->syslog_level,
436                        sc->rate_limit);
437     }
438   }
439   vec_free (defstr);
440
441   return error;
442 }
443
444 /* *INDENT-OFF* */
445 VLIB_CLI_COMMAND (cli_show_log_config, static) = {
446   .path = "show logging configuration",
447   .short_help = "show logging configuration",
448   .function = show_log_config,
449 };
450 /* *INDENT-ON* */
451
452 static clib_error_t *
453 clear_log (vlib_main_t * vm,
454            unformat_input_t * input, vlib_cli_command_t * cmd)
455 {
456   clib_error_t *error = 0;
457   vlib_log_main_t *lm = &log_main;
458   vlib_log_entry_t *e;
459   int i = last_log_entry ();
460   int count = lm->count;
461
462   while (count--)
463     {
464       e = vec_elt_at_index (lm->entries, i);
465       vec_free (e->string);
466       i = (i + 1) % lm->size;
467     }
468
469   lm->count = 0;
470   lm->next = 0;
471   vlib_log_info (log_log.class, "log cleared");
472   return error;
473 }
474
475 /* *INDENT-OFF* */
476 VLIB_CLI_COMMAND (cli_clear_log, static) = {
477   .path = "clear logging",
478   .short_help = "clear logging",
479   .function = clear_log,
480 };
481 /* *INDENT-ON* */
482
483 static uword
484 unformat_vlib_log_level (unformat_input_t * input, va_list * args)
485 {
486   vlib_log_level_t *level = va_arg (*args, vlib_log_level_t *);
487   u8 *level_str = NULL;
488   uword rv = 1;
489   if (unformat (input, "%s", &level_str))
490     {
491 #define _(uc, lc)                                      \
492   const char __##uc[] = #lc;                           \
493   if (!strcmp ((const char *) level_str, __##uc))      \
494     {                                                  \
495       *level = VLIB_LOG_LEVEL_##uc;                    \
496       rv = 1;                                          \
497       goto done;                                       \
498     }
499       foreach_vlib_log_level;
500       rv = 0;
501 #undef _
502     }
503 done:
504   vec_free (level_str);
505   return rv;
506 }
507
508 static uword
509 unformat_vlib_log_class (unformat_input_t * input, va_list * args)
510 {
511   vlib_log_class_data_t **class = va_arg (*args, vlib_log_class_data_t **);
512   uword rv = 0;
513   u8 *class_str = NULL;
514   vlib_log_main_t *lm = &log_main;
515   if (unformat (input, "%v", &class_str))
516     {
517       vlib_log_class_data_t *cdata;
518       vec_foreach (cdata, lm->classes)
519       {
520         if (vec_is_equal (cdata->name, class_str))
521           {
522             *class = cdata;
523             rv = 1;
524             break;
525           }
526       }
527     }
528   vec_free (class_str);
529   return rv;
530 }
531
532 static clib_error_t *
533 set_log_class (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 rate_limit;
539   bool set_rate_limit = false;
540   bool set_level = false;
541   bool set_syslog_level = false;
542   vlib_log_level_t level;
543   vlib_log_level_t syslog_level;
544
545   /* Get a line of input. */
546   if (!unformat_user (input, unformat_line_input, line_input))
547     return 0;
548
549   vlib_log_class_data_t *class = NULL;
550   if (!unformat (line_input, "%U", unformat_vlib_log_class, &class))
551     {
552       return clib_error_return (0, "unknown log class `%U'",
553                                 format_unformat_error, line_input);
554     }
555   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
556     {
557       if (unformat (line_input, "rate-limit %d", &rate_limit))
558         {
559           set_rate_limit = true;
560         }
561       else
562         if (unformat
563             (line_input, "level %U", unformat_vlib_log_level, &level))
564         {
565           set_level = true;
566         }
567       else
568         if (unformat
569             (line_input, "syslog-level %U", unformat_vlib_log_level,
570              &syslog_level))
571         {
572           set_syslog_level = true;
573         }
574       else
575         {
576           return clib_error_return (0, "unknown input `%U'",
577                                     format_unformat_error, line_input);
578         }
579     }
580
581   if (set_level)
582     {
583       vlib_log_subclass_data_t *subclass;
584       vec_foreach (subclass, class->subclasses)
585       {
586         subclass->level = level;
587       }
588     }
589   if (set_syslog_level)
590     {
591       vlib_log_subclass_data_t *subclass;
592       vec_foreach (subclass, class->subclasses)
593       {
594         subclass->syslog_level = syslog_level;
595       }
596     }
597   if (set_rate_limit)
598     {
599       vlib_log_subclass_data_t *subclass;
600       vec_foreach (subclass, class->subclasses)
601       {
602         subclass->rate_limit = rate_limit;
603       }
604     }
605
606   return rv;
607 }
608
609 /* *INDENT-OFF* */
610 VLIB_CLI_COMMAND (cli_set_log, static) = {
611   .path = "set logging class",
612   .short_help = "set logging class <class> [rate-limit <int>] "
613     "[level <level>] [syslog-level <level>]",
614   .function = set_log_class,
615 };
616 /* *INDENT-ON* */
617
618 static clib_error_t *
619 set_log_unth_time (vlib_main_t * vm,
620                    unformat_input_t * input, vlib_cli_command_t * cmd)
621 {
622   unformat_input_t _line_input, *line_input = &_line_input;
623   clib_error_t *rv = NULL;
624   int unthrottle_time;
625   vlib_log_main_t *lm = &log_main;
626
627   /* Get a line of input. */
628   if (!unformat_user (input, unformat_line_input, line_input))
629     return 0;
630
631   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
632     {
633       if (unformat (line_input, "%d", &unthrottle_time))
634         lm->unthrottle_time = unthrottle_time;
635       else
636         return clib_error_return (0, "unknown input `%U'",
637                                   format_unformat_error, line_input);
638     }
639
640   return rv;
641 }
642
643 /* *INDENT-OFF* */
644 VLIB_CLI_COMMAND (cli_set_log_params, static) = {
645   .path = "set logging unthrottle-time",
646   .short_help = "set logging unthrottle-time <int>",
647   .function = set_log_unth_time,
648 };
649 /* *INDENT-ON* */
650
651 static clib_error_t *
652 set_log_size (vlib_main_t * vm,
653               unformat_input_t * input, vlib_cli_command_t * cmd)
654 {
655   unformat_input_t _line_input, *line_input = &_line_input;
656   clib_error_t *rv = NULL;
657   int size;
658   vlib_log_main_t *lm = &log_main;
659
660   /* Get a line of input. */
661   if (!unformat_user (input, unformat_line_input, line_input))
662     return 0;
663
664   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
665     {
666       if (unformat (line_input, "%d", &size))
667         {
668           lm->size = size;
669           vec_validate (lm->entries, lm->size);
670         }
671       else
672         return clib_error_return (0, "unknown input `%U'",
673                                   format_unformat_error, line_input);
674     }
675
676   return rv;
677 }
678
679 /* *INDENT-OFF* */
680 VLIB_CLI_COMMAND (cli_set_log_size, static) = {
681   .path = "set logging size",
682   .short_help = "set logging size <int>",
683   .function = set_log_size,
684 };
685 /* *INDENT-ON* */
686
687 static uword
688 unformat_vlib_log_subclass (unformat_input_t * input, va_list * args)
689 {
690   vlib_log_class_data_t *class = va_arg (*args, vlib_log_class_data_t *);
691   vlib_log_subclass_data_t **subclass =
692     va_arg (*args, vlib_log_subclass_data_t **);
693   uword rv = 0;
694   u8 *subclass_str = NULL;
695   if (unformat (input, "%v", &subclass_str))
696     {
697       vlib_log_subclass_data_t *scdata;
698       vec_foreach (scdata, class->subclasses)
699       {
700         if (vec_is_equal (scdata->name, subclass_str))
701           {
702             rv = 1;
703             *subclass = scdata;
704             break;
705           }
706       }
707     }
708   vec_free (subclass_str);
709   return rv;
710 }
711
712 static clib_error_t *
713 test_log_class_subclass (vlib_main_t * vm,
714                          unformat_input_t * input, vlib_cli_command_t * cmd)
715 {
716   unformat_input_t _line_input, *line_input = &_line_input;
717   /* Get a line of input. */
718   if (!unformat_user (input, unformat_line_input, line_input))
719     return 0;
720
721   vlib_log_class_data_t *class = NULL;
722   vlib_log_subclass_data_t *subclass = NULL;
723   vlib_log_level_t level;
724   if (unformat (line_input, "%U", unformat_vlib_log_level, &level))
725     {
726       if (unformat (line_input, "%U", unformat_vlib_log_class, &class))
727         {
728           if (unformat
729               (line_input, "%U", unformat_vlib_log_subclass, class,
730                &subclass))
731             {
732               vlib_log (level,
733                         (class->index << 16) | (subclass->index), "%U",
734                         format_unformat_input, line_input);
735             }
736           else
737             {
738               return clib_error_return (0,
739                                         "unknown log subclass near beginning of `%U'",
740                                         format_unformat_error, line_input);
741             }
742         }
743       else
744         {
745           return clib_error_return (0,
746                                     "unknown log class near beginning of `%U'",
747                                     format_unformat_error, line_input);
748         }
749     }
750   else
751     {
752       return clib_error_return (0, "unknown log level near beginning of `%U'",
753                                 format_unformat_error, line_input);
754     }
755   return 0;
756 }
757
758 /* *INDENT-OFF* */
759 VLIB_CLI_COMMAND (cli_test_log, static) = {
760   .path = "test log",
761   .short_help = "test log <level> <class> <subclass> <message>",
762   .function = test_log_class_subclass,
763 };
764 /* *INDENT-ON* */
765
766 static clib_error_t *
767 log_config_class (vlib_main_t * vm, char *name, unformat_input_t * input)
768 {
769   vlib_log_main_t *lm = &log_main;
770   vlib_log_class_config_t *cc, tmp;
771   uword *p;
772
773   if (lm->config_index_by_name == 0)
774     lm->config_index_by_name = hash_create_string (0, sizeof (uword));
775
776   p = hash_get_mem (lm->config_index_by_name, name);
777
778   if (p)
779     return clib_error_return (0, "logging class '%s' already configured",
780                               name);
781
782   clib_memset_u8 (&tmp, 0xff, sizeof (vlib_log_class_config_t));
783
784   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
785     {
786       if (unformat (input, "level %U", unformat_vlib_log_level, &tmp.level))
787         ;
788       else if (unformat (input, "syslog-level %U", unformat_vlib_log_level,
789                          &tmp.syslog_level))
790         ;
791       else if (unformat (input, "rate-limit %u", &tmp.rate_limit))
792         ;
793       else
794         return clib_error_return (0, "unknown input '%U'",
795                                   format_unformat_error, input);
796     }
797
798   vec_add2 (lm->configs, cc, 1);
799   clib_memcpy_fast (cc, &tmp, sizeof (vlib_log_class_config_t));
800   cc->name = name;
801   hash_set_mem (lm->config_index_by_name, name, cc - lm->configs);
802   return 0;
803 }
804
805 static clib_error_t *
806 log_config (vlib_main_t * vm, unformat_input_t * input)
807 {
808   vlib_log_main_t *lm = &log_main;
809   unformat_input_t sub_input;
810   u8 *class = 0;
811
812   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
813     {
814       if (unformat (input, "size %d", &lm->size))
815         vec_validate (lm->entries, lm->size);
816       else if (unformat (input, "unthrottle-time %d", &lm->unthrottle_time))
817         ;
818       else if (unformat (input, "default-log-level %U",
819                          unformat_vlib_log_level, &lm->default_log_level))
820         ;
821       else if (unformat (input, "default-syslog-log-level %U",
822                          unformat_vlib_log_level,
823                          &lm->default_syslog_log_level))
824         ;
825       else if (unformat (input, "class %s %U", &class,
826                          unformat_vlib_cli_sub_input, &sub_input))
827         {
828           clib_error_t *err;
829           err = log_config_class (vm, (char *) class, &sub_input);
830           class = 0;
831           unformat_free (&sub_input);
832           if (err)
833             return err;
834         }
835       else
836         {
837           return unformat_parse_error (input);
838         }
839     }
840
841   return 0;
842 }
843
844 VLIB_EARLY_CONFIG_FUNCTION (log_config, "logging");
845
846 /*
847  * fd.io coding-style-patch-verification: ON
848  *
849  * Local Variables:
850  * eval: (c-set-style "gnu")
851  * End:
852  */