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