From: Timo Sirainen Date: Mon, 6 Feb 2012 17:30:12 +0000 (+0200) Subject: log: Keep track of last 1000 errors/warnings. "doveadm log errors" shows them. X-Git-Tag: 2.1.rc6~75 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=acba68a69cdd6f3f00faa18cccef356d95048e46;p=thirdparty%2Fdovecot%2Fcore.git log: Keep track of last 1000 errors/warnings. "doveadm log errors" shows them. --- diff --git a/src/doveadm/doveadm-log.c b/src/doveadm/doveadm-log.c index aef60da0c4..9eedb759c5 100644 --- a/src/doveadm/doveadm-log.c +++ b/src/doveadm/doveadm-log.c @@ -2,9 +2,11 @@ #include "lib.h" #include "ioloop.h" +#include "istream.h" #include "hash.h" #include "str.h" -#include "istream.h" +#include "strescape.h" +#include "time-util.h" #include "master-service-private.h" #include "master-service-settings.h" #include "doveadm.h" @@ -18,6 +20,8 @@ #define LAST_LOG_TYPE LOG_TYPE_PANIC #define TEST_LOG_MSG_PREFIX "This is Dovecot's " +#define LOG_ERRORS_FNAME "log-errors" +#define LOG_TIMESTAMP_FORMAT "%b %d %H:%M:%S" static void cmd_log_test(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) { @@ -274,10 +278,59 @@ static void cmd_log_find(int argc, char *argv[]) } } +static void cmd_log_error_write(const char *const *args) +{ + /* */ + const char *type_prefix = "?"; + unsigned int type; + time_t t; + + /* find type's prefix */ + for (type = 0; type < LOG_TYPE_COUNT; type++) { + if (strcmp(args[0], failure_log_type_names[type]) == 0) { + type_prefix = failure_log_type_prefixes[type]; + break; + } + } + + if (str_to_time(args[1], &t) < 0) { + i_error("Invalid timestamp: %s", args[1]); + t = 0; + } + + printf("%s %s%s%s\n", t_strflocaltime(LOG_TIMESTAMP_FORMAT, t), + args[2], type_prefix, args[3]); +} + +static void cmd_log_errors(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) +{ + struct istream *input; + const char *path, *line, *const *args; + int fd; + + path = t_strconcat(doveadm_settings->base_dir, + "/"LOG_ERRORS_FNAME, NULL); + fd = net_connect_unix(path); + if (fd == -1) + i_fatal("net_connect_unix(%s) failed: %m", path); + net_set_nonblock(fd, FALSE); + + input = i_stream_create_fd(fd, (size_t)-1, TRUE); + while ((line = i_stream_read_next_line(input)) != NULL) T_BEGIN { + args = t_strsplit_tabescaped(line); + if (str_array_length(args) == 4) + cmd_log_error_write(args); + else + i_error("Invalid input from log: %s", line); + } T_END; + i_stream_destroy(&input); +} + struct doveadm_cmd doveadm_cmd_log[] = { { cmd_log_test, "log test", "" }, { cmd_log_reopen, "log reopen", "" }, - { cmd_log_find, "log find", "[]" } + { cmd_log_find, "log find", "[]" }, + { cmd_log_errors, "log errors", "" } }; void doveadm_register_log_commands(void) diff --git a/src/log/Makefile.am b/src/log/Makefile.am index b2065aa51c..d9443a17a2 100644 --- a/src/log/Makefile.am +++ b/src/log/Makefile.am @@ -11,9 +11,13 @@ log_LDADD = $(LIBDOVECOT) log_DEPENDENCIES = $(LIBDOVECOT_DEPS) log_SOURCES = \ + doveadm-connection.c \ log-connection.c \ + log-error-buffer.c \ log-settings.c \ main.c noinst_HEADERS = \ - log-connection.h + doveadm-connection.h \ + log-connection.h \ + log-error-buffer.h diff --git a/src/log/log-connection.c b/src/log/log-connection.c index 87bfadda82..1a27cb25bf 100644 --- a/src/log/log-connection.c +++ b/src/log/log-connection.c @@ -8,6 +8,7 @@ #include "hash.h" #include "master-interface.h" #include "master-service.h" +#include "log-error-buffer.h" #include "log-connection.h" #include @@ -25,6 +26,7 @@ struct log_client { struct log_connection { struct log_connection *prev, *next; + struct log_error_buffer *errorbuf; int fd; int listen_fd; struct io *io; @@ -76,9 +78,39 @@ static void log_parse_option(struct log_connection *log, } } +static void +client_log_ctx(struct log_connection *log, + const struct failure_context *ctx, time_t log_time, + const char *prefix, const char *text) +{ + struct log_error err; + + switch (ctx->type) { + case LOG_TYPE_DEBUG: + case LOG_TYPE_INFO: + case LOG_TYPE_COUNT: + case LOG_TYPE_OPTION: + break; + case LOG_TYPE_WARNING: + case LOG_TYPE_ERROR: + case LOG_TYPE_FATAL: + case LOG_TYPE_PANIC: + memset(&err, 0, sizeof(err)); + err.type = ctx->type; + err.timestamp = log_time; + err.prefix = prefix; + err.text = text; + log_error_buffer_add(log->errorbuf, &err); + break; + } + i_set_failure_prefix(prefix); + i_log_type(ctx, "%s", text); + i_set_failure_prefix("log: "); +} + static void client_log_fatal(struct log_connection *log, struct log_client *client, - const char *line, const struct tm *tm) + const char *line, time_t log_time, const struct tm *tm) { struct failure_context failure_ctx; const char *prefix = log->default_prefix; @@ -95,13 +127,12 @@ client_log_fatal(struct log_connection *log, struct log_client *client, line, net_ip2addr(&client->ip)); } } - i_set_failure_prefix(prefix); - i_log_type(&failure_ctx, "master: %s", line); - i_set_failure_prefix("log: "); + client_log_ctx(log, &failure_ctx, log_time, prefix, + t_strconcat("master: ", line, NULL)); } static void -log_parse_master_line(const char *line, const struct tm *tm) +log_parse_master_line(const char *line, time_t log_time, const struct tm *tm) { struct log_connection *const *logs, *log; struct log_client *client; @@ -136,19 +167,20 @@ log_parse_master_line(const char *line, const struct tm *tm) } log_client_free(log, client, pid); } else if (strncmp(line, "FATAL ", 6) == 0) { - client_log_fatal(log, client, line + 6, tm); + client_log_fatal(log, client, line + 6, log_time, tm); } else if (strncmp(line, "DEFAULT-FATAL ", 14) == 0) { /* If the client has logged a fatal/panic, don't log this message. */ if (client == NULL || !client->fatal_logged) - client_log_fatal(log, client, line + 14, tm); + client_log_fatal(log, client, line + 14, log_time, tm); } else { i_error("Received unknown command from master: %s", line); } } static void -log_it(struct log_connection *log, const char *line, const struct tm *tm) +log_it(struct log_connection *log, const char *line, + time_t log_time, const struct tm *tm) { struct failure_line failure; struct failure_context failure_ctx; @@ -157,7 +189,7 @@ log_it(struct log_connection *log, const char *line, const struct tm *tm) if (log->master) { T_BEGIN { - log_parse_master_line(line, tm); + log_parse_master_line(line, log_time, tm); } T_END; return; } @@ -185,9 +217,7 @@ log_it(struct log_connection *log, const char *line, const struct tm *tm) prefix = client != NULL && client->prefix != NULL ? client->prefix : log->default_prefix; - i_set_failure_prefix(prefix); - i_log_type(&failure_ctx, "%s", failure.text); - i_set_failure_prefix("log: "); + client_log_ctx(log, &failure_ctx, log_time, prefix, failure.text); } static int log_connection_handshake(struct log_connection *log) @@ -256,7 +286,7 @@ static void log_connection_input(struct log_connection *log) tm = *localtime(&now); while ((line = i_stream_next_line(log->input)) != NULL) - log_it(log, line, &tm); + log_it(log, line, now, &tm); } if (log->input->eof) @@ -269,11 +299,13 @@ static void log_connection_input(struct log_connection *log) } } -struct log_connection *log_connection_create(int fd, int listen_fd) +struct log_connection * +log_connection_create(struct log_error_buffer *errorbuf, int fd, int listen_fd) { struct log_connection *log; log = i_new(struct log_connection, 1); + log->errorbuf = errorbuf; log->fd = fd; log->listen_fd = listen_fd; log->io = io_add(fd, IO_READ, log_connection_input, log); diff --git a/src/log/log-connection.h b/src/log/log-connection.h index 93f8193187..6a491bbcc3 100644 --- a/src/log/log-connection.h +++ b/src/log/log-connection.h @@ -1,7 +1,8 @@ #ifndef LOG_CONNECTION_H #define LOG_CONNECTION_H -struct log_connection *log_connection_create(int fd, int listen_fd); +struct log_connection * +log_connection_create(struct log_error_buffer *errorbuf, int fd, int listen_fd); void log_connection_destroy(struct log_connection *log); void log_connections_init(void); diff --git a/src/log/log-settings.c b/src/log/log-settings.c index 1e7f04c024..d0148cc86b 100644 --- a/src/log/log-settings.c +++ b/src/log/log-settings.c @@ -1,11 +1,25 @@ /* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "buffer.h" #include "settings-parser.h" #include "service-settings.h" #include +/* */ +static struct file_listener_settings log_unix_listeners_array[] = { + { "log-errors", 0600, "", "" } +}; +static struct file_listener_settings *log_unix_listeners[] = { + &log_unix_listeners_array[0] +}; +static buffer_t log_unix_listeners_buf = { + log_unix_listeners, + sizeof(log_unix_listeners), { 0, } +}; +/* */ + struct service_settings log_service_settings = { .name = "log", .protocol = "", @@ -23,10 +37,11 @@ struct service_settings log_service_settings = { .process_limit = 1, .client_limit = 0, .service_count = 0, - .idle_kill = 0, + .idle_kill = -1U, .vsz_limit = (uoff_t)-1, - .unix_listeners = ARRAY_INIT, + .unix_listeners = { { &log_unix_listeners_buf, + sizeof(log_unix_listeners[0]) } }, .fifo_listeners = ARRAY_INIT, .inet_listeners = ARRAY_INIT, diff --git a/src/log/main.c b/src/log/main.c index 6ce114debb..ad9573f43b 100644 --- a/src/log/main.c +++ b/src/log/main.c @@ -6,10 +6,14 @@ #include "master-interface.h" #include "master-service.h" #include "master-service-settings.h" +#include "log-error-buffer.h" #include "log-connection.h" +#include "doveadm-connection.h" #include +static struct log_error_buffer *errorbuf; + static void sig_reopen_logs(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { @@ -21,18 +25,28 @@ static void main_init(void) lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE, sig_reopen_logs, NULL); + errorbuf = log_error_buffer_init(); log_connections_init(); } static void main_deinit(void) { log_connections_deinit(); + log_error_buffer_deinit(&errorbuf); } static void client_connected(struct master_service_connection *conn) { + if (conn->fifo) + log_connection_create(errorbuf, conn->fd, conn->listen_fd); + else if (strcmp(conn->name, "log-errors") == 0) + doveadm_connection_create(errorbuf, conn->fd); + else { + i_error("Unknown listener name: %s", conn->name); + return; + } + master_service_client_connection_accept(conn); - log_connection_create(conn->fd, conn->listen_fd); } int main(int argc, char *argv[])