]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Added log throttling API.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 2 Aug 2016 20:14:23 +0000 (23:14 +0300)
committerGitLab <gitlab@git.dovecot.net>
Thu, 4 Aug 2016 18:19:37 +0000 (21:19 +0300)
src/lib/Makefile.am
src/lib/log-throttle.c [new file with mode: 0644]
src/lib/log-throttle.h [new file with mode: 0644]
src/lib/test-lib.c
src/lib/test-lib.h
src/lib/test-log-throttle.c [new file with mode: 0644]

index 00af4cfc10cec6256b6cbaf0af2968e3e14ff9dc..db8903c742c579e426c3009ced8d02c6ac59fda5 100644 (file)
@@ -91,6 +91,7 @@ liblib_la_SOURCES = \
        json-tree.c \
        lib.c \
        lib-signals.c \
+       log-throttle.c \
        md4.c \
        md5.c \
        mempool.c \
@@ -230,6 +231,7 @@ headers = \
        lib.h \
        lib-signals.h \
        llist.h \
+       log-throttle.h \
        macros.h \
        md4.h \
        md5.h \
@@ -332,6 +334,7 @@ test_lib_SOURCES = \
        test-json-parser.c \
        test-json-tree.c \
        test-llist.c \
+       test-log-throttle.c \
        test-mempool-alloconly.c \
        test-pkcs5.c \
        test-net.c \
diff --git a/src/lib/log-throttle.c b/src/lib/log-throttle.c
new file mode 100644 (file)
index 0000000..0c15619
--- /dev/null
@@ -0,0 +1,78 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "time-util.h"
+#include "log-throttle.h"
+
+struct log_throttle {
+       struct log_throttle_settings set;
+       log_throttle_callback_t *callback;
+       void *context;
+
+       struct timeval last_time;
+       unsigned int last_count;
+
+       struct timeout *to_throttled;
+};
+
+#undef log_throttle_init
+struct log_throttle *
+log_throttle_init(const struct log_throttle_settings *set,
+                 log_throttle_callback_t *callback, void *context)
+{
+       struct log_throttle *throttle;
+
+       i_assert(set->throttle_at_max_per_interval > 0);
+       i_assert(set->unthrottle_at_max_per_interval > 0);
+
+       throttle = i_new(struct log_throttle, 1);
+       throttle->set = *set;
+       if (throttle->set.interval_msecs == 0)
+               throttle->set.interval_msecs = 1000;
+       throttle->callback = callback;
+       throttle->context = context;
+       return throttle;
+}
+
+void log_throttle_deinit(struct log_throttle **_throttle)
+{
+       struct log_throttle *throttle = *_throttle;
+
+       *_throttle = NULL;
+       if (throttle->to_throttled != NULL)
+               timeout_remove(&throttle->to_throttled);
+       i_free(throttle);
+}
+
+static void log_throttle_callback(struct log_throttle *throttle)
+{
+       if (throttle->last_count > 0)
+               throttle->callback(throttle->last_count, throttle->context);
+       if (throttle->last_count < throttle->set.unthrottle_at_max_per_interval)
+               timeout_remove(&throttle->to_throttled);
+       throttle->last_count = 0;
+}
+
+bool log_throttle_accept(struct log_throttle *throttle)
+{
+       if (throttle->to_throttled != NULL) {
+               /* unthrottling and last_count resets are done only by
+                  the callback */
+               throttle->last_count++;
+               return FALSE;
+       } else if (timeval_diff_msecs(&ioloop_timeval, &throttle->last_time) >=
+                               (int)throttle->set.interval_msecs) {
+               throttle->last_time = ioloop_timeval;
+               throttle->last_count = 1;
+               return TRUE;
+       } else if (++throttle->last_count <= throttle->set.throttle_at_max_per_interval) {
+               return TRUE;
+       } else {
+               throttle->last_count = 1;
+               throttle->to_throttled =
+                       timeout_add(throttle->set.interval_msecs,
+                                   log_throttle_callback, throttle);
+               return FALSE;
+       }
+}
diff --git a/src/lib/log-throttle.h b/src/lib/log-throttle.h
new file mode 100644 (file)
index 0000000..fc13b20
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef LOG_THROTTLE_H
+#define LOG_THROTTLE_H
+
+struct log_throttle_settings {
+       /* Start throttling after we reach this many log events/interval. */
+       unsigned int throttle_at_max_per_interval;
+       /* Throttling continues until there's only this many or below
+          log events/interval. */
+       unsigned int unthrottle_at_max_per_interval;
+       /* Interval unit in milliseconds. The throttled-callback is also called
+          at this interval. Default (0) is 1000 milliseconds. */
+       unsigned int interval_msecs;
+};
+
+typedef void
+log_throttle_callback_t(unsigned int new_events_count, void *context);
+
+struct log_throttle *
+log_throttle_init(const struct log_throttle_settings *set,
+                 log_throttle_callback_t *callback, void *context);
+#define log_throttle_init(set, callback, context) \
+       log_throttle_init(set + \
+               CALLBACK_TYPECHECK(callback, void (*)(unsigned int, typeof(context))), \
+               (log_throttle_callback_t *)callback, context)
+void log_throttle_deinit(struct log_throttle **throttle);
+
+/* Increase event count. Returns TRUE if the event should be logged,
+   FALSE if it's throttled. ioloop_timeval is used to determine the current
+   time. */
+bool log_throttle_accept(struct log_throttle *throttle);
+
+#endif
index 46f6f72fda95c5f3a6c3598917679366b08f6129..58911c09376848088ef746736cd0a098dcbe05dd 100644 (file)
@@ -36,6 +36,7 @@ int main(void)
                test_json_parser,
                test_json_tree,
                test_llist,
