]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
279c9e03 | 2 | * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "vlog.h" | |
19 | #include <assert.h> | |
20 | #include <ctype.h> | |
21 | #include <errno.h> | |
22 | #include <stdarg.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
68cb8aaf | 25 | #include <sys/stat.h> |
064af421 BP |
26 | #include <sys/types.h> |
27 | #include <syslog.h> | |
28 | #include <time.h> | |
29 | #include <unistd.h> | |
30 | #include "dirs.h" | |
31 | #include "dynamic-string.h" | |
32 | #include "sat-math.h" | |
8628b0b7 | 33 | #include "svec.h" |
064af421 BP |
34 | #include "timeval.h" |
35 | #include "unixctl.h" | |
36 | #include "util.h" | |
37 | ||
d98e6007 | 38 | VLOG_DEFINE_THIS_MODULE(vlog); |
064af421 BP |
39 | |
40 | /* Name for each logging level. */ | |
41 | static const char *level_names[VLL_N_LEVELS] = { | |
42 | #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME, | |
43 | VLOG_LEVELS | |
44 | #undef VLOG_LEVEL | |
45 | }; | |
46 | ||
47 | /* Syslog value for each logging level. */ | |
48 | static int syslog_levels[VLL_N_LEVELS] = { | |
49 | #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) SYSLOG_LEVEL, | |
50 | VLOG_LEVELS | |
51 | #undef VLOG_LEVEL | |
52 | }; | |
53 | ||
480ce8ab BP |
54 | /* The log modules. */ |
55 | #if USE_LINKER_SECTIONS | |
56 | extern struct vlog_module *__start_vlog_modules[]; | |
57 | extern struct vlog_module *__stop_vlog_modules[]; | |
58 | #define vlog_modules __start_vlog_modules | |
59 | #define n_vlog_modules (__stop_vlog_modules - __start_vlog_modules) | |
60 | #else | |
61 | #define VLOG_MODULE VLOG_DEFINE_MODULE__ | |
62 | #include "vlog-modules.def" | |
63 | #undef VLOG_MODULE | |
64 | ||
65 | struct vlog_module *vlog_modules[] = { | |
66 | #define VLOG_MODULE(NAME) &VLM_##NAME, | |
064af421 BP |
67 | #include "vlog-modules.def" |
68 | #undef VLOG_MODULE | |
69 | }; | |
480ce8ab BP |
70 | #define n_vlog_modules ARRAY_SIZE(vlog_modules) |
71 | #endif | |
064af421 BP |
72 | |
73 | /* Information about each facility. */ | |
74 | struct facility { | |
75 | const char *name; /* Name. */ | |
76 | char *pattern; /* Current pattern. */ | |
77 | bool default_pattern; /* Whether current pattern is the default. */ | |
78 | }; | |
79 | static struct facility facilities[VLF_N_FACILITIES] = { | |
80 | #define VLOG_FACILITY(NAME, PATTERN) {#NAME, PATTERN, true}, | |
81 | VLOG_FACILITIES | |
82 | #undef VLOG_FACILITY | |
83 | }; | |
84 | ||
064af421 BP |
85 | /* Time at which vlog was initialized, in milliseconds. */ |
86 | static long long int boot_time; | |
87 | ||
88 | /* VLF_FILE configuration. */ | |
89 | static char *log_file_name; | |
90 | static FILE *log_file; | |
91 | ||
1e8cf0f7 BP |
92 | /* vlog initialized? */ |
93 | static bool vlog_inited; | |
94 | ||
480ce8ab | 95 | static void format_log_message(const struct vlog_module *, enum vlog_level, |
064af421 BP |
96 | enum vlog_facility, unsigned int msg_num, |
97 | const char *message, va_list, struct ds *) | |
98 | PRINTF_FORMAT(5, 0); | |
99 | ||
100 | /* Searches the 'n_names' in 'names'. Returns the index of a match for | |
101 | * 'target', or 'n_names' if no name matches. */ | |
102 | static size_t | |
d295e8e9 | 103 | search_name_array(const char *target, const char **names, size_t n_names) |
064af421 BP |
104 | { |
105 | size_t i; | |
106 | ||
107 | for (i = 0; i < n_names; i++) { | |
108 | assert(names[i]); | |
109 | if (!strcasecmp(names[i], target)) { | |
110 | break; | |
111 | } | |
112 | } | |
113 | return i; | |
114 | } | |
115 | ||
116 | /* Returns the name for logging level 'level'. */ | |
117 | const char * | |
118 | vlog_get_level_name(enum vlog_level level) | |
119 | { | |
120 | assert(level < VLL_N_LEVELS); | |
121 | return level_names[level]; | |
122 | } | |
123 | ||
124 | /* Returns the logging level with the given 'name', or VLL_N_LEVELS if 'name' | |
125 | * is not the name of a logging level. */ | |
126 | enum vlog_level | |
d295e8e9 | 127 | vlog_get_level_val(const char *name) |
064af421 BP |
128 | { |
129 | return search_name_array(name, level_names, ARRAY_SIZE(level_names)); | |
130 | } | |
131 | ||
132 | /* Returns the name for logging facility 'facility'. */ | |
133 | const char * | |
d295e8e9 | 134 | vlog_get_facility_name(enum vlog_facility facility) |
064af421 BP |
135 | { |
136 | assert(facility < VLF_N_FACILITIES); | |
137 | return facilities[facility].name; | |
138 | } | |
139 | ||
140 | /* Returns the logging facility named 'name', or VLF_N_FACILITIES if 'name' is | |
141 | * not the name of a logging facility. */ | |
142 | enum vlog_facility | |
d295e8e9 | 143 | vlog_get_facility_val(const char *name) |
064af421 BP |
144 | { |
145 | size_t i; | |
146 | ||
147 | for (i = 0; i < VLF_N_FACILITIES; i++) { | |
148 | if (!strcasecmp(facilities[i].name, name)) { | |
149 | break; | |
150 | } | |
151 | } | |
152 | return i; | |
153 | } | |
154 | ||
155 | /* Returns the name for logging module 'module'. */ | |
480ce8ab BP |
156 | const char * |
157 | vlog_get_module_name(const struct vlog_module *module) | |
064af421 | 158 | { |
480ce8ab | 159 | return module->name; |
064af421 BP |
160 | } |
161 | ||
480ce8ab BP |
162 | /* Returns the logging module named 'name', or NULL if 'name' is not the name |
163 | * of a logging module. */ | |
164 | struct vlog_module * | |
165 | vlog_module_from_name(const char *name) | |
064af421 | 166 | { |
480ce8ab BP |
167 | struct vlog_module **mp; |
168 | ||
169 | for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { | |
170 | if (!strcasecmp(name, (*mp)->name)) { | |
171 | return *mp; | |
172 | } | |
173 | } | |
174 | return NULL; | |
064af421 BP |
175 | } |
176 | ||
177 | /* Returns the current logging level for the given 'module' and 'facility'. */ | |
178 | enum vlog_level | |
d295e8e9 | 179 | vlog_get_level(const struct vlog_module *module, enum vlog_facility facility) |
064af421 | 180 | { |
064af421 | 181 | assert(facility < VLF_N_FACILITIES); |
480ce8ab | 182 | return module->levels[facility]; |
064af421 BP |
183 | } |
184 | ||
185 | static void | |
480ce8ab | 186 | update_min_level(struct vlog_module *module) |
064af421 | 187 | { |
064af421 BP |
188 | enum vlog_facility facility; |
189 | ||
c1a543a8 | 190 | module->min_level = VLL_OFF; |
064af421 BP |
191 | for (facility = 0; facility < VLF_N_FACILITIES; facility++) { |
192 | if (log_file || facility != VLF_FILE) { | |
480ce8ab | 193 | enum vlog_level level = module->levels[facility]; |
56cee53b | 194 | if (level > module->min_level) { |
480ce8ab BP |
195 | module->min_level = level; |
196 | } | |
064af421 BP |
197 | } |
198 | } | |
064af421 BP |
199 | } |
200 | ||
201 | static void | |
480ce8ab | 202 | set_facility_level(enum vlog_facility facility, struct vlog_module *module, |
064af421 BP |
203 | enum vlog_level level) |
204 | { | |
205 | assert(facility >= 0 && facility < VLF_N_FACILITIES); | |
206 | assert(level < VLL_N_LEVELS); | |
207 | ||
480ce8ab BP |
208 | if (!module) { |
209 | struct vlog_module **mp; | |
210 | ||
211 | for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { | |
212 | (*mp)->levels[facility] = level; | |
213 | update_min_level(*mp); | |
064af421 BP |
214 | } |
215 | } else { | |
480ce8ab | 216 | module->levels[facility] = level; |
064af421 BP |
217 | update_min_level(module); |
218 | } | |
219 | } | |
220 | ||
480ce8ab BP |
221 | /* Sets the logging level for the given 'module' and 'facility' to 'level'. A |
222 | * null 'module' or a 'facility' of VLF_ANY_FACILITY is treated as a wildcard | |
223 | * across all modules or facilities, respectively. */ | |
064af421 | 224 | void |
480ce8ab | 225 | vlog_set_levels(struct vlog_module *module, enum vlog_facility facility, |
d295e8e9 | 226 | enum vlog_level level) |
064af421 BP |
227 | { |
228 | assert(facility < VLF_N_FACILITIES || facility == VLF_ANY_FACILITY); | |
229 | if (facility == VLF_ANY_FACILITY) { | |
230 | for (facility = 0; facility < VLF_N_FACILITIES; facility++) { | |
231 | set_facility_level(facility, module, level); | |
232 | } | |
233 | } else { | |
234 | set_facility_level(facility, module, level); | |
235 | } | |
236 | } | |
237 | ||
238 | static void | |
d295e8e9 | 239 | do_set_pattern(enum vlog_facility facility, const char *pattern) |
064af421 BP |
240 | { |
241 | struct facility *f = &facilities[facility]; | |
242 | if (!f->default_pattern) { | |
243 | free(f->pattern); | |
244 | } else { | |
245 | f->default_pattern = false; | |
246 | } | |
247 | f->pattern = xstrdup(pattern); | |
248 | } | |
249 | ||
250 | /* Sets the pattern for the given 'facility' to 'pattern'. */ | |
251 | void | |
252 | vlog_set_pattern(enum vlog_facility facility, const char *pattern) | |
253 | { | |
254 | assert(facility < VLF_N_FACILITIES || facility == VLF_ANY_FACILITY); | |
255 | if (facility == VLF_ANY_FACILITY) { | |
256 | for (facility = 0; facility < VLF_N_FACILITIES; facility++) { | |
257 | do_set_pattern(facility, pattern); | |
258 | } | |
259 | } else { | |
260 | do_set_pattern(facility, pattern); | |
261 | } | |
262 | } | |
263 | ||
264 | /* Returns the name of the log file used by VLF_FILE, or a null pointer if no | |
265 | * log file has been set. (A non-null return value does not assert that the | |
266 | * named log file is in use: if vlog_set_log_file() or vlog_reopen_log_file() | |
267 | * fails, it still sets the log file name.) */ | |
268 | const char * | |
269 | vlog_get_log_file(void) | |
270 | { | |
271 | return log_file_name; | |
272 | } | |
273 | ||
274 | /* Sets the name of the log file used by VLF_FILE to 'file_name', or to the | |
275 | * default file name if 'file_name' is null. Returns 0 if successful, | |
276 | * otherwise a positive errno value. */ | |
277 | int | |
278 | vlog_set_log_file(const char *file_name) | |
279 | { | |
280 | char *old_log_file_name; | |
480ce8ab | 281 | struct vlog_module **mp; |
064af421 BP |
282 | int error; |
283 | ||
284 | /* Close old log file. */ | |
285 | if (log_file) { | |
286 | VLOG_INFO("closing log file"); | |
287 | fclose(log_file); | |
288 | log_file = NULL; | |
289 | } | |
290 | ||
291 | /* Update log file name and free old name. The ordering is important | |
292 | * because 'file_name' might be 'log_file_name' or some suffix of it. */ | |
293 | old_log_file_name = log_file_name; | |
294 | log_file_name = (file_name | |
295 | ? xstrdup(file_name) | |
b43c6fe2 | 296 | : xasprintf("%s/%s.log", ovs_logdir(), program_name)); |
064af421 BP |
297 | free(old_log_file_name); |
298 | file_name = NULL; /* Might have been freed. */ | |
299 | ||
300 | /* Open new log file and update min_levels[] to reflect whether we actually | |
301 | * have a log_file. */ | |
302 | log_file = fopen(log_file_name, "a"); | |
480ce8ab BP |
303 | for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { |
304 | update_min_level(*mp); | |
064af421 BP |
305 | } |
306 | ||
307 | /* Log success or failure. */ | |
308 | if (!log_file) { | |
309 | VLOG_WARN("failed to open %s for logging: %s", | |
310 | log_file_name, strerror(errno)); | |
311 | error = errno; | |
312 | } else { | |
313 | VLOG_INFO("opened log file %s", log_file_name); | |
314 | error = 0; | |
315 | } | |
316 | ||
317 | return error; | |
318 | } | |
319 | ||
320 | /* Closes and then attempts to re-open the current log file. (This is useful | |
321 | * just after log rotation, to ensure that the new log file starts being used.) | |
322 | * Returns 0 if successful, otherwise a positive errno value. */ | |
323 | int | |
324 | vlog_reopen_log_file(void) | |
325 | { | |
68cb8aaf BP |
326 | struct stat old, new; |
327 | ||
328 | /* Skip re-opening if there's nothing to reopen. */ | |
329 | if (!log_file_name) { | |
330 | return 0; | |
331 | } | |
332 | ||
333 | /* Skip re-opening if it would be a no-op because the old and new files are | |
334 | * the same. (This avoids writing "closing log file" followed immediately | |
335 | * by "opened log file".) */ | |
336 | if (log_file | |
337 | && !fstat(fileno(log_file), &old) | |
338 | && !stat(log_file_name, &new) | |
339 | && old.st_dev == new.st_dev | |
340 | && old.st_ino == new.st_ino) { | |
341 | return 0; | |
342 | } | |
343 | ||
344 | return vlog_set_log_file(log_file_name); | |
064af421 BP |
345 | } |
346 | ||
347 | /* Set debugging levels: | |
348 | * | |
349 | * mod[:facility[:level]] mod2[:facility[:level]] ... | |
350 | * | |
351 | * Return null if successful, otherwise an error message that the caller must | |
352 | * free(). | |
353 | */ | |
354 | char * | |
355 | vlog_set_levels_from_string(const char *s_) | |
356 | { | |
ba8de5cb | 357 | char *save_ptr = NULL; |
064af421 BP |
358 | char *s = xstrdup(s_); |
359 | char *module, *facility; | |
360 | ||
361 | for (module = strtok_r(s, ": \t", &save_ptr); module != NULL; | |
362 | module = strtok_r(NULL, ": \t", &save_ptr)) { | |
480ce8ab | 363 | struct vlog_module *e_module; |
064af421 BP |
364 | enum vlog_facility e_facility; |
365 | ||
366 | facility = strtok_r(NULL, ":", &save_ptr); | |
367 | ||
c49aae24 | 368 | if (!facility || !strcasecmp(facility, "ANY")) { |
064af421 BP |
369 | e_facility = VLF_ANY_FACILITY; |
370 | } else { | |
371 | e_facility = vlog_get_facility_val(facility); | |
372 | if (e_facility >= VLF_N_FACILITIES) { | |
373 | char *msg = xasprintf("unknown facility \"%s\"", facility); | |
374 | free(s); | |
375 | return msg; | |
376 | } | |
377 | } | |
378 | ||
c49aae24 | 379 | if (!strcasecmp(module, "PATTERN")) { |
064af421 BP |
380 | vlog_set_pattern(e_facility, save_ptr); |
381 | break; | |
382 | } else { | |
383 | char *level; | |
384 | enum vlog_level e_level; | |
385 | ||
c49aae24 | 386 | if (!strcasecmp(module, "ANY")) { |
480ce8ab | 387 | e_module = NULL; |
064af421 | 388 | } else { |
480ce8ab BP |
389 | e_module = vlog_module_from_name(module); |
390 | if (!e_module) { | |
064af421 BP |
391 | char *msg = xasprintf("unknown module \"%s\"", module); |
392 | free(s); | |
393 | return msg; | |
394 | } | |
395 | } | |
396 | ||
397 | level = strtok_r(NULL, ":", &save_ptr); | |
398 | e_level = level ? vlog_get_level_val(level) : VLL_DBG; | |
399 | if (e_level >= VLL_N_LEVELS) { | |
400 | char *msg = xasprintf("unknown level \"%s\"", level); | |
401 | free(s); | |
402 | return msg; | |
403 | } | |
404 | ||
405 | vlog_set_levels(e_module, e_facility, e_level); | |
406 | } | |
407 | } | |
408 | free(s); | |
409 | return NULL; | |
410 | } | |
411 | ||
412 | /* If 'arg' is null, configure maximum verbosity. Otherwise, sets | |
413 | * configuration according to 'arg' (see vlog_set_levels_from_string()). */ | |
414 | void | |
415 | vlog_set_verbosity(const char *arg) | |
416 | { | |
417 | if (arg) { | |
418 | char *msg = vlog_set_levels_from_string(arg); | |
419 | if (msg) { | |
420 | ovs_fatal(0, "processing \"%s\": %s", arg, msg); | |
421 | } | |
422 | } else { | |
480ce8ab | 423 | vlog_set_levels(NULL, VLF_ANY_FACILITY, VLL_DBG); |
064af421 BP |
424 | } |
425 | } | |
426 | ||
427 | static void | |
0e15264f BP |
428 | vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[], |
429 | void *aux OVS_UNUSED) | |
064af421 | 430 | { |
0e15264f BP |
431 | int i; |
432 | ||
433 | for (i = 1; i < argc; i++) { | |
434 | char *msg = vlog_set_levels_from_string(argv[i]); | |
435 | if (msg) { | |
436 | unixctl_command_reply(conn, 501, msg); | |
437 | free(msg); | |
438 | return; | |
439 | } | |
440 | } | |
441 | unixctl_command_reply(conn, 202, NULL); | |
064af421 BP |
442 | } |
443 | ||
444 | static void | |
0e15264f BP |
445 | vlog_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED, |
446 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
064af421 BP |
447 | { |
448 | char *msg = vlog_get_levels(); | |
449 | unixctl_command_reply(conn, 200, msg); | |
450 | free(msg); | |
451 | } | |
452 | ||
453 | static void | |
0e15264f BP |
454 | vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED, |
455 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
064af421 BP |
456 | { |
457 | if (log_file_name) { | |
458 | int error = vlog_reopen_log_file(); | |
459 | if (error) { | |
460 | unixctl_command_reply(conn, 503, strerror(errno)); | |
461 | } else { | |
462 | unixctl_command_reply(conn, 202, NULL); | |
463 | } | |
464 | } else { | |
465 | unixctl_command_reply(conn, 403, "Logging to file not configured"); | |
466 | } | |
467 | } | |
468 | ||
df5d2ed9 BP |
469 | /* Initializes the logging subsystem and registers its unixctl server |
470 | * commands. */ | |
064af421 | 471 | void |
d295e8e9 | 472 | vlog_init(void) |
064af421 BP |
473 | { |
474 | time_t now; | |
475 | ||
1e8cf0f7 BP |
476 | if (vlog_inited) { |
477 | return; | |
478 | } | |
479 | vlog_inited = true; | |
480 | ||
064af421 | 481 | openlog(program_name, LOG_NDELAY, LOG_DAEMON); |
064af421 BP |
482 | |
483 | boot_time = time_msec(); | |
c73814a3 | 484 | now = time_wall(); |
064af421 BP |
485 | if (now < 0) { |
486 | struct tm tm; | |
487 | char s[128]; | |
488 | ||
489 | localtime_r(&now, &tm); | |
490 | strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S %z", &tm); | |
491 | VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now); | |
492 | } | |
493 | ||
0e15264f BP |
494 | unixctl_command_register( |
495 | "vlog/set", "{module[:facility[:level]] | PATTERN:facility:pattern}", | |
496 | 1, INT_MAX, vlog_unixctl_set, NULL); | |
497 | unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list, NULL); | |
498 | unixctl_command_register("vlog/reopen", "", 0, 0, | |
499 | vlog_unixctl_reopen, NULL); | |
064af421 BP |
500 | } |
501 | ||
502 | /* Closes the logging subsystem. */ | |
503 | void | |
d295e8e9 | 504 | vlog_exit(void) |
064af421 | 505 | { |
1e8cf0f7 BP |
506 | if (vlog_inited) { |
507 | closelog(); | |
508 | vlog_inited = false; | |
509 | } | |
064af421 BP |
510 | } |
511 | ||
512 | /* Print the current logging level for each module. */ | |
513 | char * | |
514 | vlog_get_levels(void) | |
515 | { | |
516 | struct ds s = DS_EMPTY_INITIALIZER; | |
480ce8ab | 517 | struct vlog_module **mp; |
8628b0b7 JP |
518 | struct svec lines = SVEC_EMPTY_INITIALIZER; |
519 | char *line; | |
520 | size_t i; | |
064af421 BP |
521 | |
522 | ds_put_format(&s, " console syslog file\n"); | |
523 | ds_put_format(&s, " ------- ------ ------\n"); | |
524 | ||
480ce8ab | 525 | for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { |
8628b0b7 | 526 | line = xasprintf("%-16s %4s %4s %4s\n", |
480ce8ab BP |
527 | vlog_get_module_name(*mp), |
528 | vlog_get_level_name(vlog_get_level(*mp, VLF_CONSOLE)), | |
529 | vlog_get_level_name(vlog_get_level(*mp, VLF_SYSLOG)), | |
530 | vlog_get_level_name(vlog_get_level(*mp, VLF_FILE))); | |
8628b0b7 JP |
531 | svec_add_nocopy(&lines, line); |
532 | } | |
533 | ||
534 | svec_sort(&lines); | |
535 | SVEC_FOR_EACH (i, line, &lines) { | |
536 | ds_put_cstr(&s, line); | |
064af421 | 537 | } |
8628b0b7 | 538 | svec_destroy(&lines); |
064af421 BP |
539 | |
540 | return ds_cstr(&s); | |
541 | } | |
542 | ||
543 | /* Returns true if a log message emitted for the given 'module' and 'level' | |
544 | * would cause some log output, false if that module and level are completely | |
545 | * disabled. */ | |
546 | bool | |
480ce8ab | 547 | vlog_is_enabled(const struct vlog_module *module, enum vlog_level level) |
064af421 | 548 | { |
480ce8ab | 549 | return module->min_level >= level; |
064af421 BP |
550 | } |
551 | ||
552 | static const char * | |
553 | fetch_braces(const char *p, const char *def, char *out, size_t out_size) | |
554 | { | |
555 | if (*p == '{') { | |
556 | size_t n = strcspn(p + 1, "}"); | |
557 | size_t n_copy = MIN(n, out_size - 1); | |
558 | memcpy(out, p + 1, n_copy); | |
559 | out[n_copy] = '\0'; | |
560 | p += n + 2; | |
561 | } else { | |
562 | ovs_strlcpy(out, def, out_size); | |
563 | } | |
564 | return p; | |
565 | } | |
566 | ||
567 | static void | |
480ce8ab | 568 | format_log_message(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
569 | enum vlog_facility facility, unsigned int msg_num, |
570 | const char *message, va_list args_, struct ds *s) | |
571 | { | |
572 | char tmp[128]; | |
573 | va_list args; | |
574 | const char *p; | |
575 | ||
576 | ds_clear(s); | |
577 | for (p = facilities[facility].pattern; *p != '\0'; ) { | |
578 | enum { LEFT, RIGHT } justify = RIGHT; | |
579 | int pad = '0'; | |
580 | size_t length, field, used; | |
581 | ||
582 | if (*p != '%') { | |
583 | ds_put_char(s, *p++); | |
584 | continue; | |
585 | } | |
586 | ||
587 | p++; | |
588 | if (*p == '-') { | |
589 | justify = LEFT; | |
590 | p++; | |
591 | } | |
592 | if (*p == '0') { | |
593 | pad = '0'; | |
594 | p++; | |
595 | } | |
596 | field = 0; | |
be2c418b | 597 | while (isdigit((unsigned char)*p)) { |
064af421 BP |
598 | field = (field * 10) + (*p - '0'); |
599 | p++; | |
600 | } | |
601 | ||
602 | length = s->length; | |
603 | switch (*p++) { | |
604 | case 'A': | |
605 | ds_put_cstr(s, program_name); | |
606 | break; | |
607 | case 'c': | |
608 | p = fetch_braces(p, "", tmp, sizeof tmp); | |
609 | ds_put_cstr(s, vlog_get_module_name(module)); | |
610 | break; | |
611 | case 'd': | |
612 | p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp); | |
b5d29991 GS |
613 | ds_put_strftime(s, tmp, false); |
614 | break; | |
615 | case 'D': | |
616 | p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp); | |
617 | ds_put_strftime(s, tmp, true); | |
064af421 BP |
618 | break; |
619 | case 'm': | |
620 | /* Format user-supplied log message and trim trailing new-lines. */ | |
621 | length = s->length; | |
622 | va_copy(args, args_); | |
623 | ds_put_format_valist(s, message, args); | |
624 | va_end(args); | |
625 | while (s->length > length && s->string[s->length - 1] == '\n') { | |
626 | s->length--; | |
627 | } | |
628 | break; | |
629 | case 'N': | |
630 | ds_put_format(s, "%u", msg_num); | |
631 | break; | |
632 | case 'n': | |
633 | ds_put_char(s, '\n'); | |
634 | break; | |
635 | case 'p': | |
636 | ds_put_cstr(s, vlog_get_level_name(level)); | |
637 | break; | |
638 | case 'P': | |
639 | ds_put_format(s, "%ld", (long int) getpid()); | |
640 | break; | |
641 | case 'r': | |
642 | ds_put_format(s, "%lld", time_msec() - boot_time); | |
643 | break; | |
644 | default: | |
645 | ds_put_char(s, p[-1]); | |
646 | break; | |
647 | } | |
648 | used = s->length - length; | |
649 | if (used < field) { | |
650 | size_t n_pad = field - used; | |
651 | if (justify == RIGHT) { | |
652 | ds_put_uninit(s, n_pad); | |
653 | memmove(&s->string[length + n_pad], &s->string[length], used); | |
654 | memset(&s->string[length], pad, n_pad); | |
655 | } else { | |
656 | ds_put_char_multiple(s, pad, n_pad); | |
657 | } | |
658 | } | |
659 | } | |
660 | } | |
661 | ||
662 | /* Writes 'message' to the log at the given 'level' and as coming from the | |
663 | * given 'module'. | |
664 | * | |
665 | * Guaranteed to preserve errno. */ | |
666 | void | |
480ce8ab | 667 | vlog_valist(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
668 | const char *message, va_list args) |
669 | { | |
480ce8ab BP |
670 | bool log_to_console = module->levels[VLF_CONSOLE] >= level; |
671 | bool log_to_syslog = module->levels[VLF_SYSLOG] >= level; | |
672 | bool log_to_file = module->levels[VLF_FILE] >= level && log_file; | |
064af421 BP |
673 | if (log_to_console || log_to_syslog || log_to_file) { |
674 | int save_errno = errno; | |
675 | static unsigned int msg_num; | |
676 | struct ds s; | |
677 | ||
1e8cf0f7 BP |
678 | vlog_init(); |
679 | ||
064af421 BP |
680 | ds_init(&s); |
681 | ds_reserve(&s, 1024); | |
682 | msg_num++; | |
683 | ||
684 | if (log_to_console) { | |
685 | format_log_message(module, level, VLF_CONSOLE, msg_num, | |
686 | message, args, &s); | |
687 | ds_put_char(&s, '\n'); | |
688 | fputs(ds_cstr(&s), stderr); | |
689 | } | |
690 | ||
691 | if (log_to_syslog) { | |
692 | int syslog_level = syslog_levels[level]; | |
693 | char *save_ptr = NULL; | |
694 | char *line; | |
695 | ||
696 | format_log_message(module, level, VLF_SYSLOG, msg_num, | |
697 | message, args, &s); | |
698 | for (line = strtok_r(s.string, "\n", &save_ptr); line; | |
699 | line = strtok_r(NULL, "\n", &save_ptr)) { | |
700 | syslog(syslog_level, "%s", line); | |
701 | } | |
702 | } | |
703 | ||
704 | if (log_to_file) { | |
705 | format_log_message(module, level, VLF_FILE, msg_num, | |
706 | message, args, &s); | |
707 | ds_put_char(&s, '\n'); | |
708 | fputs(ds_cstr(&s), log_file); | |
709 | fflush(log_file); | |
710 | } | |
711 | ||
712 | ds_destroy(&s); | |
713 | errno = save_errno; | |
714 | } | |
715 | } | |
716 | ||
717 | void | |
480ce8ab BP |
718 | vlog(const struct vlog_module *module, enum vlog_level level, |
719 | const char *message, ...) | |
064af421 BP |
720 | { |
721 | va_list args; | |
722 | ||
723 | va_start(args, message); | |
724 | vlog_valist(module, level, message, args); | |
725 | va_end(args); | |
726 | } | |
727 | ||
279c9e03 | 728 | void |
c1a543a8 | 729 | vlog_fatal_valist(const struct vlog_module *module_, |
279c9e03 BP |
730 | const char *message, va_list args) |
731 | { | |
732 | struct vlog_module *module = (struct vlog_module *) module_; | |
733 | ||
734 | /* Don't log this message to the console to avoid redundancy with the | |
735 | * message written by the later ovs_fatal_valist(). */ | |
c1a543a8 | 736 | module->levels[VLF_CONSOLE] = VLL_OFF; |
279c9e03 | 737 | |
c1a543a8 | 738 | vlog_valist(module, VLL_EMER, message, args); |
279c9e03 BP |
739 | ovs_fatal_valist(0, message, args); |
740 | } | |
741 | ||
742 | void | |
c1a543a8 | 743 | vlog_fatal(const struct vlog_module *module, const char *message, ...) |
279c9e03 BP |
744 | { |
745 | va_list args; | |
746 | ||
747 | va_start(args, message); | |
c1a543a8 | 748 | vlog_fatal_valist(module, message, args); |
279c9e03 BP |
749 | va_end(args); |
750 | } | |
751 | ||
064af421 | 752 | bool |
480ce8ab | 753 | vlog_should_drop(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
754 | struct vlog_rate_limit *rl) |
755 | { | |
756 | if (!vlog_is_enabled(module, level)) { | |
757 | return true; | |
758 | } | |
759 | ||
760 | if (rl->tokens < VLOG_MSG_TOKENS) { | |
761 | time_t now = time_now(); | |
762 | if (rl->last_fill > now) { | |
763 | /* Last filled in the future? Time must have gone backward, or | |
764 | * 'rl' has not been used before. */ | |
765 | rl->tokens = rl->burst; | |
766 | } else if (rl->last_fill < now) { | |
767 | unsigned int add = sat_mul(rl->rate, now - rl->last_fill); | |
768 | unsigned int tokens = sat_add(rl->tokens, add); | |
769 | rl->tokens = MIN(tokens, rl->burst); | |
770 | rl->last_fill = now; | |
771 | } | |
772 | if (rl->tokens < VLOG_MSG_TOKENS) { | |
773 | if (!rl->n_dropped) { | |
774 | rl->first_dropped = now; | |
775 | } | |
e2eed6a7 | 776 | rl->last_dropped = now; |
064af421 BP |
777 | rl->n_dropped++; |
778 | return true; | |
779 | } | |
780 | } | |
781 | rl->tokens -= VLOG_MSG_TOKENS; | |
782 | ||
783 | if (rl->n_dropped) { | |
e2eed6a7 BP |
784 | time_t now = time_now(); |
785 | unsigned int first_dropped_elapsed = now - rl->first_dropped; | |
786 | unsigned int last_dropped_elapsed = now - rl->last_dropped; | |
787 | ||
064af421 | 788 | vlog(module, level, |
e2eed6a7 BP |
789 | "Dropped %u log messages in last %u seconds (most recently, " |
790 | "%u seconds ago) due to excessive rate", | |
791 | rl->n_dropped, first_dropped_elapsed, last_dropped_elapsed); | |
792 | ||
064af421 BP |
793 | rl->n_dropped = 0; |
794 | } | |
795 | return false; | |
796 | } | |
797 | ||
798 | void | |
480ce8ab | 799 | vlog_rate_limit(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
800 | struct vlog_rate_limit *rl, const char *message, ...) |
801 | { | |
802 | if (!vlog_should_drop(module, level, rl)) { | |
803 | va_list args; | |
804 | ||
805 | va_start(args, message); | |
806 | vlog_valist(module, level, message, args); | |
807 | va_end(args); | |
808 | } | |
809 | } | |
810 | ||
811 | void | |
d295e8e9 | 812 | vlog_usage(void) |
064af421 BP |
813 | { |
814 | printf("\nLogging options:\n" | |
815 | " -v, --verbose=MODULE[:FACILITY[:LEVEL]] set logging levels\n" | |
816 | " -v, --verbose set maximum verbosity level\n" | |
817 | " --log-file[=FILE] enable logging to specified FILE\n" | |
818 | " (default: %s/%s.log)\n", | |
b43c6fe2 | 819 | ovs_logdir(), program_name); |
064af421 | 820 | } |