X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fvlog.c;h=193e4f7483133ca9f4954203c29ad93ddf641975;hb=a39c3ff9f586d3416976d3c3bd095fa81d73efa0;hp=899072e451e58d934a2aef628686a69397d266b2;hpb=487657b2b82da6516f6b96f7dacc92db9f28ede6;p=sliver-openvswitch.git diff --git a/lib/vlog.c b/lib/vlog.c index 899072e45..193e4f748 100644 --- a/lib/vlog.c +++ b/lib/vlog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,25 +28,35 @@ #include #include #include +#include "coverage.h" #include "dirs.h" #include "dynamic-string.h" +#include "ofpbuf.h" #include "sat-math.h" #include "svec.h" #include "timeval.h" #include "unixctl.h" #include "util.h" +#include "worker.h" VLOG_DEFINE_THIS_MODULE(vlog); +COVERAGE_DEFINE(vlog_recursive); + +/* ovs_assert() logs the assertion message, so using ovs_assert() in this + * source file could cause recursion. */ +#undef ovs_assert +#define ovs_assert use_assert_instead_of_ovs_assert_in_this_module + /* Name for each logging level. */ -static const char *level_names[VLL_N_LEVELS] = { +static const char *const level_names[VLL_N_LEVELS] = { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME, VLOG_LEVELS #undef VLOG_LEVEL }; /* Syslog value for each logging level. */ -static int syslog_levels[VLL_N_LEVELS] = { +static const int syslog_levels[VLL_N_LEVELS] = { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) SYSLOG_LEVEL, VLOG_LEVELS #undef VLOG_LEVEL @@ -84,7 +95,7 @@ static struct facility facilities[VLF_N_FACILITIES] = { /* VLF_FILE configuration. */ static char *log_file_name; -static FILE *log_file; +static int log_fd = -1; /* vlog initialized? */ static bool vlog_inited; @@ -93,11 +104,13 @@ static void format_log_message(const struct vlog_module *, enum vlog_level, enum vlog_facility, unsigned int msg_num, const char *message, va_list, struct ds *) PRINTF_FORMAT(5, 0); +static void vlog_write_file(struct ds *); +static void vlog_update_async_log_fd(void); /* Searches the 'n_names' in 'names'. Returns the index of a match for * 'target', or 'n_names' if no name matches. */ static size_t -search_name_array(const char *target, const char **names, size_t n_names) +search_name_array(const char *target, const char *const *names, size_t n_names) { size_t i; @@ -186,7 +199,7 @@ update_min_level(struct vlog_module *module) module->min_level = VLL_OFF; for (facility = 0; facility < VLF_N_FACILITIES; facility++) { - if (log_file || facility != VLF_FILE) { + if (log_fd >= 0 || facility != VLF_FILE) { enum vlog_level level = module->levels[facility]; if (level > module->min_level) { module->min_level = level; @@ -279,10 +292,10 @@ vlog_set_log_file(const char *file_name) int error; /* Close old log file. */ - if (log_file) { + if (log_fd >= 0) { VLOG_INFO("closing log file"); - fclose(log_file); - log_file = NULL; + close(log_fd); + log_fd = -1; } /* Update log file name and free old name. The ordering is important @@ -296,13 +309,16 @@ vlog_set_log_file(const char *file_name) /* Open new log file and update min_levels[] to reflect whether we actually * have a log_file. */ - log_file = fopen(log_file_name, "a"); + log_fd = open(log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (log_fd >= 0) { + vlog_update_async_log_fd(); + } for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { update_min_level(*mp); } /* Log success or failure. */ - if (!log_file) { + if (log_fd < 0) { VLOG_WARN("failed to open %s for logging: %s", log_file_name, strerror(errno)); error = errno; @@ -330,8 +346,8 @@ vlog_reopen_log_file(void) /* Skip re-opening if it would be a no-op because the old and new files are * the same. (This avoids writing "closing log file" followed immediately * by "opened log file".) */ - if (log_file - && !fstat(fileno(log_file), &old) + if (log_fd >= 0 + && !fstat(log_fd, &old) && !stat(log_file_name, &new) && old.st_dev == new.st_dev && old.st_ino == new.st_ino) { @@ -415,6 +431,16 @@ exit: return msg; } +/* Set debugging levels. Abort with an error message if 's' is invalid. */ +void +vlog_set_levels_from_string_assert(const char *s) +{ + char *error = vlog_set_levels_from_string(s); + if (error) { + ovs_fatal(0, "%s", error); + } +} + /* If 'arg' is null, configure maximum verbosity. Otherwise, sets * configuration according to 'arg' (see vlog_set_levels_from_string()). */ void @@ -472,6 +498,55 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED, } } +static void +set_all_rate_limits(bool enable) +{ + struct vlog_module **mp; + + for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { + (*mp)->honor_rate_limits = enable; + } +} + +static void +set_rate_limits(struct unixctl_conn *conn, int argc, + const char *argv[], bool enable) +{ + if (argc > 1) { + int i; + + for (i = 1; i < argc; i++) { + if (!strcasecmp(argv[i], "ANY")) { + set_all_rate_limits(enable); + } else { + struct vlog_module *module = vlog_module_from_name(argv[i]); + if (!module) { + unixctl_command_reply_error(conn, "unknown module"); + return; + } + module->honor_rate_limits = enable; + } + } + } else { + set_all_rate_limits(enable); + } + unixctl_command_reply(conn, NULL); +} + +static void +vlog_enable_rate_limit(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + set_rate_limits(conn, argc, argv, true); +} + +static void +vlog_disable_rate_limit(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + set_rate_limits(conn, argc, argv, false); +} + /* Initializes the logging subsystem and registers its unixctl server * commands. */ void @@ -495,18 +570,19 @@ vlog_init(void) now = time_wall(); if (now < 0) { - struct tm tm; - char s[128]; - - localtime_r(&now, &tm); - strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S %z", &tm); + char *s = xastrftime("%a, %d %b %Y %H:%M:%S", now, true); VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now); + free(s); } unixctl_command_register( "vlog/set", "{spec | PATTERN:facility:pattern}", 1, INT_MAX, vlog_unixctl_set, NULL); unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list, NULL); + unixctl_command_register("vlog/enable-rate-limit", "[module]...", + 0, INT_MAX, vlog_enable_rate_limit, NULL); + unixctl_command_register("vlog/disable-rate-limit", "[module]...", + 0, INT_MAX, vlog_disable_rate_limit, NULL); unixctl_command_register("vlog/reopen", "", 0, 0, vlog_unixctl_reopen, NULL); } @@ -535,12 +611,20 @@ vlog_get_levels(void) ds_put_format(&s, " ------- ------ ------\n"); for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) { - line = xasprintf("%-16s %4s %4s %4s\n", - vlog_get_module_name(*mp), - vlog_get_level_name(vlog_get_level(*mp, VLF_CONSOLE)), - vlog_get_level_name(vlog_get_level(*mp, VLF_SYSLOG)), - vlog_get_level_name(vlog_get_level(*mp, VLF_FILE))); - svec_add_nocopy(&lines, line); + struct ds line; + + ds_init(&line); + ds_put_format(&line, "%-16s %4s %4s %4s", + vlog_get_module_name(*mp), + vlog_get_level_name(vlog_get_level(*mp, VLF_CONSOLE)), + vlog_get_level_name(vlog_get_level(*mp, VLF_SYSLOG)), + vlog_get_level_name(vlog_get_level(*mp, VLF_FILE))); + if (!(*mp)->honor_rate_limits) { + ds_put_cstr(&line, " (rate limiting disabled)"); + } + ds_put_char(&line, '\n'); + + svec_add_nocopy(&lines, ds_steal_cstr(&line)); } svec_sort(&lines); @@ -622,11 +706,11 @@ format_log_message(const struct vlog_module *module, enum vlog_level level, break; case 'd': p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp); - ds_put_strftime(s, tmp, false); + ds_put_strftime(s, tmp, time_wall(), false); break; case 'D': p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp); - ds_put_strftime(s, tmp, true); + ds_put_strftime(s, tmp, time_wall(), true); break; case 'm': /* Format user-supplied log message and trim trailing new-lines. */ @@ -653,6 +737,14 @@ format_log_message(const struct vlog_module *module, enum vlog_level level, case 'r': ds_put_format(s, "%lld", time_msec() - time_boot_msec()); break; + case 't': + ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main"); + break; + case 'T': + if (subprogram_name[0]) { + ds_put_format(s, "(%s)", subprogram_name); + } + break; default: ds_put_char(s, p[-1]); break; @@ -681,7 +773,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level, { bool log_to_console = module->levels[VLF_CONSOLE] >= level; bool log_to_syslog = module->levels[VLF_SYSLOG] >= level; - bool log_to_file = module->levels[VLF_FILE] >= level && log_file; + bool log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0; if (log_to_console || log_to_syslog || log_to_file) { int save_errno = errno; static unsigned int msg_num; @@ -717,8 +809,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level, format_log_message(module, level, VLF_FILE, msg_num, message, args, &s); ds_put_char(&s, '\n'); - fputs(ds_cstr(&s), log_file); - fflush(log_file); + vlog_write_file(&s); } ds_destroy(&s); @@ -737,11 +828,17 @@ vlog(const struct vlog_module *module, enum vlog_level level, va_end(args); } +/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure + * exit code. Always writes the message to stderr, even if the console + * facility is disabled. + * + * Choose this function instead of vlog_abort_valist() if the daemon monitoring + * facility shouldn't automatically restart the current daemon. */ void vlog_fatal_valist(const struct vlog_module *module_, const char *message, va_list args) { - struct vlog_module *module = (struct vlog_module *) module_; + struct vlog_module *module = CONST_CAST(struct vlog_module *, module_); /* Don't log this message to the console to avoid redundancy with the * message written by the later ovs_fatal_valist(). */ @@ -751,6 +848,12 @@ vlog_fatal_valist(const struct vlog_module *module_, ovs_fatal_valist(0, message, args); } +/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure + * exit code. Always writes the message to stderr, even if the console + * facility is disabled. + * + * Choose this function instead of vlog_abort() if the daemon monitoring + * facility shouldn't automatically restart the current daemon. */ void vlog_fatal(const struct vlog_module *module, const char *message, ...) { @@ -761,36 +864,61 @@ vlog_fatal(const struct vlog_module *module, const char *message, ...) va_end(args); } +/* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always + * writes the message to stderr, even if the console facility is disabled. + * + * Choose this function instead of vlog_fatal_valist() if the daemon monitoring + * facility should automatically restart the current daemon. */ +void +vlog_abort_valist(const struct vlog_module *module_, + const char *message, va_list args) +{ + struct vlog_module *module = (struct vlog_module *) module_; + + /* Don't log this message to the console to avoid redundancy with the + * message written by the later ovs_abort_valist(). */ + module->levels[VLF_CONSOLE] = VLL_OFF; + + vlog_valist(module, VLL_EMER, message, args); + ovs_abort_valist(0, message, args); +} + +/* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always + * writes the message to stderr, even if the console facility is disabled. + * + * Choose this function instead of vlog_fatal() if the daemon monitoring + * facility should automatically restart the current daemon. */ +void +vlog_abort(const struct vlog_module *module, const char *message, ...) +{ + va_list args; + + va_start(args, message); + vlog_abort_valist(module, message, args); + va_end(args); +} + bool vlog_should_drop(const struct vlog_module *module, enum vlog_level level, struct vlog_rate_limit *rl) { + if (!module->honor_rate_limits) { + return false; + } + if (!vlog_is_enabled(module, level)) { return true; } - if (rl->tokens < VLOG_MSG_TOKENS) { + if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) { time_t now = time_now(); - if (rl->last_fill > now) { - /* Last filled in the future? Time must have gone backward, or - * 'rl' has not been used before. */ - rl->tokens = rl->burst; - } else if (rl->last_fill < now) { - unsigned int add = sat_mul(rl->rate, now - rl->last_fill); - unsigned int tokens = sat_add(rl->tokens, add); - rl->tokens = MIN(tokens, rl->burst); - rl->last_fill = now; - } - if (rl->tokens < VLOG_MSG_TOKENS) { - if (!rl->n_dropped) { - rl->first_dropped = now; - } - rl->last_dropped = now; - rl->n_dropped++; - return true; + if (!rl->n_dropped) { + rl->first_dropped = now; } + rl->last_dropped = now; + rl->n_dropped++; + return true; } - rl->tokens -= VLOG_MSG_TOKENS; if (rl->n_dropped) { time_t now = time_now(); @@ -830,3 +958,59 @@ vlog_usage(void) " (default: %s/%s.log)\n", ovs_logdir(), program_name); } + +static bool vlog_async_inited = false; + +static worker_request_func vlog_async_write_request_cb; + +static void +vlog_write_file(struct ds *s) +{ + if (worker_is_running()) { + static bool in_worker_request = false; + if (!in_worker_request) { + in_worker_request = true; + + worker_request(s->string, s->length, + &log_fd, vlog_async_inited ? 0 : 1, + vlog_async_write_request_cb, NULL, NULL); + vlog_async_inited = true; + + in_worker_request = false; + return; + } else { + /* We've been entered recursively. This can happen if + * worker_request(), or a function that it calls, tries to log + * something. We can't call worker_request() recursively, so fall + * back to writing the log file directly. */ + COVERAGE_INC(vlog_recursive); + } + } + ignore(write(log_fd, s->string, s->length)); +} + +static void +vlog_update_async_log_fd(void) +{ + if (worker_is_running()) { + worker_request(NULL, 0, &log_fd, 1, vlog_async_write_request_cb, + NULL, NULL); + vlog_async_inited = true; + } +} + +static void +vlog_async_write_request_cb(struct ofpbuf *request, + const int *fd, size_t n_fds) +{ + if (n_fds > 0) { + if (log_fd >= 0) { + close(log_fd); + } + log_fd = *fd; + } + + if (request->size > 0) { + ignore(write(log_fd, request->data, request->size)); + } +}