* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vlog.h"
#include <assert.h>
+#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
+#include <sys/types.h>
#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
#include "dynamic-string.h"
+#include "sat-math.h"
+#include "timeval.h"
#include "util.h"
+#define THIS_MODULE VLM_vlog
+
/* Name for each logging level. */
static const char *level_names[VLL_N_LEVELS] = {
- [VLL_EMER] = "EMER",
- [VLL_ERR] = "ERR",
- [VLL_WARN] = "WARN",
- [VLL_DBG] = "DBG",
+#define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME,
+ VLOG_LEVELS
+#undef VLOG_LEVEL
};
-/* Name for each logging facility. */
-static const char *facility_names[VLF_N_FACILITIES] = {
- [VLF_CONSOLE] = "console",
- [VLF_SYSLOG] = "syslog",
+/* Syslog value for each logging level. */
+static int syslog_levels[VLL_N_LEVELS] = {
+#define VLOG_LEVEL(NAME, SYSLOG_LEVEL) SYSLOG_LEVEL,
+ VLOG_LEVELS
+#undef VLOG_LEVEL
};
/* Name for each logging module */
static const char *module_names[VLM_N_MODULES] = {
#define VLOG_MODULE(NAME) #NAME,
- VLOG_MODULES
-#undef VLOG_MODULES
+#include "vlog-modules.def"
+#undef VLOG_MODULE
+};
+
+/* Information about each facility. */
+struct facility {
+ const char *name; /* Name. */
+ char *pattern; /* Current pattern. */
+ bool default_pattern; /* Whether current pattern is the default. */
+};
+static struct facility facilities[VLF_N_FACILITIES] = {
+#define VLOG_FACILITY(NAME, PATTERN) {#NAME, PATTERN, true},
+ VLOG_FACILITIES
+#undef VLOG_FACILITY
};
+/* Current log levels. */
static int levels[VLM_N_MODULES][VLF_N_FACILITIES];
+/* For fast checking whether we're logging anything for a given module and
+ * level.*/
+enum vlog_level min_vlog_levels[VLM_N_MODULES];
+
+/* Time at which vlog was initialized, in milliseconds. */
+static long long int boot_time;
+
/* Searches the 'n_names' in 'names'. Returns the index of a match for
* 'target', or 'n_names' if no name matches. */
static size_t
vlog_get_facility_name(enum vlog_facility facility)
{
assert(facility < VLF_N_FACILITIES);
- return facility_names[facility];
+ return facilities[facility].name;
}
/* Returns the logging facility named 'name', or VLF_N_FACILITIES if 'name' is
enum vlog_facility
vlog_get_facility_val(const char *name)
{
- return search_name_array(name, facility_names, ARRAY_SIZE(facility_names));
+ size_t i;
+
+ for (i = 0; i < VLF_N_FACILITIES; i++) {
+ if (!strcasecmp(facilities[i].name, name)) {
+ break;
+ }
+ }
+ return i;
}
/* Returns the name for logging module 'module'. */
return levels[module][facility];
}
+static void
+update_min_level(enum vlog_module module)
+{
+ enum vlog_level min_level = VLL_EMER;
+ enum vlog_facility facility;
+
+ for (facility = 0; facility < VLF_N_FACILITIES; facility++) {
+ min_level = MAX(min_level, levels[module][facility]);
+ }
+ min_vlog_levels[module] = min_level;
+}
+
static void
set_facility_level(enum vlog_facility facility, enum vlog_module module,
enum vlog_level level)
if (module == VLM_ANY_MODULE) {
for (module = 0; module < VLM_N_MODULES; module++) {
levels[module][facility] = level;
+ update_min_level(module);
}
} else {
levels[module][facility] = level;
+ update_min_level(module);
}
}
}
}
+static void
+do_set_pattern(enum vlog_facility facility, const char *pattern)
+{
+ struct facility *f = &facilities[facility];
+ if (!f->default_pattern) {
+ free(f->pattern);
+ } else {
+ f->default_pattern = false;
+ }
+ f->pattern = xstrdup(pattern);
+}
+
+/* Sets the pattern for the given 'facility' to 'pattern'. */
+void
+vlog_set_pattern(enum vlog_facility facility, const char *pattern)
+{
+ assert(facility < VLF_N_FACILITIES || facility == VLF_ANY_FACILITY);
+ if (facility == VLF_ANY_FACILITY) {
+ for (facility = 0; facility < VLF_N_FACILITIES; facility++) {
+ do_set_pattern(facility, pattern);
+ }
+ } else {
+ do_set_pattern(facility, pattern);
+ }
+}
+
/* Set debugging levels:
*
- * mod:facility:level mod2:facility:level ...
+ * mod[:facility[:level]] mod2[:facility[:level]] ...
*
* Return null if successful, otherwise an error message that the caller must
* free().
{
char *save_ptr;
char *s = xstrdup(s_);
- char *module, *level, *facility;
+ char *module, *facility;
for (module = strtok_r(s, ": \t", &save_ptr); module != NULL;
module = strtok_r(NULL, ": \t", &save_ptr)) {
enum vlog_module e_module;
- enum vlog_level e_level;
enum vlog_facility e_facility;
facility = strtok_r(NULL, ":", &save_ptr);
- level = strtok_r(NULL, ":", &save_ptr);
- if (level == NULL || facility == NULL) {
- free(s);
- return xstrdup("syntax error in level string");
- }
- if (!strcmp(module, "ANY")) {
- e_module = VLM_ANY_MODULE;
+ if (!facility || !strcmp(facility, "ANY")) {
+ e_facility = VLF_ANY_FACILITY;
} else {
- e_module = vlog_get_module_val(module);
- if (e_module >= VLM_N_MODULES) {
- char *msg = xasprintf("unknown module \"%s\"", module);
+ e_facility = vlog_get_facility_val(facility);
+ if (e_facility >= VLF_N_FACILITIES) {
+ char *msg = xasprintf("unknown facility \"%s\"", facility);
free(s);
return msg;
}
}
- if (!strcmp(facility, "ANY")) {
- e_facility = VLF_ANY_FACILITY;
+ if (!strcmp(module, "PATTERN")) {
+ vlog_set_pattern(e_facility, save_ptr);
+ break;
} else {
- e_facility = vlog_get_facility_val(facility);
- if (e_facility >= VLF_N_FACILITIES) {
- char *msg = xasprintf("unknown facility \"%s\"", facility);
+ char *level;
+ enum vlog_level e_level;
+
+ if (!strcmp(module, "ANY")) {
+ e_module = VLM_ANY_MODULE;
+ } else {
+ e_module = vlog_get_module_val(module);
+ if (e_module >= VLM_N_MODULES) {
+ char *msg = xasprintf("unknown module \"%s\"", module);
+ free(s);
+ return msg;
+ }
+ }
+
+ level = strtok_r(NULL, ":", &save_ptr);
+ e_level = level ? vlog_get_level_val(level) : VLL_DBG;
+ if (e_level >= VLL_N_LEVELS) {
+ char *msg = xasprintf("unknown level \"%s\"", level);
free(s);
return msg;
}
- }
- e_level = vlog_get_level_val(level);
- if (e_level >= VLL_N_LEVELS) {
- char *msg = xasprintf("unknown level \"%s\"", level);
- free(s);
- return msg;
+ vlog_set_levels(e_module, e_facility, e_level);
}
-
- vlog_set_levels(e_module, e_facility, e_level);
}
free(s);
return NULL;
if (arg) {
char *msg = vlog_set_levels_from_string(arg);
if (msg) {
- fatal(0, "processing \"%s\": %s", arg, msg);
+ ofp_fatal(0, "processing \"%s\": %s", arg, msg);
}
} else {
- vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_DBG);
+ vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_DBG);
}
}
void
vlog_init(void)
{
+ time_t now;
+
openlog(program_name, LOG_NDELAY, LOG_DAEMON);
- vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_WARN);
+ vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_WARN);
+
+ boot_time = time_msec();
+ now = time_now();
+ 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);
+ VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
+ }
}
/* Closes the logging subsystem. */
bool
vlog_is_enabled(enum vlog_module module, enum vlog_level level)
{
- return (levels[module][VLF_CONSOLE] >= level
- || levels[module][VLF_SYSLOG] >= level);
+ return min_vlog_levels[module] >= level;
+}
+
+static const char *
+fetch_braces(const char *p, const char *def, char *out, size_t out_size)
+{
+ if (*p == '{') {
+ size_t n = strcspn(p + 1, "}");
+ size_t n_copy = MIN(n, out_size - 1);
+ memcpy(out, p + 1, n_copy);
+ out[n_copy] = '\0';
+ p += n + 2;
+ } else {
+ strlcpy(out, def, out_size);
+ }
+ return p;
+}
+
+static void
+format_log_message(enum vlog_module module, enum vlog_level level,
+ enum vlog_facility facility, unsigned int msg_num,
+ const char *message, va_list args_, struct ds *s)
+{
+ char tmp[128];
+ va_list args;
+ const char *p;
+
+ ds_clear(s);
+ for (p = facilities[facility].pattern; *p != '\0'; ) {
+ enum { LEFT, RIGHT } justify = RIGHT;
+ int pad = '0';
+ size_t length, field, used;
+
+ if (*p != '%') {
+ ds_put_char(s, *p++);
+ continue;
+ }
+
+ p++;
+ if (*p == '-') {
+ justify = LEFT;
+ p++;
+ }
+ if (*p == '0') {
+ pad = '0';
+ p++;
+ }
+ field = 0;
+ while (isdigit(*p)) {
+ field = (field * 10) + (*p - '0');
+ p++;
+ }
+
+ length = s->length;
+ switch (*p++) {
+ case 'A':
+ ds_put_cstr(s, program_name);
+ break;
+ case 'c':
+ p = fetch_braces(p, "", tmp, sizeof tmp);
+ ds_put_cstr(s, vlog_get_module_name(module));
+ break;
+ case 'd':
+ p = fetch_braces(p, "%Y-%m-%d %H:%M:%S", tmp, sizeof tmp);
+ ds_put_strftime(s, tmp, NULL);
+ break;
+ case 'm':
+ va_copy(args, args_);
+ ds_put_format_valist(s, message, args);
+ va_end(args);
+ break;
+ case 'N':
+ ds_put_format(s, "%u", msg_num);
+ break;
+ case 'n':
+ ds_put_char(s, '\n');
+ break;
+ case 'p':
+ ds_put_cstr(s, vlog_get_level_name(level));
+ break;
+ case 'P':
+ ds_put_format(s, "%ld", (long int) getpid());
+ break;
+ case 'r':
+ ds_put_format(s, "%lld", time_msec() - boot_time);
+ break;
+ default:
+ ds_put_char(s, p[-1]);
+ break;
+ }
+ used = s->length - length;
+ if (used < field) {
+ size_t n_pad = field - used;
+ if (justify == RIGHT) {
+ ds_put_uninit(s, n_pad);
+ memmove(&s->string[length + n_pad], &s->string[length], used);
+ memset(&s->string[length], pad, n_pad);
+ } else {
+ ds_put_char_multiple(s, pad, n_pad);
+ }
+ }
+ }
}
/* Writes 'message' to the log at the given 'level' and as coming from the
*
* Guaranteed to preserve errno. */
void
-vlog(enum vlog_module module, enum vlog_level level, const char *message, ...)
+vlog_valist(enum vlog_module module, enum vlog_level level,
+ const char *message, va_list args)
{
bool log_console = levels[module][VLF_CONSOLE] >= level;
bool log_syslog = levels[module][VLF_SYSLOG] >= level;
if (log_console || log_syslog) {
int save_errno = errno;
- static int msg_num;
- const char *module_name = vlog_get_module_name(module);
- const char *level_name = vlog_get_level_name(level);
- va_list args;
- char s[1024];
- size_t len;
-
- len = sprintf(s, "%05d|%s|%s:", ++msg_num, module_name, level_name);
- va_start(args, message);
- len += vsnprintf(s + len, sizeof s - len, message, args);
- va_end(args);
- if (len >= sizeof s) {
- len = sizeof s;
- }
- if (s[len - 1] == '\n') {
- s[len - 1] = '\0';
- }
+ static unsigned int msg_num;
+ struct ds s;
+
+ ds_init(&s);
+ ds_reserve(&s, 1024);
+ msg_num++;
if (log_console) {
- fprintf(stderr, "%s\n", s);
+ format_log_message(module, level, VLF_CONSOLE, msg_num,
+ message, args, &s);
+ ds_put_char(&s, '\n');
+ fputs(ds_cstr(&s), stderr);
}
if (log_syslog) {
- static const int syslog_levels[VLL_N_LEVELS] = {
- [VLL_EMER] = LOG_EMERG,
- [VLL_ERR] = LOG_ERR,
- [VLL_WARN] = LOG_WARNING,
- [VLL_DBG] = LOG_DEBUG,
- };
-
- syslog(syslog_levels[level], "%s", s);
+ int syslog_level = syslog_levels[level];
+ char *save_ptr = NULL;
+ char *line;
+
+ format_log_message(module, level, VLF_SYSLOG, msg_num,
+ 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);
+ }
}
+
+ ds_destroy(&s);
errno = save_errno;
}
}
+
+void
+vlog(enum vlog_module module, enum vlog_level level, const char *message, ...)
+{
+ va_list args;
+
+ va_start(args, message);
+ vlog_valist(module, level, message, args);
+ va_end(args);
+}
+
+void
+vlog_rate_limit(enum vlog_module module, enum vlog_level level,
+ struct vlog_rate_limit *rl, const char *message, ...)
+{
+ va_list args;
+
+ if (!vlog_is_enabled(module, level)) {
+ return;
+ }
+
+ if (rl->tokens < 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->n_dropped++;
+ return;
+ }
+ }
+ rl->tokens -= VLOG_MSG_TOKENS;
+
+ va_start(args, message);
+ vlog_valist(module, level, message, args);
+ va_end(args);
+
+ if (rl->n_dropped) {
+ vlog(module, level,
+ "Dropped %u messages in last %u seconds due to excessive rate",
+ rl->n_dropped, (unsigned int) (time_now() - rl->first_dropped));
+ rl->n_dropped = 0;
+ }
+}