+               test_log_throttle,
                test_mempool_alloconly,
                test_net,
                test_numpack,
index 23fc1eee6cfc3aa4e17a95397ad55c1a3c2e216a..5c81fe0c8dddfb349e425c3ca91a12051051622d 100644 (file)
@@ -37,6 +37,7 @@ void test_istream_unix(void);
 void test_json_parser(void);
 void test_json_tree(void);
 void test_llist(void);
+void test_log_throttle(void);
 void test_mempool_alloconly(void);
 enum fatal_test_state fatal_mempool(int);
 void test_pkcs5_pbkdf2(void);
diff --git a/src/lib/test-log-throttle.c b/src/lib/test-log-throttle.c
new file mode 100644 (file)
index 0000000..181216c
--- /dev/null
@@ -0,0 +1,57 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "ioloop.h"
+#include "log-throttle.h"
+
+static unsigned int test_log_throttle_new_events_count;
+
+static void test_log_throttle_callback(unsigned int new_events_count,
+                                      struct ioloop *ioloop)
+{
+       test_log_throttle_new_events_count = new_events_count;
+       io_loop_stop(ioloop);
+}
+
+void test_log_throttle(void)
+{
+       const struct log_throttle_settings set = {
+               .throttle_at_max_per_interval = 10,
+               .unthrottle_at_max_per_interval = 5,
+               .interval_msecs = 10,
+       };
+       struct log_throttle *throttle;
+       struct ioloop *ioloop;
+       unsigned int i;
+
+       test_begin("log throttle");
+
+       ioloop = io_loop_create();
+       throttle = log_throttle_init(&set, test_log_throttle_callback, ioloop);
+
+       /* throttle once and drop out just below */
+       for (i = 0; i < 10; i++)
+               test_assert_idx(log_throttle_accept(throttle), i);
+       for (i = 0; i < 4; i++)
+               test_assert_idx(!log_throttle_accept(throttle), i);
+
+       io_loop_run(ioloop);
+       test_assert(test_log_throttle_new_events_count == 4);
+
+       /* throttle and continue just above */
+       for (i = 0; i < 10; i++)
+               test_assert_idx(log_throttle_accept(throttle), i);
+       for (i = 0; i < 5; i++)
+               test_assert_idx(!log_throttle_accept(throttle), i);
+
+       io_loop_run(ioloop);
+       test_assert(test_log_throttle_new_events_count == 5);
+
+       /* we should be still throttled */
+       test_assert(!log_throttle_accept(throttle));
+
+       log_throttle_deinit(&throttle);
+       io_loop_destroy(&ioloop);
+
+       test_end();
+}