2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-02 07:15:17 +00:00

vlog: Make thread-safe.

Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Ben Pfaff
2013-07-17 11:42:10 -07:00
parent a5fb0e29a9
commit 81d6495fd9
2 changed files with 120 additions and 79 deletions

View File

@@ -33,6 +33,7 @@
#include "dirs.h"
#include "dynamic-string.h"
#include "ofpbuf.h"
#include "ovs-thread.h"
#include "sat-math.h"
#include "svec.h"
#include "timeval.h"
@@ -84,7 +85,7 @@ struct vlog_module *vlog_modules[] = {
/* Information about each facility. */
struct facility {
const char *name; /* Name. */
char *pattern; /* Current pattern. */
char *pattern; /* Current pattern (see 'pattern_rwlock'). */
bool default_pattern; /* Whether current pattern is the default. */
};
static struct facility facilities[VLF_N_FACILITIES] = {
@@ -93,18 +94,27 @@ static struct facility facilities[VLF_N_FACILITIES] = {
#undef VLOG_FACILITY
};
/* VLF_FILE configuration. */
/* Protects the 'pattern' in all "struct facility"s, so that a race between
* changing and reading the pattern does not cause an access to freed
* memory. */
static pthread_rwlock_t pattern_rwlock = PTHREAD_RWLOCK_INITIALIZER;
/* Sequence number for the message currently being composed. */
DEFINE_PER_THREAD_DATA(unsigned int, msg_num, 0);
/* VLF_FILE configuration.
*
* All of the following is protected by 'log_file_mutex', which nests inside
* pattern_rwlock. */
static pthread_mutex_t log_file_mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER;
static char *log_file_name;
static int log_fd = -1;
static struct async_append *log_writer;
/* vlog initialized? */
static bool vlog_inited;
static void format_log_message(const struct vlog_module *, enum vlog_level,
enum vlog_facility, unsigned int msg_num,
enum vlog_facility,
const char *message, va_list, struct ds *)
PRINTF_FORMAT(5, 0);
PRINTF_FORMAT(4, 0);
/* Searches the 'n_names' in 'names'. Returns the index of a match for
* 'target', or 'n_names' if no name matches. */
@@ -191,7 +201,7 @@ vlog_get_level(const struct vlog_module *module, enum vlog_facility facility)
return module->levels[facility];
}
static void
static void OVS_MUST_HOLD(log_file_mutex)
update_min_level(struct vlog_module *module)
{
enum vlog_facility facility;
@@ -214,6 +224,7 @@ set_facility_level(enum vlog_facility facility, struct vlog_module *module,
assert(facility >= 0 && facility < VLF_N_FACILITIES);
assert(level < VLL_N_LEVELS);
xpthread_mutex_lock(&log_file_mutex);
if (!module) {
struct vlog_module **mp;
@@ -225,6 +236,7 @@ set_facility_level(enum vlog_facility facility, struct vlog_module *module,
module->levels[facility] = level;
update_min_level(module);
}
xpthread_mutex_unlock(&log_file_mutex);
}
/* Sets the logging level for the given 'module' and 'facility' to 'level'. A
@@ -248,12 +260,15 @@ static void
do_set_pattern(enum vlog_facility facility, const char *pattern)
{
struct facility *f = &facilities[facility];
xpthread_rwlock_wrlock(&pattern_rwlock);
if (!f->default_pattern) {
free(f->pattern);
} else {
f->default_pattern = false;
}
f->pattern = xstrdup(pattern);
xpthread_rwlock_unlock(&pattern_rwlock);
}
/* Sets the pattern for the given 'facility' to 'pattern'. */
@@ -276,49 +291,67 @@ vlog_set_pattern(enum vlog_facility facility, const char *pattern)
int
vlog_set_log_file(const char *file_name)
{
char *old_log_file_name;
char *new_log_file_name;
struct vlog_module **mp;
int error;
struct stat old_stat;
struct stat new_stat;
int new_log_fd;
bool same_file;
/* Close old log file. */
if (log_fd >= 0) {
VLOG_INFO("closing log file");
async_append_destroy(log_writer);
log_writer = NULL;
close(log_fd);
log_fd = -1;
/* Open new log file. */
new_log_file_name = (file_name
? xstrdup(file_name)
: xasprintf("%s/%s.log", ovs_logdir(), program_name));
new_log_fd = open(new_log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
if (new_log_fd < 0) {
VLOG_WARN("failed to open %s for logging: %s",
new_log_file_name, ovs_strerror(errno));
free(new_log_file_name);
return errno;
}
/* Update log file name and free old name. The ordering is important
* because 'file_name' might be 'log_file_name' or some suffix of it. */
old_log_file_name = log_file_name;
log_file_name = (file_name
? xstrdup(file_name)
: xasprintf("%s/%s.log", ovs_logdir(), program_name));
free(old_log_file_name);
file_name = NULL; /* Might have been freed. */
/* If the new log file is the same one we already have open, bail out. */
xpthread_mutex_lock(&log_file_mutex);
same_file = (log_fd >= 0
&& new_log_fd >= 0
&& !fstat(log_fd, &old_stat)
&& !fstat(new_log_fd, &new_stat)
&& old_stat.st_dev == new_stat.st_dev
&& old_stat.st_ino == new_stat.st_ino);
xpthread_mutex_unlock(&log_file_mutex);
if (same_file) {
close(new_log_fd);
free(new_log_file_name);
return 0;
}
/* Log closing old log file (we can't log while holding log_file_mutex). */
if (log_fd >= 0) {
VLOG_INFO("closing log file");
}
/* Close old log file, if any, and install new one. */
xpthread_mutex_lock(&log_file_mutex);
if (log_fd >= 0) {
free(log_file_name);
close(log_fd);
async_append_destroy(log_writer);
}
log_file_name = xstrdup(new_log_file_name);
log_fd = new_log_fd;
log_writer = async_append_create(new_log_fd);
/* Open new log file and update min_levels[] to reflect whether we actually
* have a log_file. */
log_fd = open(log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
update_min_level(*mp);
}
xpthread_mutex_unlock(&log_file_mutex);
/* Log success or failure. */
if (log_fd < 0) {
VLOG_WARN("failed to open %s for logging: %s",
log_file_name, ovs_strerror(errno));
error = errno;
} else {
log_writer = async_append_create(log_fd);
VLOG_INFO("opened log file %s", log_file_name);
error = 0;
}
/* Log opening new log file (we can't log while holding log_file_mutex). */
VLOG_INFO("opened log file %s", new_log_file_name);
free(new_log_file_name);
return error;
return 0;
}
/* Closes and then attempts to re-open the current log file. (This is useful
@@ -327,25 +360,19 @@ vlog_set_log_file(const char *file_name)
int
vlog_reopen_log_file(void)
{
struct stat old, new;
char *fn;
/* Skip re-opening if there's nothing to reopen. */
if (!log_file_name) {
xpthread_mutex_lock(&log_file_mutex);
fn = log_file_name ? xstrdup(log_file_name) : NULL;
xpthread_mutex_unlock(&log_file_mutex);
if (fn) {
int error = vlog_set_log_file(fn);
free(fn);
return error;
} else {
return 0;
}
/* 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_fd >= 0
&& !fstat(log_fd, &old)
&& !stat(log_file_name, &new)
&& old.st_dev == new.st_dev
&& old.st_ino == new.st_ino) {
return 0;
}
return vlog_set_log_file(log_file_name);
}
/* Set debugging levels. Returns null if successful, otherwise an error
@@ -538,19 +565,12 @@ vlog_disable_rate_limit(struct unixctl_conn *conn, int argc,
set_rate_limits(conn, argc, argv, false);
}
/* Initializes the logging subsystem and registers its unixctl server
* commands. */
void
vlog_init(void)
static void
vlog_init__(void)
{
static char *program_name_copy;
time_t now;
if (vlog_inited) {
return;
}
vlog_inited = true;
/* openlog() is allowed to keep the pointer passed in, without making a
* copy. The daemonize code sometimes frees and replaces 'program_name',
* so make a private copy just for openlog(). (We keep a pointer to the
@@ -578,6 +598,15 @@ vlog_init(void)
vlog_unixctl_reopen, NULL);
}
/* Initializes the logging subsystem and registers its unixctl server
* commands. */
void
vlog_init(void)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once, vlog_init__);
}
/* Print the current logging level for each module. */
char *
vlog_get_levels(void)
@@ -643,7 +672,7 @@ fetch_braces(const char *p, const char *def, char *out, size_t out_size)
static void
format_log_message(const struct vlog_module *module, enum vlog_level level,
enum vlog_facility facility, unsigned int msg_num,
enum vlog_facility facility,
const char *message, va_list args_, struct ds *s)
{
char tmp[128];
@@ -705,7 +734,7 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
}
break;
case 'N':
ds_put_format(s, "%u", msg_num);
ds_put_format(s, "%u", *msg_num_get_unsafe());
break;
case 'n':
ds_put_char(s, '\n');
@@ -760,18 +789,17 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
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;
struct ds s;
vlog_init();
ds_init(&s);
ds_reserve(&s, 1024);
msg_num++;
++*msg_num_get();
xpthread_rwlock_rdlock(&pattern_rwlock);
if (log_to_console) {
format_log_message(module, level, VLF_CONSOLE, msg_num,
message, args, &s);
format_log_message(module, level, VLF_CONSOLE, message, args, &s);
ds_put_char(&s, '\n');
fputs(ds_cstr(&s), stderr);
}
@@ -781,8 +809,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
char *save_ptr = NULL;
char *line;
format_log_message(module, level, VLF_SYSLOG, msg_num,
message, args, &s);
format_log_message(module, level, VLF_SYSLOG, message, args, &s);
for (line = strtok_r(s.string, "\n", &save_ptr); line;
line = strtok_r(NULL, "\n", &save_ptr)) {
syslog(syslog_level, "%s", line);
@@ -790,14 +817,19 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
}
if (log_to_file) {
format_log_message(module, level, VLF_FILE, msg_num,
message, args, &s);
format_log_message(module, level, VLF_FILE, message, args, &s);
ds_put_char(&s, '\n');
async_append_write(log_writer, s.string, s.length);
if (level == VLL_EMER) {
async_append_flush(log_writer);
xpthread_mutex_lock(&log_file_mutex);
if (log_fd >= 0) {
async_append_write(log_writer, s.string, s.length);
if (level == VLL_EMER) {
async_append_flush(log_writer);
}
}
xpthread_mutex_unlock(&log_file_mutex);
}
xpthread_rwlock_unlock(&pattern_rwlock);
ds_destroy(&s);
errno = save_errno;

View File

@@ -17,6 +17,15 @@
#ifndef VLOG_H
#define VLOG_H 1
/* Logging.
*
*
* Thread-safety
* =============
*
* Fully thread safe.
*/
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>