]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
log: Keep track of last 1000 errors/warnings. "doveadm log errors" shows them.
authorTimo Sirainen <tss@iki.fi>
Mon, 6 Feb 2012 17:30:12 +0000 (19:30 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 6 Feb 2012 17:30:12 +0000 (19:30 +0200)
src/doveadm/doveadm-log.c
src/log/Makefile.am
src/log/log-connection.c
src/log/log-connection.h
src/log/log-settings.c
src/log/main.c

index aef60da0c418d28c5e76f3bcd1d70ed17ebe5df1..9eedb759c55d428db021f57b770fb215259a3bc8 100644 (file)
@@ -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)
+{
+       /* <type> <timestamp> <prefix> <text> */
+       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", "[<dir>]" }
+       { cmd_log_find, "log find", "[<dir>]" },
+       { cmd_log_errors, "log errors", "" }
 };
 
 void doveadm_register_log_commands(void)
index b2065aa51c5c4d6f9c0bbf3abac7cd4a112a37fa..d9443a17a29e6a13fc352d279f83bbc5503e8b3d 100644 (file)
@@ -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
index 87bfadda82f9b9949f748d9cf9e9e5e0e0513c2a..1a27cb25bf68c8e04d5b32953c780301dcf8f3ba 100644 (file)
@@ -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 <stdio.h>
@@ -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);
index 93f8193187cf70171fc80ef9a056f310609699b2..6a491bbcc384415e23e8cf2b0784e4f11dcd3cd3 100644 (file)
@@ -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);
index 1e7f04c024dd7b75554a2f9f6076d8d0b1cf9bed..d0148cc86b24e1d88b6269349c360001ff94f7ee 100644 (file)
@@ -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 <stddef.h>
 
+/* <settings checks> */
+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, }
+};
+/* </settings checks> */
+
 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,
 
index 6ce114debbfeaf49f20b36f7845c0b4ed5a781f4..ad9573f43b30b82c297d622d74b0458a9d4243e5 100644 (file)
@@ -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 <unistd.h>
 
+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[])