]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
4958e3ee | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
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> | |
9eb94563 | 22 | #include <fcntl.h> |
064af421 BP |
23 | #include <stdarg.h> |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
68cb8aaf | 26 | #include <sys/stat.h> |
064af421 BP |
27 | #include <sys/types.h> |
28 | #include <syslog.h> | |
29 | #include <time.h> | |
30 | #include <unistd.h> | |
a5fb0e29 | 31 | #include "async-append.h" |
4958e3ee | 32 | #include "coverage.h" |
064af421 BP |
33 | #include "dirs.h" |
34 | #include "dynamic-string.h" | |
cbb13e8e | 35 | #include "ofpbuf.h" |
81d6495f | 36 | #include "ovs-thread.h" |
064af421 | 37 | #include "sat-math.h" |
afc9f547 | 38 | #include "socket-util.h" |
8628b0b7 | 39 | #include "svec.h" |
064af421 BP |
40 | #include "timeval.h" |
41 | #include "unixctl.h" | |
42 | #include "util.h" | |
43 | ||
d98e6007 | 44 | VLOG_DEFINE_THIS_MODULE(vlog); |
064af421 | 45 | |
4958e3ee BP |
46 | /* ovs_assert() logs the assertion message, so using ovs_assert() in this |
47 | * source file could cause recursion. */ | |
48 | #undef ovs_assert | |
49 | #define ovs_assert use_assert_instead_of_ovs_assert_in_this_module | |
50 | ||
064af421 | 51 | /* Name for each logging level. */ |
4b5f1d2c | 52 | static const char *const level_names[VLL_N_LEVELS] = { |
afc9f547 | 53 | #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) #NAME, |
064af421 BP |
54 | VLOG_LEVELS |
55 | #undef VLOG_LEVEL | |
56 | }; | |
57 | ||
58 | /* Syslog value for each logging level. */ | |
4b5f1d2c | 59 | static const int syslog_levels[VLL_N_LEVELS] = { |
afc9f547 | 60 | #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) SYSLOG_LEVEL, |
064af421 BP |
61 | VLOG_LEVELS |
62 | #undef VLOG_LEVEL | |
63 | }; | |
64 | ||
afc9f547 HM |
65 | /* RFC 5424 defines specific values for each syslog level. Normally LOG_* use |
66 | * the same values. Verify that in fact they're the same. If we get assertion | |
67 | * failures here then we need to define a separate rfc5424_levels[] array. */ | |
68 | #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) \ | |
69 | BUILD_ASSERT_DECL(SYSLOG_LEVEL == RFC5424); | |
70 | VLOG_LEVELS | |
71 | #undef VLOG_LEVELS | |
72 | ||
73 | /* Similarly, RFC 5424 defines the local0 facility with the value ordinarily | |
74 | * used for LOG_LOCAL0. */ | |
75 | BUILD_ASSERT_DECL(LOG_LOCAL0 == (16 << 3)); | |
76 | ||
480ce8ab | 77 | /* The log modules. */ |
ca6ba700 | 78 | struct ovs_list vlog_modules = LIST_INITIALIZER(&vlog_modules); |
064af421 | 79 | |
97be1538 EJ |
80 | /* Protects the 'pattern' in all "struct facility"s, so that a race between |
81 | * changing and reading the pattern does not cause an access to freed | |
82 | * memory. */ | |
83 | static struct ovs_rwlock pattern_rwlock = OVS_RWLOCK_INITIALIZER; | |
84 | ||
064af421 BP |
85 | /* Information about each facility. */ |
86 | struct facility { | |
87 | const char *name; /* Name. */ | |
97be1538 | 88 | char *pattern OVS_GUARDED_BY(pattern_rwlock); /* Current pattern. */ |
064af421 BP |
89 | bool default_pattern; /* Whether current pattern is the default. */ |
90 | }; | |
91 | static struct facility facilities[VLF_N_FACILITIES] = { | |
92 | #define VLOG_FACILITY(NAME, PATTERN) {#NAME, PATTERN, true}, | |
93 | VLOG_FACILITIES | |
94 | #undef VLOG_FACILITY | |
95 | }; | |
96 | ||
81d6495f | 97 | /* Sequence number for the message currently being composed. */ |
2ba4f163 | 98 | DEFINE_STATIC_PER_THREAD_DATA(unsigned int, msg_num, 0); |
81d6495f BP |
99 | |
100 | /* VLF_FILE configuration. | |
101 | * | |
102 | * All of the following is protected by 'log_file_mutex', which nests inside | |
103 | * pattern_rwlock. */ | |
834d6caf | 104 | static struct ovs_mutex log_file_mutex = OVS_MUTEX_INITIALIZER; |
97be1538 EJ |
105 | static char *log_file_name OVS_GUARDED_BY(log_file_mutex); |
106 | static int log_fd OVS_GUARDED_BY(log_file_mutex) = -1; | |
107 | static struct async_append *log_writer OVS_GUARDED_BY(log_file_mutex); | |
888e0cf4 | 108 | static bool log_async OVS_GUARDED_BY(log_file_mutex); |
064af421 | 109 | |
afc9f547 HM |
110 | /* Syslog export configuration. */ |
111 | static int syslog_fd OVS_GUARDED_BY(pattern_rwlock) = -1; | |
112 | ||
480ce8ab | 113 | static void format_log_message(const struct vlog_module *, enum vlog_level, |
afc9f547 | 114 | const char *pattern, |
064af421 | 115 | const char *message, va_list, struct ds *) |
cab50449 | 116 | OVS_PRINTF_FORMAT(4, 0); |
064af421 BP |
117 | |
118 | /* Searches the 'n_names' in 'names'. Returns the index of a match for | |
119 | * 'target', or 'n_names' if no name matches. */ | |
120 | static size_t | |
4b5f1d2c | 121 | search_name_array(const char *target, const char *const *names, size_t n_names) |
064af421 BP |
122 | { |
123 | size_t i; | |
124 | ||
125 | for (i = 0; i < n_names; i++) { | |
126 | assert(names[i]); | |
127 | if (!strcasecmp(names[i], target)) { | |
128 | break; | |
129 | } | |
130 | } | |
131 | return i; | |
132 | } | |
133 | ||
134 | /* Returns the name for logging level 'level'. */ | |
135 | const char * | |
136 | vlog_get_level_name(enum vlog_level level) | |
137 | { | |
138 | assert(level < VLL_N_LEVELS); | |
139 | return level_names[level]; | |
140 | } | |
141 | ||
142 | /* Returns the logging level with the given 'name', or VLL_N_LEVELS if 'name' | |
143 | * is not the name of a logging level. */ | |
144 | enum vlog_level | |
d295e8e9 | 145 | vlog_get_level_val(const char *name) |
064af421 BP |
146 | { |
147 | return search_name_array(name, level_names, ARRAY_SIZE(level_names)); | |
148 | } | |
149 | ||
150 | /* Returns the name for logging facility 'facility'. */ | |
151 | const char * | |
d295e8e9 | 152 | vlog_get_facility_name(enum vlog_facility facility) |
064af421 BP |
153 | { |
154 | assert(facility < VLF_N_FACILITIES); | |
155 | return facilities[facility].name; | |
156 | } | |
157 | ||
158 | /* Returns the logging facility named 'name', or VLF_N_FACILITIES if 'name' is | |
159 | * not the name of a logging facility. */ | |
160 | enum vlog_facility | |
d295e8e9 | 161 | vlog_get_facility_val(const char *name) |
064af421 BP |
162 | { |
163 | size_t i; | |
164 | ||
165 | for (i = 0; i < VLF_N_FACILITIES; i++) { | |
166 | if (!strcasecmp(facilities[i].name, name)) { | |
167 | break; | |
168 | } | |
169 | } | |
170 | return i; | |
171 | } | |
172 | ||
173 | /* Returns the name for logging module 'module'. */ | |
480ce8ab BP |
174 | const char * |
175 | vlog_get_module_name(const struct vlog_module *module) | |
064af421 | 176 | { |
480ce8ab | 177 | return module->name; |
064af421 BP |
178 | } |
179 | ||
480ce8ab BP |
180 | /* Returns the logging module named 'name', or NULL if 'name' is not the name |
181 | * of a logging module. */ | |
182 | struct vlog_module * | |
183 | vlog_module_from_name(const char *name) | |
064af421 | 184 | { |
86e504e1 | 185 | struct vlog_module *mp; |
480ce8ab | 186 | |
86e504e1 HS |
187 | LIST_FOR_EACH (mp, list, &vlog_modules) { |
188 | if (!strcasecmp(name, mp->name)) { | |
189 | return mp; | |
480ce8ab BP |
190 | } |
191 | } | |
86e504e1 | 192 | |
480ce8ab | 193 | return NULL; |
064af421 BP |
194 | } |
195 | ||
196 | /* Returns the current logging level for the given 'module' and 'facility'. */ | |
197 | enum vlog_level | |
d295e8e9 | 198 | vlog_get_level(const struct vlog_module *module, enum vlog_facility facility) |
064af421 | 199 | { |
064af421 | 200 | assert(facility < VLF_N_FACILITIES); |
480ce8ab | 201 | return module->levels[facility]; |
064af421 BP |
202 | } |
203 | ||
97be1538 EJ |
204 | static void |
205 | update_min_level(struct vlog_module *module) OVS_REQUIRES(&log_file_mutex) | |
064af421 | 206 | { |
064af421 BP |
207 | enum vlog_facility facility; |
208 | ||
c1a543a8 | 209 | module->min_level = VLL_OFF; |
064af421 | 210 | for (facility = 0; facility < VLF_N_FACILITIES; facility++) { |
9eb94563 | 211 | if (log_fd >= 0 || facility != VLF_FILE) { |
480ce8ab | 212 | enum vlog_level level = module->levels[facility]; |
56cee53b | 213 | if (level > module->min_level) { |
480ce8ab BP |
214 | module->min_level = level; |
215 | } | |
064af421 BP |
216 | } |
217 | } | |
064af421 BP |
218 | } |
219 | ||
220 | static void | |
480ce8ab | 221 | set_facility_level(enum vlog_facility facility, struct vlog_module *module, |
064af421 BP |
222 | enum vlog_level level) |
223 | { | |
224 | assert(facility >= 0 && facility < VLF_N_FACILITIES); | |
225 | assert(level < VLL_N_LEVELS); | |
226 | ||
97be1538 | 227 | ovs_mutex_lock(&log_file_mutex); |
480ce8ab | 228 | if (!module) { |
86e504e1 HS |
229 | struct vlog_module *mp; |
230 | LIST_FOR_EACH (mp, list, &vlog_modules) { | |
231 | mp->levels[facility] = level; | |
232 | update_min_level(mp); | |
064af421 BP |
233 | } |
234 | } else { | |
480ce8ab | 235 | module->levels[facility] = level; |
064af421 BP |
236 | update_min_level(module); |
237 | } | |
97be1538 | 238 | ovs_mutex_unlock(&log_file_mutex); |
064af421 BP |
239 | } |
240 | ||
480ce8ab BP |
241 | /* Sets the logging level for the given 'module' and 'facility' to 'level'. A |
242 | * null 'module' or a 'facility' of VLF_ANY_FACILITY is treated as a wildcard | |
243 | * across all modules or facilities, respectively. */ | |
064af421 | 244 | void |
480ce8ab | 245 | vlog_set_levels(struct vlog_module *module, enum vlog_facility facility, |
d295e8e9 | 246 | enum vlog_level level) |
064af421 BP |
247 | { |
248 | assert(facility < VLF_N_FACILITIES || facility == VLF_ANY_FACILITY); | |
249 | if (facility == VLF_ANY_FACILITY) { | |
250 | for (facility = 0; facility < VLF_N_FACILITIES; facility++) { | |
251 | set_facility_level(facility, module, level); | |
252 | } | |
253 | } else { | |
254 | set_facility_level(facility, module, level); | |
255 | } | |
256 | } | |
257 | ||
258 | static void | |
d295e8e9 | 259 | do_set_pattern(enum vlog_facility facility, const char *pattern) |
064af421 BP |
260 | { |
261 | struct facility *f = &facilities[facility]; | |
81d6495f | 262 | |
97be1538 | 263 | ovs_rwlock_wrlock(&pattern_rwlock); |
064af421 BP |
264 | if (!f->default_pattern) { |
265 | free(f->pattern); | |
266 | } else { | |
267 | f->default_pattern = false; | |
268 | } | |
269 | f->pattern = xstrdup(pattern); | |
97be1538 | 270 | ovs_rwlock_unlock(&pattern_rwlock); |
064af421 BP |
271 | } |
272 | ||
273 | /* Sets the pattern for the given 'facility' to 'pattern'. */ | |
274 | void | |
275 | vlog_set_pattern(enum vlog_facility facility, const char *pattern) | |
276 | { | |
277 | assert(facility < VLF_N_FACILITIES || facility == VLF_ANY_FACILITY); | |
278 | if (facility == VLF_ANY_FACILITY) { | |
279 | for (facility = 0; facility < VLF_N_FACILITIES; facility++) { | |
280 | do_set_pattern(facility, pattern); | |
281 | } | |
282 | } else { | |
283 | do_set_pattern(facility, pattern); | |
284 | } | |
285 | } | |
286 | ||
064af421 BP |
287 | /* Sets the name of the log file used by VLF_FILE to 'file_name', or to the |
288 | * default file name if 'file_name' is null. Returns 0 if successful, | |
289 | * otherwise a positive errno value. */ | |
290 | int | |
291 | vlog_set_log_file(const char *file_name) | |
292 | { | |
81d6495f | 293 | char *new_log_file_name; |
86e504e1 | 294 | struct vlog_module *mp; |
81d6495f BP |
295 | struct stat old_stat; |
296 | struct stat new_stat; | |
297 | int new_log_fd; | |
298 | bool same_file; | |
97be1538 | 299 | bool log_close; |
81d6495f BP |
300 | |
301 | /* Open new log file. */ | |
302 | new_log_file_name = (file_name | |
303 | ? xstrdup(file_name) | |
304 | : xasprintf("%s/%s.log", ovs_logdir(), program_name)); | |
305 | new_log_fd = open(new_log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666); | |
306 | if (new_log_fd < 0) { | |
307 | VLOG_WARN("failed to open %s for logging: %s", | |
308 | new_log_file_name, ovs_strerror(errno)); | |
309 | free(new_log_file_name); | |
310 | return errno; | |
311 | } | |
312 | ||
313 | /* If the new log file is the same one we already have open, bail out. */ | |
97be1538 | 314 | ovs_mutex_lock(&log_file_mutex); |
81d6495f BP |
315 | same_file = (log_fd >= 0 |
316 | && new_log_fd >= 0 | |
317 | && !fstat(log_fd, &old_stat) | |
318 | && !fstat(new_log_fd, &new_stat) | |
319 | && old_stat.st_dev == new_stat.st_dev | |
320 | && old_stat.st_ino == new_stat.st_ino); | |
97be1538 | 321 | ovs_mutex_unlock(&log_file_mutex); |
81d6495f BP |
322 | if (same_file) { |
323 | close(new_log_fd); | |
324 | free(new_log_file_name); | |
325 | return 0; | |
326 | } | |
064af421 | 327 | |
81d6495f | 328 | /* Log closing old log file (we can't log while holding log_file_mutex). */ |
97be1538 EJ |
329 | ovs_mutex_lock(&log_file_mutex); |
330 | log_close = log_fd >= 0; | |
331 | ovs_mutex_unlock(&log_file_mutex); | |
332 | if (log_close) { | |
064af421 | 333 | VLOG_INFO("closing log file"); |
81d6495f | 334 | } |
a5fb0e29 | 335 | |
81d6495f | 336 | /* Close old log file, if any, and install new one. */ |
97be1538 | 337 | ovs_mutex_lock(&log_file_mutex); |
81d6495f BP |
338 | if (log_fd >= 0) { |
339 | free(log_file_name); | |
9eb94563 | 340 | close(log_fd); |
81d6495f | 341 | async_append_destroy(log_writer); |
064af421 BP |
342 | } |
343 | ||
81d6495f BP |
344 | log_file_name = xstrdup(new_log_file_name); |
345 | log_fd = new_log_fd; | |
888e0cf4 BP |
346 | if (log_async) { |
347 | log_writer = async_append_create(new_log_fd); | |
348 | } | |
81d6495f | 349 | |
86e504e1 HS |
350 | LIST_FOR_EACH (mp, list, &vlog_modules) { |
351 | update_min_level(mp); | |
064af421 | 352 | } |
97be1538 | 353 | ovs_mutex_unlock(&log_file_mutex); |
064af421 | 354 | |
81d6495f BP |
355 | /* Log opening new log file (we can't log while holding log_file_mutex). */ |
356 | VLOG_INFO("opened log file %s", new_log_file_name); | |
357 | free(new_log_file_name); | |
064af421 | 358 | |
81d6495f | 359 | return 0; |
064af421 BP |
360 | } |
361 | ||
362 | /* Closes and then attempts to re-open the current log file. (This is useful | |
363 | * just after log rotation, to ensure that the new log file starts being used.) | |
364 | * Returns 0 if successful, otherwise a positive errno value. */ | |
365 | int | |
366 | vlog_reopen_log_file(void) | |
367 | { | |
81d6495f | 368 | char *fn; |
68cb8aaf | 369 | |
97be1538 | 370 | ovs_mutex_lock(&log_file_mutex); |
81d6495f | 371 | fn = log_file_name ? xstrdup(log_file_name) : NULL; |
97be1538 | 372 | ovs_mutex_unlock(&log_file_mutex); |
68cb8aaf | 373 | |
81d6495f BP |
374 | if (fn) { |
375 | int error = vlog_set_log_file(fn); | |
376 | free(fn); | |
377 | return error; | |
378 | } else { | |
68cb8aaf BP |
379 | return 0; |
380 | } | |
064af421 BP |
381 | } |
382 | ||
2a3e30b2 BP |
383 | /* Set debugging levels. Returns null if successful, otherwise an error |
384 | * message that the caller must free(). */ | |
064af421 BP |
385 | char * |
386 | vlog_set_levels_from_string(const char *s_) | |
387 | { | |
064af421 | 388 | char *s = xstrdup(s_); |
2a3e30b2 BP |
389 | char *save_ptr = NULL; |
390 | char *msg = NULL; | |
391 | char *word; | |
064af421 | 392 | |
2a3e30b2 BP |
393 | word = strtok_r(s, " ,:\t", &save_ptr); |
394 | if (word && !strcasecmp(word, "PATTERN")) { | |
395 | enum vlog_facility facility; | |
064af421 | 396 | |
2a3e30b2 BP |
397 | word = strtok_r(NULL, " ,:\t", &save_ptr); |
398 | if (!word) { | |
399 | msg = xstrdup("missing facility"); | |
400 | goto exit; | |
064af421 BP |
401 | } |
402 | ||
2a3e30b2 BP |
403 | facility = (!strcasecmp(word, "ANY") |
404 | ? VLF_ANY_FACILITY | |
405 | : vlog_get_facility_val(word)); | |
406 | if (facility == VLF_N_FACILITIES) { | |
407 | msg = xasprintf("unknown facility \"%s\"", word); | |
408 | goto exit; | |
409 | } | |
410 | vlog_set_pattern(facility, save_ptr); | |
411 | } else { | |
412 | struct vlog_module *module = NULL; | |
413 | enum vlog_level level = VLL_N_LEVELS; | |
414 | enum vlog_facility facility = VLF_N_FACILITIES; | |
415 | ||
416 | for (; word != NULL; word = strtok_r(NULL, " ,:\t", &save_ptr)) { | |
417 | if (!strcasecmp(word, "ANY")) { | |
418 | continue; | |
419 | } else if (vlog_get_facility_val(word) != VLF_N_FACILITIES) { | |
420 | if (facility != VLF_N_FACILITIES) { | |
421 | msg = xstrdup("cannot specify multiple facilities"); | |
422 | goto exit; | |
064af421 | 423 | } |
2a3e30b2 BP |
424 | facility = vlog_get_facility_val(word); |
425 | } else if (vlog_get_level_val(word) != VLL_N_LEVELS) { | |
426 | if (level != VLL_N_LEVELS) { | |
427 | msg = xstrdup("cannot specify multiple levels"); | |
428 | goto exit; | |
429 | } | |
430 | level = vlog_get_level_val(word); | |
431 | } else if (vlog_module_from_name(word)) { | |
432 | if (module) { | |
433 | msg = xstrdup("cannot specify multiple modules"); | |
434 | goto exit; | |
435 | } | |
436 | module = vlog_module_from_name(word); | |
437 | } else { | |
438 | msg = xasprintf("no facility, level, or module \"%s\"", word); | |
439 | goto exit; | |
064af421 | 440 | } |
2a3e30b2 | 441 | } |
064af421 | 442 | |
2a3e30b2 BP |
443 | if (facility == VLF_N_FACILITIES) { |
444 | facility = VLF_ANY_FACILITY; | |
445 | } | |
446 | if (level == VLL_N_LEVELS) { | |
447 | level = VLL_DBG; | |
064af421 | 448 | } |
2a3e30b2 | 449 | vlog_set_levels(module, facility, level); |
064af421 | 450 | } |
2a3e30b2 BP |
451 | |
452 | exit: | |
064af421 | 453 | free(s); |
2a3e30b2 | 454 | return msg; |
064af421 BP |
455 | } |
456 | ||
316bd0f8 BP |
457 | /* Set debugging levels. Abort with an error message if 's' is invalid. */ |
458 | void | |
459 | vlog_set_levels_from_string_assert(const char *s) | |
460 | { | |
461 | char *error = vlog_set_levels_from_string(s); | |
462 | if (error) { | |
463 | ovs_fatal(0, "%s", error); | |
464 | } | |
465 | } | |
466 | ||
064af421 BP |
467 | /* If 'arg' is null, configure maximum verbosity. Otherwise, sets |
468 | * configuration according to 'arg' (see vlog_set_levels_from_string()). */ | |
469 | void | |
470 | vlog_set_verbosity(const char *arg) | |
471 | { | |
472 | if (arg) { | |
473 | char *msg = vlog_set_levels_from_string(arg); | |
474 | if (msg) { | |
475 | ovs_fatal(0, "processing \"%s\": %s", arg, msg); | |
476 | } | |
477 | } else { | |
480ce8ab | 478 | vlog_set_levels(NULL, VLF_ANY_FACILITY, VLL_DBG); |
064af421 BP |
479 | } |
480 | } | |
481 | ||
afc9f547 HM |
482 | /* Set the vlog udp syslog target. */ |
483 | void | |
484 | vlog_set_syslog_target(const char *target) | |
485 | { | |
486 | int new_fd; | |
487 | ||
488 | inet_open_active(SOCK_DGRAM, target, 0, NULL, &new_fd, 0); | |
489 | ||
490 | ovs_rwlock_wrlock(&pattern_rwlock); | |
491 | if (syslog_fd >= 0) { | |
492 | close(syslog_fd); | |
493 | } | |
494 | syslog_fd = new_fd; | |
495 | ovs_rwlock_unlock(&pattern_rwlock); | |
496 | } | |
497 | ||
064af421 | 498 | static void |
0e15264f BP |
499 | vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[], |
500 | void *aux OVS_UNUSED) | |
064af421 | 501 | { |
0e15264f BP |
502 | int i; |
503 | ||
504 | for (i = 1; i < argc; i++) { | |
505 | char *msg = vlog_set_levels_from_string(argv[i]); | |
506 | if (msg) { | |
bde9f75d | 507 | unixctl_command_reply_error(conn, msg); |
0e15264f BP |
508 | free(msg); |
509 | return; | |
510 | } | |
511 | } | |
bde9f75d | 512 | unixctl_command_reply(conn, NULL); |
064af421 BP |
513 | } |
514 | ||
515 | static void | |
0e15264f BP |
516 | vlog_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED, |
517 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
064af421 BP |
518 | { |
519 | char *msg = vlog_get_levels(); | |
bde9f75d | 520 | unixctl_command_reply(conn, msg); |
064af421 BP |
521 | free(msg); |
522 | } | |
523 | ||
524 | static void | |
0e15264f BP |
525 | vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED, |
526 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
064af421 | 527 | { |
97be1538 EJ |
528 | bool has_log_file; |
529 | ||
530 | ovs_mutex_lock(&log_file_mutex); | |
531 | has_log_file = log_file_name != NULL; | |
532 | ovs_mutex_unlock(&log_file_mutex); | |
533 | ||
534 | if (has_log_file) { | |
064af421 BP |
535 | int error = vlog_reopen_log_file(); |
536 | if (error) { | |
10a89ef0 | 537 | unixctl_command_reply_error(conn, ovs_strerror(errno)); |
064af421 | 538 | } else { |
bde9f75d | 539 | unixctl_command_reply(conn, NULL); |
064af421 BP |
540 | } |
541 | } else { | |
bde9f75d | 542 | unixctl_command_reply_error(conn, "Logging to file not configured"); |
064af421 BP |
543 | } |
544 | } | |
545 | ||
4958e3ee BP |
546 | static void |
547 | set_all_rate_limits(bool enable) | |
548 | { | |
86e504e1 | 549 | struct vlog_module *mp; |
4958e3ee | 550 | |
86e504e1 HS |
551 | LIST_FOR_EACH (mp, list, &vlog_modules) { |
552 | mp->honor_rate_limits = enable; | |
4958e3ee BP |
553 | } |
554 | } | |
555 | ||
556 | static void | |
557 | set_rate_limits(struct unixctl_conn *conn, int argc, | |
558 | const char *argv[], bool enable) | |
559 | { | |
560 | if (argc > 1) { | |
561 | int i; | |
562 | ||
563 | for (i = 1; i < argc; i++) { | |
564 | if (!strcasecmp(argv[i], "ANY")) { | |
565 | set_all_rate_limits(enable); | |
566 | } else { | |
567 | struct vlog_module *module = vlog_module_from_name(argv[i]); | |
568 | if (!module) { | |
569 | unixctl_command_reply_error(conn, "unknown module"); | |
570 | return; | |
571 | } | |
572 | module->honor_rate_limits = enable; | |
573 | } | |
574 | } | |
575 | } else { | |
576 | set_all_rate_limits(enable); | |
577 | } | |
578 | unixctl_command_reply(conn, NULL); | |
579 | } | |
580 | ||
581 | static void | |
582 | vlog_enable_rate_limit(struct unixctl_conn *conn, int argc, | |
583 | const char *argv[], void *aux OVS_UNUSED) | |
584 | { | |
585 | set_rate_limits(conn, argc, argv, true); | |
586 | } | |
587 | ||
588 | static void | |
589 | vlog_disable_rate_limit(struct unixctl_conn *conn, int argc, | |
590 | const char *argv[], void *aux OVS_UNUSED) | |
591 | { | |
592 | set_rate_limits(conn, argc, argv, false); | |
593 | } | |
594 | ||
81d6495f BP |
595 | /* Initializes the logging subsystem and registers its unixctl server |
596 | * commands. */ | |
597 | void | |
598 | vlog_init(void) | |
599 | { | |
c3b758f6 BP |
600 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
601 | ||
602 | if (ovsthread_once_start(&once)) { | |
603 | static char *program_name_copy; | |
604 | long long int now; | |
605 | ||
606 | /* Do initialization work that needs to be done before any logging | |
607 | * occurs. We want to keep this really minimal because any attempt to | |
608 | * log anything before calling ovsthread_once_done() will deadlock. */ | |
609 | ||
610 | /* openlog() is allowed to keep the pointer passed in, without making a | |
611 | * copy. The daemonize code sometimes frees and replaces | |
612 | * 'program_name', so make a private copy just for openlog(). (We keep | |
613 | * a pointer to the private copy to suppress memory leak warnings in | |
614 | * case openlog() does make its own copy.) */ | |
615 | program_name_copy = program_name ? xstrdup(program_name) : NULL; | |
616 | openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON); | |
617 | ovsthread_once_done(&once); | |
618 | ||
619 | /* Now do anything that we want to happen only once but doesn't have to | |
620 | * finish before we start logging. */ | |
621 | ||
622 | now = time_wall_msec(); | |
623 | if (now < 0) { | |
624 | char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true); | |
625 | VLOG_ERR("current time is negative: %s (%lld)", s, now); | |
626 | free(s); | |
627 | } | |
628 | ||
629 | unixctl_command_register( | |
630 | "vlog/set", "{spec | PATTERN:facility:pattern}", | |
631 | 1, INT_MAX, vlog_unixctl_set, NULL); | |
632 | unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list, | |
633 | NULL); | |
634 | unixctl_command_register("vlog/enable-rate-limit", "[module]...", | |
635 | 0, INT_MAX, vlog_enable_rate_limit, NULL); | |
636 | unixctl_command_register("vlog/disable-rate-limit", "[module]...", | |
637 | 0, INT_MAX, vlog_disable_rate_limit, NULL); | |
638 | unixctl_command_register("vlog/reopen", "", 0, 0, | |
639 | vlog_unixctl_reopen, NULL); | |
640 | } | |
81d6495f BP |
641 | } |
642 | ||
888e0cf4 BP |
643 | /* Enables VLF_FILE log output to be written asynchronously to disk. |
644 | * Asynchronous file writes avoid blocking the process in the case of a busy | |
645 | * disk, but on the other hand they are less robust: there is a chance that the | |
646 | * write will not make it to the log file if the process crashes soon after the | |
647 | * log call. */ | |
648 | void | |
649 | vlog_enable_async(void) | |
650 | { | |
651 | ovs_mutex_lock(&log_file_mutex); | |
652 | log_async = true; | |
653 | if (log_fd >= 0 && !log_writer) { | |
654 | log_writer = async_append_create(log_fd); | |
655 | } | |
656 | ovs_mutex_unlock(&log_file_mutex); | |
657 | } | |
658 | ||
064af421 BP |
659 | /* Print the current logging level for each module. */ |
660 | char * | |
661 | vlog_get_levels(void) | |
662 | { | |
663 | struct ds s = DS_EMPTY_INITIALIZER; | |
86e504e1 | 664 | struct vlog_module *mp; |
8628b0b7 JP |
665 | struct svec lines = SVEC_EMPTY_INITIALIZER; |
666 | char *line; | |
667 | size_t i; | |
064af421 BP |
668 | |
669 | ds_put_format(&s, " console syslog file\n"); | |
670 | ds_put_format(&s, " ------- ------ ------\n"); | |
671 | ||
86e504e1 | 672 | LIST_FOR_EACH (mp, list, &vlog_modules) { |
4958e3ee BP |
673 | struct ds line; |
674 | ||
675 | ds_init(&line); | |
676 | ds_put_format(&line, "%-16s %4s %4s %4s", | |
86e504e1 HS |
677 | vlog_get_module_name(mp), |
678 | vlog_get_level_name(vlog_get_level(mp, VLF_CONSOLE)), | |
679 | vlog_get_level_name(vlog_get_level(mp, VLF_SYSLOG)), | |
680 | vlog_get_level_name(vlog_get_level(mp, VLF_FILE))); | |
681 | if (!mp->honor_rate_limits) { | |
4958e3ee BP |
682 | ds_put_cstr(&line, " (rate limiting disabled)"); |
683 | } | |
684 | ds_put_char(&line, '\n'); | |
685 | ||
686 | svec_add_nocopy(&lines, ds_steal_cstr(&line)); | |
8628b0b7 JP |
687 | } |
688 | ||
689 | svec_sort(&lines); | |
690 | SVEC_FOR_EACH (i, line, &lines) { | |
691 | ds_put_cstr(&s, line); | |
064af421 | 692 | } |
8628b0b7 | 693 | svec_destroy(&lines); |
064af421 BP |
694 | |
695 | return ds_cstr(&s); | |
696 | } | |
697 | ||
698 | /* Returns true if a log message emitted for the given 'module' and 'level' | |
699 | * would cause some log output, false if that module and level are completely | |
700 | * disabled. */ | |
701 | bool | |
480ce8ab | 702 | vlog_is_enabled(const struct vlog_module *module, enum vlog_level level) |
064af421 | 703 | { |
480ce8ab | 704 | return module->min_level >= level; |
064af421 BP |
705 | } |
706 | ||
707 | static const char * | |
708 | fetch_braces(const char *p, const char *def, char *out, size_t out_size) | |
709 | { | |
710 | if (*p == '{') { | |
711 | size_t n = strcspn(p + 1, "}"); | |
712 | size_t n_copy = MIN(n, out_size - 1); | |
713 | memcpy(out, p + 1, n_copy); | |
714 | out[n_copy] = '\0'; | |
715 | p += n + 2; | |
716 | } else { | |
717 | ovs_strlcpy(out, def, out_size); | |
718 | } | |
719 | return p; | |
720 | } | |
721 | ||
722 | static void | |
480ce8ab | 723 | format_log_message(const struct vlog_module *module, enum vlog_level level, |
afc9f547 HM |
724 | const char *pattern, const char *message, |
725 | va_list args_, struct ds *s) | |
064af421 BP |
726 | { |
727 | char tmp[128]; | |
728 | va_list args; | |
729 | const char *p; | |
730 | ||
731 | ds_clear(s); | |
afc9f547 | 732 | for (p = pattern; *p != '\0'; ) { |
bc9fb3a9 | 733 | const char *subprogram_name; |
064af421 BP |
734 | enum { LEFT, RIGHT } justify = RIGHT; |
735 | int pad = '0'; | |
736 | size_t length, field, used; | |
737 | ||
738 | if (*p != '%') { | |
739 | ds_put_char(s, *p++); | |
740 | continue; | |
741 | } | |
742 | ||
743 | p++; | |
744 | if (*p == '-') { | |
745 | justify = LEFT; | |
746 | p++; | |
747 | } | |
748 | if (*p == '0') { | |
749 | pad = '0'; | |
750 | p++; | |
751 | } | |
752 | field = 0; | |
be2c418b | 753 | while (isdigit((unsigned char)*p)) { |
064af421 BP |
754 | field = (field * 10) + (*p - '0'); |
755 | p++; | |
756 | } | |
757 | ||
758 | length = s->length; | |
759 | switch (*p++) { | |
760 | case 'A': | |
761 | ds_put_cstr(s, program_name); | |
762 | break; | |
afc9f547 HM |
763 | case 'B': |
764 | ds_put_format(s, "%d", LOG_LOCAL0 + syslog_levels[level]); | |
765 | break; | |
064af421 BP |
766 | case 'c': |
767 | p = fetch_braces(p, "", tmp, sizeof tmp); | |
768 | ds_put_cstr(s, vlog_get_module_name(module)); | |
769 | break; | |
770 | case 'd': | |
2b31d8e7 PI |
771 | p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp); |
772 | ds_put_strftime_msec(s, tmp, time_wall_msec(), false); | |
b5d29991 GS |
773 | break; |
774 | case 'D': | |
2b31d8e7 PI |
775 | p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp); |
776 | ds_put_strftime_msec(s, tmp, time_wall_msec(), true); | |
064af421 | 777 | break; |
afc9f547 HM |
778 | case 'E': |
779 | gethostname(tmp, sizeof tmp); | |
780 | tmp[sizeof tmp - 1] = '\0'; | |
781 | ds_put_cstr(s, tmp); | |
782 | break; | |
064af421 BP |
783 | case 'm': |
784 | /* Format user-supplied log message and trim trailing new-lines. */ | |
785 | length = s->length; | |
786 | va_copy(args, args_); | |
787 | ds_put_format_valist(s, message, args); | |
788 | va_end(args); | |
789 | while (s->length > length && s->string[s->length - 1] == '\n') { | |
790 | s->length--; | |
791 | } | |
792 | break; | |
793 | case 'N': | |
81d6495f | 794 | ds_put_format(s, "%u", *msg_num_get_unsafe()); |
064af421 BP |
795 | break; |
796 | case 'n': | |
797 | ds_put_char(s, '\n'); | |
798 | break; | |
799 | case 'p': | |
800 | ds_put_cstr(s, vlog_get_level_name(level)); | |
801 | break; | |
802 | case 'P': | |
803 | ds_put_format(s, "%ld", (long int) getpid()); | |
804 | break; | |
805 | case 'r': | |
4ae90ff9 | 806 | ds_put_format(s, "%lld", time_msec() - time_boot_msec()); |
064af421 | 807 | break; |
781dee08 | 808 | case 't': |
bc9fb3a9 | 809 | subprogram_name = get_subprogram_name(); |
781dee08 BP |
810 | ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main"); |
811 | break; | |
812 | case 'T': | |
bc9fb3a9 | 813 | subprogram_name = get_subprogram_name(); |
781dee08 BP |
814 | if (subprogram_name[0]) { |
815 | ds_put_format(s, "(%s)", subprogram_name); | |
816 | } | |
817 | break; | |
064af421 BP |
818 | default: |
819 | ds_put_char(s, p[-1]); | |
820 | break; | |
821 | } | |
822 | used = s->length - length; | |
823 | if (used < field) { | |
824 | size_t n_pad = field - used; | |
825 | if (justify == RIGHT) { | |
826 | ds_put_uninit(s, n_pad); | |
827 | memmove(&s->string[length + n_pad], &s->string[length], used); | |
828 | memset(&s->string[length], pad, n_pad); | |
829 | } else { | |
830 | ds_put_char_multiple(s, pad, n_pad); | |
831 | } | |
832 | } | |
833 | } | |
834 | } | |
835 | ||
afc9f547 HM |
836 | /* Exports the given 'syslog_message' to the configured udp syslog sink. */ |
837 | static void | |
838 | send_to_syslog_fd(const char *s, size_t length) | |
839 | OVS_REQ_RDLOCK(pattern_rwlock) | |
840 | { | |
841 | static size_t max_length = SIZE_MAX; | |
842 | size_t send_len = MIN(length, max_length); | |
843 | ||
844 | while (write(syslog_fd, s, send_len) < 0 && errno == EMSGSIZE) { | |
845 | send_len -= send_len / 20; | |
846 | max_length = send_len; | |
847 | } | |
848 | } | |
849 | ||
064af421 BP |
850 | /* Writes 'message' to the log at the given 'level' and as coming from the |
851 | * given 'module'. | |
852 | * | |
853 | * Guaranteed to preserve errno. */ | |
854 | void | |
480ce8ab | 855 | vlog_valist(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
856 | const char *message, va_list args) |
857 | { | |
480ce8ab BP |
858 | bool log_to_console = module->levels[VLF_CONSOLE] >= level; |
859 | bool log_to_syslog = module->levels[VLF_SYSLOG] >= level; | |
97be1538 EJ |
860 | bool log_to_file; |
861 | ||
862 | ovs_mutex_lock(&log_file_mutex); | |
863 | log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0; | |
864 | ovs_mutex_unlock(&log_file_mutex); | |
064af421 BP |
865 | if (log_to_console || log_to_syslog || log_to_file) { |
866 | int save_errno = errno; | |
064af421 BP |
867 | struct ds s; |
868 | ||
1e8cf0f7 BP |
869 | vlog_init(); |
870 | ||
064af421 BP |
871 | ds_init(&s); |
872 | ds_reserve(&s, 1024); | |
81d6495f | 873 | ++*msg_num_get(); |
064af421 | 874 | |
97be1538 | 875 | ovs_rwlock_rdlock(&pattern_rwlock); |
064af421 | 876 | if (log_to_console) { |
afc9f547 HM |
877 | format_log_message(module, level, facilities[VLF_CONSOLE].pattern, |
878 | message, args, &s); | |
064af421 BP |
879 | ds_put_char(&s, '\n'); |
880 | fputs(ds_cstr(&s), stderr); | |
881 | } | |
882 | ||
883 | if (log_to_syslog) { | |
884 | int syslog_level = syslog_levels[level]; | |
885 | char *save_ptr = NULL; | |
886 | char *line; | |
887 | ||
afc9f547 HM |
888 | format_log_message(module, level, facilities[VLF_SYSLOG].pattern, |
889 | message, args, &s); | |
064af421 BP |
890 | for (line = strtok_r(s.string, "\n", &save_ptr); line; |
891 | line = strtok_r(NULL, "\n", &save_ptr)) { | |
892 | syslog(syslog_level, "%s", line); | |
893 | } | |
afc9f547 HM |
894 | |
895 | if (syslog_fd >= 0) { | |
896 | format_log_message(module, level, | |
897 | "<%B>1 %D{%Y-%m-%dT%H:%M:%S.###Z} " | |
898 | "%E %A %P %c - \xef\xbb\xbf%m", | |
899 | message, args, &s); | |
900 | send_to_syslog_fd(ds_cstr(&s), s.length); | |
901 | } | |
064af421 BP |
902 | } |
903 | ||
904 | if (log_to_file) { | |
afc9f547 HM |
905 | format_log_message(module, level, facilities[VLF_FILE].pattern, |
906 | message, args, &s); | |
064af421 | 907 | ds_put_char(&s, '\n'); |
81d6495f | 908 | |
97be1538 | 909 | ovs_mutex_lock(&log_file_mutex); |
81d6495f | 910 | if (log_fd >= 0) { |
888e0cf4 BP |
911 | if (log_writer) { |
912 | async_append_write(log_writer, s.string, s.length); | |
913 | if (level == VLL_EMER) { | |
914 | async_append_flush(log_writer); | |
915 | } | |
916 | } else { | |
917 | ignore(write(log_fd, s.string, s.length)); | |
81d6495f | 918 | } |
a5fb0e29 | 919 | } |
97be1538 | 920 | ovs_mutex_unlock(&log_file_mutex); |
064af421 | 921 | } |
97be1538 | 922 | ovs_rwlock_unlock(&pattern_rwlock); |
064af421 BP |
923 | |
924 | ds_destroy(&s); | |
925 | errno = save_errno; | |
926 | } | |
927 | } | |
928 | ||
929 | void | |
480ce8ab BP |
930 | vlog(const struct vlog_module *module, enum vlog_level level, |
931 | const char *message, ...) | |
064af421 BP |
932 | { |
933 | va_list args; | |
934 | ||
935 | va_start(args, message); | |
936 | vlog_valist(module, level, message, args); | |
937 | va_end(args); | |
938 | } | |
939 | ||
d41d4b71 BP |
940 | /* Logs 'message' to 'module' at maximum verbosity, then exits with a failure |
941 | * exit code. Always writes the message to stderr, even if the console | |
942 | * facility is disabled. | |
943 | * | |
944 | * Choose this function instead of vlog_abort_valist() if the daemon monitoring | |
945 | * facility shouldn't automatically restart the current daemon. */ | |
279c9e03 | 946 | void |
c1a543a8 | 947 | vlog_fatal_valist(const struct vlog_module *module_, |
279c9e03 BP |
948 | const char *message, va_list args) |
949 | { | |
ebc56baa | 950 | struct vlog_module *module = CONST_CAST(struct vlog_module *, module_); |
279c9e03 BP |
951 | |
952 | /* Don't log this message to the console to avoid redundancy with the | |
953 | * message written by the later ovs_fatal_valist(). */ | |
c1a543a8 | 954 | module->levels[VLF_CONSOLE] = VLL_OFF; |
279c9e03 | 955 | |
c1a543a8 | 956 | vlog_valist(module, VLL_EMER, message, args); |
279c9e03 BP |
957 | ovs_fatal_valist(0, message, args); |
958 | } | |
959 | ||
d41d4b71 BP |
960 | /* Logs 'message' to 'module' at maximum verbosity, then exits with a failure |
961 | * exit code. Always writes the message to stderr, even if the console | |
962 | * facility is disabled. | |
963 | * | |
964 | * Choose this function instead of vlog_abort() if the daemon monitoring | |
965 | * facility shouldn't automatically restart the current daemon. */ | |
279c9e03 | 966 | void |
c1a543a8 | 967 | vlog_fatal(const struct vlog_module *module, const char *message, ...) |
279c9e03 BP |
968 | { |
969 | va_list args; | |
970 | ||
971 | va_start(args, message); | |
c1a543a8 | 972 | vlog_fatal_valist(module, message, args); |
279c9e03 BP |
973 | va_end(args); |
974 | } | |
975 | ||
d41d4b71 BP |
976 | /* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always |
977 | * writes the message to stderr, even if the console facility is disabled. | |
978 | * | |
979 | * Choose this function instead of vlog_fatal_valist() if the daemon monitoring | |
980 | * facility should automatically restart the current daemon. */ | |
981 | void | |
982 | vlog_abort_valist(const struct vlog_module *module_, | |
983 | const char *message, va_list args) | |
984 | { | |
985 | struct vlog_module *module = (struct vlog_module *) module_; | |
986 | ||
987 | /* Don't log this message to the console to avoid redundancy with the | |
988 | * message written by the later ovs_abort_valist(). */ | |
989 | module->levels[VLF_CONSOLE] = VLL_OFF; | |
990 | ||
991 | vlog_valist(module, VLL_EMER, message, args); | |
992 | ovs_abort_valist(0, message, args); | |
993 | } | |
994 | ||
995 | /* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always | |
996 | * writes the message to stderr, even if the console facility is disabled. | |
997 | * | |
998 | * Choose this function instead of vlog_fatal() if the daemon monitoring | |
999 | * facility should automatically restart the current daemon. */ | |
1000 | void | |
1001 | vlog_abort(const struct vlog_module *module, const char *message, ...) | |
1002 | { | |
1003 | va_list args; | |
1004 | ||
1005 | va_start(args, message); | |
1006 | vlog_abort_valist(module, message, args); | |
1007 | va_end(args); | |
1008 | } | |
1009 | ||
064af421 | 1010 | bool |
480ce8ab | 1011 | vlog_should_drop(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
1012 | struct vlog_rate_limit *rl) |
1013 | { | |
4958e3ee BP |
1014 | if (!module->honor_rate_limits) { |
1015 | return false; | |
1016 | } | |
1017 | ||
064af421 BP |
1018 | if (!vlog_is_enabled(module, level)) { |
1019 | return true; | |
1020 | } | |
1021 | ||
97be1538 | 1022 | ovs_mutex_lock(&rl->mutex); |
648f4f1f | 1023 | if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) { |
064af421 | 1024 | time_t now = time_now(); |
648f4f1f BP |
1025 | if (!rl->n_dropped) { |
1026 | rl->first_dropped = now; | |
064af421 | 1027 | } |
648f4f1f BP |
1028 | rl->last_dropped = now; |
1029 | rl->n_dropped++; | |
97be1538 | 1030 | ovs_mutex_unlock(&rl->mutex); |
648f4f1f | 1031 | return true; |
064af421 | 1032 | } |
064af421 | 1033 | |
fda28014 | 1034 | if (!rl->n_dropped) { |
97be1538 | 1035 | ovs_mutex_unlock(&rl->mutex); |
fda28014 | 1036 | } else { |
e2eed6a7 | 1037 | time_t now = time_now(); |
fda28014 | 1038 | unsigned int n_dropped = rl->n_dropped; |
e2eed6a7 BP |
1039 | unsigned int first_dropped_elapsed = now - rl->first_dropped; |
1040 | unsigned int last_dropped_elapsed = now - rl->last_dropped; | |
fda28014 | 1041 | rl->n_dropped = 0; |
97be1538 | 1042 | ovs_mutex_unlock(&rl->mutex); |
e2eed6a7 | 1043 | |
064af421 | 1044 | vlog(module, level, |
e2eed6a7 BP |
1045 | "Dropped %u log messages in last %u seconds (most recently, " |
1046 | "%u seconds ago) due to excessive rate", | |
fda28014 | 1047 | n_dropped, first_dropped_elapsed, last_dropped_elapsed); |
064af421 | 1048 | } |
fda28014 | 1049 | |
064af421 BP |
1050 | return false; |
1051 | } | |
1052 | ||
1053 | void | |
480ce8ab | 1054 | vlog_rate_limit(const struct vlog_module *module, enum vlog_level level, |
064af421 BP |
1055 | struct vlog_rate_limit *rl, const char *message, ...) |
1056 | { | |
1057 | if (!vlog_should_drop(module, level, rl)) { | |
1058 | va_list args; | |
1059 | ||
1060 | va_start(args, message); | |
1061 | vlog_valist(module, level, message, args); | |
1062 | va_end(args); | |
1063 | } | |
1064 | } | |
1065 | ||
1066 | void | |
d295e8e9 | 1067 | vlog_usage(void) |
064af421 | 1068 | { |
afc9f547 HM |
1069 | printf("\n\ |
1070 | Logging options:\n\ | |
1071 | -vSPEC, --verbose=SPEC set logging levels\n\ | |
1072 | -v, --verbose set maximum verbosity level\n\ | |
1073 | --log-file[=FILE] enable logging to specified FILE\n\ | |
1074 | (default: %s/%s.log)\n\ | |
1075 | --syslog-target=HOST:PORT also send syslog msgs to HOST:PORT via UDP\n", | |
b43c6fe2 | 1076 | ovs_logdir(), program_name); |
064af421 | 1077 | } |