Skip to content

Commit

Permalink
vlog: Ability to override the default log facility.
Browse files Browse the repository at this point in the history
When Open vSwitch is run in hundreds of hypervisors, it is
useful to collect log messages through log collectors. To
collect log messages like this, it is useful to log them
in a particular RFC5424 facility in the local system. The
log collectors can then be used to collect logs anytime
desired.

This commit provides a sysadmin the ability to specify the
facility through which the log messages are logged.

Signed-off-by: Gurucharan Shetty <[email protected]>
Acked-by: Ben Pfaff <[email protected]>
  • Loading branch information
shettyg committed Jan 28, 2015
1 parent 1667bb3 commit d69d61c
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 6 deletions.
13 changes: 13 additions & 0 deletions include/windows/syslog.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@
#define LOG_NDELAY 8 /* don't delay open */
#define LOG_DAEMON 24 /* system daemons */

#define LOG_KERN (0<<3) /* kernel messages */
#define LOG_USER (1<<3) /* user-level messages */
#define LOG_MAIL (2<<3) /* mail system */
#define LOG_DAEMON (3<<3) /* system daemons */
#define LOG_AUTH (4<<3) /* security/authorization messages */
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
#define LOG_LPR (6<<3) /* line printer subsystem */
#define LOG_NEWS (7<<3) /* network news subsystem */
#define LOG_UUCP (8<<3) /* UUCP subsystem */
#define LOG_CRON (9<<3) /* clock daemon */
#define LOG_AUTHPRIV (10<<3) /* security/authorization messages */
#define LOG_FTP (11<<3) /* FTP daemon */

#define LOG_LOCAL0 (16<<3) /* reserved for local use */
#define LOG_LOCAL1 (17<<3) /* reserved for local use */
#define LOG_LOCAL2 (18<<3) /* reserved for local use */
Expand Down
75 changes: 72 additions & 3 deletions lib/vlog.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,41 @@ static bool log_async OVS_GUARDED_BY(log_file_mutex);
/* Syslog export configuration. */
static int syslog_fd OVS_GUARDED_BY(pattern_rwlock) = -1;

/* Log facility configuration. */
static atomic_int log_facility = ATOMIC_VAR_INIT(0);

/* Facility name and its value. */
struct vlog_facility {
char *name; /* Name. */
unsigned int value; /* Facility associated with 'name'. */
};
static struct vlog_facility vlog_facilities[] = {
{"kern", LOG_KERN},
{"user", LOG_USER},
{"mail", LOG_MAIL},
{"daemon", LOG_DAEMON},
{"auth", LOG_AUTH},
{"syslog", LOG_SYSLOG},
{"lpr", LOG_LPR},
{"news", LOG_NEWS},
{"uucp", LOG_UUCP},
{"clock", LOG_CRON},
{"ftp", LOG_FTP},
{"ntp", 12<<3},
{"audit", 13<<3},
{"alert", 14<<3},
{"clock2", 15<<3},
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
{"local3", LOG_LOCAL3},
{"local4", LOG_LOCAL4},
{"local5", LOG_LOCAL5},
{"local6", LOG_LOCAL6},
{"local7", LOG_LOCAL7}
};
static bool vlog_facility_exists(const char* facility, int *value);

static void format_log_message(const struct vlog_module *, enum vlog_level,
const char *pattern,
const char *message, va_list, struct ds *)
Expand Down Expand Up @@ -419,6 +454,14 @@ vlog_set_levels_from_string(const char *s_)
goto exit;
}
vlog_set_pattern(destination, save_ptr);
} else if (word && !strcasecmp(word, "FACILITY")) {
int value;

if (!vlog_facility_exists(save_ptr, &value)) {
msg = xstrdup("invalid facility");
goto exit;
}
atomic_store_explicit(&log_facility, value, memory_order_relaxed);
} else {
struct vlog_module *module = NULL;
enum vlog_level level = VLL_N_LEVELS;
Expand Down Expand Up @@ -507,6 +550,22 @@ vlog_set_syslog_target(const char *target)
ovs_rwlock_unlock(&pattern_rwlock);
}

/* Returns 'false' if 'facility' is not a valid string. If 'facility'
* is a valid string, sets 'value' with the integer value of 'facility'
* and returns 'true'. */
static bool
vlog_facility_exists(const char* facility, int *value)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(vlog_facilities); i++) {
if (!strcasecmp(vlog_facilities[i].name, facility)) {
*value = vlog_facilities[i].value;
return true;
}
}
return false;
}

