/*
- * 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.
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#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] = {
#define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME,
/* VLF_FILE configuration. */
static char *log_file_name;
-static FILE *log_file;
+static int log_fd = -1;
/* vlog initialized? */
static bool vlog_inited;
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. */
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;
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
/* 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;
/* 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) {
}
}
+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
"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);
}
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);
{
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;
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);
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(). */
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;
}
" (default: %s/%s.log)\n",
ovs_logdir(), program_name);
}
+\f
+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));
+ }
+}