static void
vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[],
void *aux OVS_UNUSED)
Expand Down Expand Up @@ -614,6 +673,7 @@ vlog_init(void)
if (ovsthread_once_start(&once)) {
static char *program_name_copy;
long long int now;
int facility;

/* Do initialization work that needs to be done before any logging
* occurs. We want to keep this really minimal because any attempt to
Expand All @@ -625,7 +685,9 @@ vlog_init(void)
* a pointer to the private copy to suppress memory leak warnings in
* case openlog() does make its own copy.) */
program_name_copy = program_name ? xstrdup(program_name) : NULL;
openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON);
atomic_read_explicit(&log_facility, &facility, memory_order_relaxed);
openlog(program_name_copy, LOG_NDELAY,
facility ? facility : LOG_DAEMON);
ovsthread_once_done(&once);

/* Now do anything that we want to happen only once but doesn't have to
Expand Down Expand Up @@ -739,6 +801,7 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
char tmp[128];
va_list args;
const char *p;
int facility;

ds_clear(s);
for (p = pattern; *p != '\0'; ) {
Expand Down Expand Up @@ -773,7 +836,10 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
ds_put_cstr(s, program_name);
break;
case 'B':
ds_put_format(s, "%d", LOG_LOCAL0 + syslog_levels[level]);
atomic_read_explicit(&log_facility, &facility,
memory_order_relaxed);
facility = facility ? facility : LOG_LOCAL0;
ds_put_format(s, "%d", facility + syslog_levels[level]);
break;
case 'c':
p = fetch_braces(p, "", tmp, sizeof tmp);
Expand Down Expand Up @@ -897,12 +963,15 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
int syslog_level = syslog_levels[level];
char *save_ptr = NULL;
char *line;
int facility;

format_log_message(module, level, destinations[VLF_SYSLOG].pattern,
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);
atomic_read_explicit(&log_facility, &facility,
memory_order_relaxed);
syslog(syslog_level|facility, "%s", line);
}

if (syslog_fd >= 0) {
Expand Down
11 changes: 11 additions & 0 deletions lib/vlog.man
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ Sets the maximum logging verbosity level, equivalent to
Sets the log pattern for \fIdestination\fR to \fIpattern\fR. Refer to
\fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
.
.IP "\fB\-vFACILITY:\fIfacility\fR"
.IQ "\fB\-\-verbose=FACILITY:\fIfacility\fR"
Sets the RFC5424 facility of the log message. \fIfacility\fR can be one of
\fBkern\fR, \fBuser\fR, \fBmail\fR, \fBdaemon\fR, \fBauth\fR, \fBsyslog\fR,
\fBlpr\fR, \fBnews\fR, \fBuucp\fR, \fBclock\fR, \fBftp\fR, \fBntp\fR,
\fBaudit\fR, \fBalert\fR, \fBclock2\fR, \fBlocal0\fR, \fBlocal1\fR,
\fBlocal2\fR, \fBlocal3\fR, \fBlocal4\fR, \fBlocal5\fR, \fBlocal6\fR or
\fBlocal7\fR. If this option is not specified, \fBdaemon\fR is used as
the default for the local system syslog and \fBlocal0\fR is used while sending
a message to the target provided via the \fB\-\-syslog\-target\fR option.
.
.TP
\fB\-\-log\-file\fR[\fB=\fIfile\fR]
Enables logging to a file. If \fIfile\fR is specified, then it is
Expand Down
35 changes: 32 additions & 3 deletions python/ovs/vlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
"emer": logging.CRITICAL,
"off": logging.CRITICAL
}
FACILITIES = ['auth', 'authpriv', 'cron', 'daemon', 'ftp', 'kern', 'lpr',
'mail', 'news', 'syslog', 'user', 'uucp', 'local0', 'local1',
'local2', 'local3', 'local4', 'local5', 'local6', 'local7']
syslog_facility = "daemon"
syslog_handler = ''


def get_level(level_str):
Expand Down Expand Up @@ -224,9 +229,7 @@ def init(log_file=None):
if f == "console":
logger.addHandler(logging.StreamHandler(sys.stderr))
elif f == "syslog":
logger.addHandler(logging.handlers.SysLogHandler(
address="/dev/log",
facility=logging.handlers.SysLogHandler.LOG_DAEMON))
Vlog.add_syslog_handler()
elif f == "file" and Vlog.__log_file:
Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
logger.addHandler(Vlog.__file_handler)
Expand Down Expand Up @@ -280,6 +283,26 @@ def set_pattern(destination, pattern):
destination = destination.lower()
Vlog.__log_patterns[destination] = pattern

@staticmethod
def add_syslog_handler(facility=None):
global syslog_facility, syslog_handler

# If handler is already added and there is no change in 'facility',
# there is nothing to do.
if (not facility or facility == syslog_facility) and syslog_handler:
return

if facility:
syslog_facility = facility

logger = logging.getLogger('syslog')
if syslog_handler:
logger.removeHandler(syslog_handler)
syslog_handler = logging.handlers.SysLogHandler(address="/dev/log",
facility=syslog_facility)
logger.addHandler(syslog_handler)
return

@staticmethod
def set_levels_from_string(s):
module = None
Expand All @@ -298,6 +321,12 @@ def set_levels_from_string(s):
return "Destination %s does not exist" % words[1]
except IndexError:
return "Please supply a valid pattern and destination"
elif words[0] == "FACILITY":
if words[1] in FACILITIES:
Vlog.add_syslog_handler(words[1])
return
else:
return "Facility %s is invalid" % words[1]

for word in [w.lower() for w in words]:
if word == "any":
Expand Down
63 changes: 63 additions & 0 deletions tests/vlog.at
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,66 @@ AT_CHECK([APPCTL -t test-unixctl.py vlog/set pattern:file:'I<3OVS|%m'])
AT_CHECK([APPCTL -t test-unixctl.py log patterntest])
AT_CHECK([grep -q 'I<3OVS' log])
AT_CLEANUP

AT_SETUP([vlog - RFC5424 facility])
OVS_RUNDIR=`pwd`; export OVS_RUNDIR
OVS_LOGDIR=`pwd`; export OVS_LOGDIR
OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
ON_EXIT([kill `cat ovsdb-server.pid`])

dnl Create database.
touch .conf.db.~lock~
AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])

AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \
--remote=punix:$OVS_RUNDIR/db.sock -vPATTERN:file:"<%B>1 %A %m" \
--log-file], [0], [], [stderr])
AT_CHECK([ovs-appctl -t ovsdb-server exit])

# A default facility of LOG_LOCAL0 while writing to file.
AT_CHECK([cat ovsdb-server.log | head -1 | awk '{print $1}'], [0], [<133>1
])
rm ovsdb-server.log

AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \
--remote=punix:$OVS_RUNDIR/db.sock -vPATTERN:file:"<%B>1 %A %m" \
-vFACILITY:daemon --log-file], [0], [], [stderr])

AT_CHECK([cat ovsdb-server.log | head -1 | awk '{print $1}'], [0], [<29>1
])

AT_CHECK([ovs-appctl -t ovsdb-server vlog/set FACILITY:invalid], [2], [],
[invalid facility
ovs-appctl: ovsdb-server: server returned an error
])

AT_CHECK([ovs-appctl -t ovsdb-server vlog/set FACILITY:local7])
AT_CHECK([ovs-appctl -t ovsdb-server vlog/set ANY:file:DBG])
AT_CHECK([ovs-appctl -t ovsdb-server exit])

AT_CHECK([cat ovsdb-server.log | tail -1 | awk '{print $1}'], [0], [<191>1
])
AT_CLEANUP

AT_SETUP([vlog - RFC5424 facility - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
OVS_RUNDIR=`pwd`; export OVS_RUNDIR
OVS_LOGDIR=`pwd`; export OVS_LOGDIR
OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
ON_EXIT([kill `cat test-unixctl.py.pid`])

AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile \
-vFACILITY:invalid --detach], [1], [], [test-unixctl.py: processing "FACILITY:invalid": Facility invalid is invalid
])

AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile \
-vFACILITY:daemon --detach])

AT_CHECK([ovs-appctl -t test-unixctl.py vlog/set FACILITY:invalid], [0],
[Facility invalid is invalid
])

AT_CHECK([ovs-appctl -t test-unixctl.py vlog/set FACILITY:local0])
AT_CLEANUP
8 changes: 8 additions & 0 deletions utilities/ovs-appctl.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,14 @@ The default pattern for console and file output is \fB%D{%Y-%m-%dT
Daemons written in Python (e.g. \fBovs\-xapi\-sync\fR,
\fBovs\-monitor\-ipsec) do not allow control over the log pattern.
.
.IP "\fBvlog/set\fR FACILITY:\fIfacility\fR"
Sets the RFC5424 facility of the log message. \fIfacility\fR can be one of
\fBkern\fR, \fBuser\fR, \fBmail\fR, \fBdaemon\fR, \fBauth\fR, \fBsyslog\fR,
\fBlpr\fR, \fBnews\fR, \fBuucp\fR, \fBclock\fR, \fBftp\fR, \fBntp\fR,
\fBaudit\fR, \fBalert\fR, \fBclock2\fR, \fBlocal0\fR, \fBlocal1\fR,
\fBlocal2\fR, \fBlocal3\fR, \fBlocal4\fR, \fBlocal5\fR, \fBlocal6\fR or
\fBlocal7\fR.
.
.IP "\fBvlog/reopen\fR"
Causes the daemon to close and reopen its log file. (This
is useful after rotating log files, to cause a new log file to be
Expand Down

0 comments on commit d69d61c

Please sign in to comment.