From 0787125ede9f0be9d9cbd760a0aed20e81025b51 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Thu, 29 Aug 2024 12:49:36 +0300 Subject: [PATCH] stats: Change event exporter transports to be extensible --- src/stats/Makefile.am | 1 + src/stats/client-reader.c | 3 +- src/stats/event-exporter-transport-drop.c | 11 +++++- src/stats/event-exporter-transport-file.c | 35 ++++++++++++----- .../event-exporter-transport-http-post.c | 13 +++++-- src/stats/event-exporter-transport-log.c | 11 +++++- src/stats/event-exporter.c | 38 +++++++++++++++++++ src/stats/event-exporter.h | 32 +++++++++++----- src/stats/stats-metrics.c | 34 +++-------------- src/stats/stats-metrics.h | 5 +-- src/stats/stats-settings.c | 17 +++------ src/stats/stats-settings.h | 1 + 12 files changed, 129 insertions(+), 72 deletions(-) create mode 100644 src/stats/event-exporter.c diff --git a/src/stats/Makefile.am b/src/stats/Makefile.am index a6b0da3483..2415bceb57 100644 --- a/src/stats/Makefile.am +++ b/src/stats/Makefile.am @@ -36,6 +36,7 @@ libstats_local_la_SOURCES = \ client-reader.c \ client-writer.c \ client-http.c \ + event-exporter.c \ event-exporter-fmt.c \ event-exporter-fmt-json.c \ event-exporter-fmt-none.c \ diff --git a/src/stats/client-reader.c b/src/stats/client-reader.c index e73ea982e1..59d81c85ba 100644 --- a/src/stats/client-reader.c +++ b/src/stats/client-reader.c @@ -12,6 +12,7 @@ #include "master-service.h" #include "stats-metrics.h" #include "stats-settings.h" +#include "event-exporter.h" #include "client-reader.h" #include "client-writer.h" #include "event-exporter.h" @@ -257,7 +258,7 @@ reader_client_input_args(struct connection *conn, const char *const *args) else if (strcmp(cmd, "DUMP-RESET") == 0) return reader_client_input_dump_reset(client, args); else if (strcmp(cmd, "REOPEN") == 0) { - event_export_transport_file_reopen(); + event_exporter_transports_reopen(); o_stream_nsend(client->conn.output, "+\n", 2); } return 1; diff --git a/src/stats/event-exporter-transport-drop.c b/src/stats/event-exporter-transport-drop.c index 943f30579f..06663e25f1 100644 --- a/src/stats/event-exporter-transport-drop.c +++ b/src/stats/event-exporter-transport-drop.c @@ -3,7 +3,14 @@ #include "lib.h" #include "event-exporter.h" -void event_export_transport_drop(const struct exporter *exporter ATTR_UNUSED, - const buffer_t *buf ATTR_UNUSED) +static void +event_exporter_drop_send(struct exporter *exporter ATTR_UNUSED, + const buffer_t *buf ATTR_UNUSED) { } + +const struct event_exporter_transport event_exporter_transport_drop = { + .name = "drop", + + .send = event_exporter_drop_send, +}; diff --git a/src/stats/event-exporter-transport-file.c b/src/stats/event-exporter-transport-file.c index 9f44cf1de9..e2640ef7eb 100644 --- a/src/stats/event-exporter-transport-file.c +++ b/src/stats/event-exporter-transport-file.c @@ -50,7 +50,7 @@ static void exporter_file_destroy(struct exporter_file **_node) i_free(node); } -void event_export_transport_file_deinit(void) +static void event_exporter_file_deinit(void) { struct exporter_file *node, *next = exporter_file_list_head; exporter_file_list_head = NULL; @@ -125,8 +125,8 @@ static bool exporter_file_open(struct exporter_file *node) return TRUE; } -static void event_export_transport_file_write(struct exporter_file *node, - const buffer_t *buf) +static void event_exporter_file_write(struct exporter_file *node, + const buffer_t *buf) { const struct const_iovec vec[] = { { .iov_base = buf->data, .iov_len = buf->used }, @@ -142,29 +142,29 @@ static void event_export_transport_file_write(struct exporter_file *node, } } -void event_export_transport_file(const struct exporter *exporter, - const buffer_t *buf) +static void +event_exporter_file_send(struct exporter *exporter, const buffer_t *buf) { struct exporter_file *node = exporter->transport_context; if (node == NULL) node = exporter_file_init(exporter, FALSE); if (!exporter_file_open(node)) return; - event_export_transport_file_write(node, buf); + event_exporter_file_write(node, buf); } -void event_export_transport_unix(const struct exporter *exporter, - const buffer_t *buf) +static void +event_exporter_unix_send(struct exporter *exporter, const buffer_t *buf) { struct exporter_file *node = exporter->transport_context; if (node == NULL) node = exporter_file_init(exporter, TRUE); if (!exporter_file_open(node)) return; - event_export_transport_file_write(node, buf); + event_exporter_file_write(node, buf); } -void event_export_transport_file_reopen(void) +static void event_exporter_file_reopen(void) { /* close all files, but not unix sockets */ struct exporter_file *node = exporter_file_list_head; @@ -174,3 +174,18 @@ void event_export_transport_file_reopen(void) node = node->next; } } + +const struct event_exporter_transport event_exporter_transport_file = { + .name = "file", + + .send = event_exporter_file_send, + .reopen = event_exporter_file_reopen, +}; + +const struct event_exporter_transport event_exporter_transport_unix = { + .name = "unix", + + .deinit = event_exporter_file_deinit, + .send = event_exporter_unix_send, + .reopen = event_exporter_file_reopen, +}; diff --git a/src/stats/event-exporter-transport-http-post.c b/src/stats/event-exporter-transport-http-post.c index 9ab6ccee80..9a5fc47233 100644 --- a/src/stats/event-exporter-transport-http-post.c +++ b/src/stats/event-exporter-transport-http-post.c @@ -14,7 +14,7 @@ /* the http client used to export all events with exporter=http-post */ static struct http_client *exporter_http_client; -void event_export_transport_http_post_deinit(void) +static void event_exporter_http_post_deinit(void) { if (exporter_http_client != NULL) http_client_deinit(&exporter_http_client); @@ -45,8 +45,8 @@ static void response_fxn(const struct http_response *response, suppressed = 0; } -void event_export_transport_http_post(const struct exporter *exporter, - const buffer_t *buf) +static void +event_exporter_http_post_send(struct exporter *exporter, const buffer_t *buf) { struct http_client_request *req; @@ -66,3 +66,10 @@ void event_export_transport_http_post(const struct exporter *exporter, http_client_request_set_timeout_msecs(req, exporter->transport_timeout); http_client_request_submit(req); } + +const struct event_exporter_transport event_exporter_transport_http_post = { + .name = "http-post", + + .deinit = event_exporter_http_post_deinit, + .send = event_exporter_http_post_send, +}; diff --git a/src/stats/event-exporter-transport-log.c b/src/stats/event-exporter-transport-log.c index a0cf67fcf1..3adceb2793 100644 --- a/src/stats/event-exporter-transport-log.c +++ b/src/stats/event-exporter-transport-log.c @@ -5,8 +5,15 @@ #include "str.h" #include "event-exporter.h" -void event_export_transport_log(const struct exporter *exporter ATTR_UNUSED, - const buffer_t *buf) +static void +event_exporter_log_send(struct exporter *exporter ATTR_UNUSED, + const buffer_t *buf) { i_info("%.*s", (int)buf->used, (const char *)buf->data); } + +const struct event_exporter_transport event_exporter_transport_log = { + .name = "log", + + .send = event_exporter_log_send, +}; diff --git a/src/stats/event-exporter.c b/src/stats/event-exporter.c new file mode 100644 index 0000000000..0fda4205fd --- /dev/null +++ b/src/stats/event-exporter.c @@ -0,0 +1,38 @@ +/* Copyright (c) 2024 Dovecot authors, see the included COPYING file */ + +#include "stats-common.h" +#include "event-exporter.h" + +static const struct event_exporter_transport *event_exporter_transports[] = { + &event_exporter_transport_drop, + &event_exporter_transport_file, + &event_exporter_transport_unix, + &event_exporter_transport_http_post, + &event_exporter_transport_log, +}; + +const struct event_exporter_transport * +event_exporter_transport_find(const char *name) +{ + for (unsigned int i = 0; i < N_ELEMENTS(event_exporter_transports); i++) { + if (strcmp(event_exporter_transports[i]->name, name) == 0) + return event_exporter_transports[i]; + } + return NULL; +} + +void event_exporter_transports_reopen(void) +{ + for (unsigned int i = 0; i < N_ELEMENTS(event_exporter_transports); i++) { + if (event_exporter_transports[i]->reopen != NULL) + event_exporter_transports[i]->reopen(); + } +} + +void event_exporter_transports_deinit(void) +{ + for (unsigned int i = 0; i < N_ELEMENTS(event_exporter_transports); i++) { + if (event_exporter_transports[i]->reopen != NULL) + event_exporter_transports[i]->deinit(); + } +} diff --git a/src/stats/event-exporter.h b/src/stats/event-exporter.h index 704f0bffa6..9003fb5876 100644 --- a/src/stats/event-exporter.h +++ b/src/stats/event-exporter.h @@ -3,21 +3,33 @@ #include "stats-metrics.h" +struct event_exporter_transport { + const char *name; + + void (*deinit)(void); + + /* function to send the event */ + void (*send)(struct exporter *exporter, const buffer_t *buf); + + void (*reopen)(void); +}; + +extern const struct event_exporter_transport event_exporter_transport_drop; +extern const struct event_exporter_transport event_exporter_transport_file; +extern const struct event_exporter_transport event_exporter_transport_unix; +extern const struct event_exporter_transport event_exporter_transport_http_post; +extern const struct event_exporter_transport event_exporter_transport_log; + +const struct event_exporter_transport * +event_exporter_transport_find(const char *name); +void event_exporter_transports_reopen(void); +void event_exporter_transports_deinit(void); + /* fmt functions */ void event_export_fmt_json(const struct metric *metric, struct event *event, buffer_t *dest); void event_export_fmt_none(const struct metric *metric, struct event *event, buffer_t *dest); void event_export_fmt_tabescaped_text(const struct metric *metric, struct event *event, buffer_t *dest); -/* transport functions */ -void event_export_transport_drop(const struct exporter *exporter, const buffer_t *buf); -void event_export_transport_http_post(const struct exporter *exporter, const buffer_t *buf); -void event_export_transport_http_post_deinit(void); -void event_export_transport_log(const struct exporter *exporter, const buffer_t *buf); -void event_export_transport_file(const struct exporter *exporter, const buffer_t *buf); -void event_export_transport_unix(const struct exporter *exporter, const buffer_t *buf); -void event_export_transport_file_reopen(void); -void event_export_transport_file_deinit(void); - /* append a microsecond resolution RFC3339 UTC timestamp */ void event_export_helper_fmt_rfc3339_time(string_t *dest, const struct timeval *time); /* append a microsecond resolution unix timestamp in seconds (i.e., %u.%06u) */ diff --git a/src/stats/stats-metrics.c b/src/stats/stats-metrics.c index 12336994b4..e19c63aff8 100644 --- a/src/stats/stats-metrics.c +++ b/src/stats/stats-metrics.c @@ -61,27 +61,11 @@ static void stats_exporters_add_set(struct stats_metrics *metrics, i_unreached(); } - /* TODO: The following should be plugable. - * - * Note: Make sure to mirror any changes to the below code in - * stats_exporter_settings_check(). - */ - if (strcmp(set->driver, "drop") == 0) { - exporter->transport = event_export_transport_drop; - } else if (strcmp(set->driver, "http-post") == 0) { - exporter->transport = event_export_transport_http_post; - } else if (strcmp(set->driver, "log") == 0) { - exporter->transport = event_export_transport_log; + if (strcmp(set->parsed_transport->name, "log") == 0) { exporter->format_max_field_len = LOG_EXPORTER_LONG_FIELD_TRUNCATE_LEN; - } else if (strcmp(set->driver, "file") == 0) { - exporter->transport = event_export_transport_file; - } else if (strcmp(set->driver, "unix") == 0) { - exporter->transport = event_export_transport_unix; - } else { - i_unreached(); } - + exporter->transport = set->parsed_transport; exporter->transport_args = set->transport_args; array_push_back(&metrics->exporters, &exporter); @@ -380,14 +364,6 @@ static void stats_metric_free(struct metric *metric) stats_metric_free(sub_metric); } -static void stats_export_deinit(void) -{ - /* no need for event_export_transport_drop_deinit() - no-op */ - event_export_transport_http_post_deinit(); - /* no need for event_export_transport_log_deinit() - no-op */ - event_export_transport_file_deinit(); -} - void stats_metrics_deinit(struct stats_metrics **_metrics) { struct stats_metrics *metrics = *_metrics; @@ -395,7 +371,7 @@ void stats_metrics_deinit(struct stats_metrics **_metrics) *_metrics = NULL; - stats_export_deinit(); + event_exporter_transports_deinit(); array_foreach_elem(&metrics->metrics, metric) stats_metric_free(metric); @@ -818,7 +794,7 @@ static void stats_export_event(struct metric *metric, struct event *oldevent) { const struct metric_export_info *info = &metric->export_info; - const struct exporter *exporter = info->exporter; + struct exporter *exporter = info->exporter; struct event *event; i_assert(exporter != NULL); @@ -831,7 +807,7 @@ stats_export_event(struct metric *metric, struct event *oldevent) buf = t_buffer_create(128); exporter->format(metric, event, buf); - exporter->transport(exporter, buf); + exporter->transport->send(exporter, buf); } T_END; event_unref(&event); diff --git a/src/stats/stats-metrics.h b/src/stats/stats-metrics.h index e2b43acc1c..a9968c035b 100644 --- a/src/stats/stats-metrics.h +++ b/src/stats/stats-metrics.h @@ -37,12 +37,11 @@ struct exporter { unsigned int transport_timeout; void *transport_context; - /* function to send the event */ - void (*transport)(const struct exporter *, const buffer_t *); + const struct event_exporter_transport *transport; }; struct metric_export_info { - const struct exporter *exporter; + struct exporter *exporter; enum event_exporter_includes { EVENT_EXPORTER_INCL_NONE = 0, diff --git a/src/stats/stats-settings.c b/src/stats/stats-settings.c index 2051f897bc..d9600bb2d3 100644 --- a/src/stats/stats-settings.c +++ b/src/stats/stats-settings.c @@ -5,6 +5,7 @@ #include "settings.h" #include "service-settings.h" #include "stats-settings.h" +#include "event-exporter.h" #include "array.h" #include "str.h" #include "var-expand.h" @@ -260,24 +261,16 @@ static bool stats_exporter_settings_check(void *_set, pool_t pool ATTR_UNUSED, return FALSE; } - /* TODO: The following should be plugable. - * - * Note: Make sure to mirror any changes to the below code in - * stats_exporters_add_set(). - */ if (set->driver[0] == '\0') set->driver = set->name; - if (strcmp(set->driver, "drop") == 0 || - strcmp(set->driver, "http-post") == 0 || - strcmp(set->driver, "log") == 0 || - strcmp(set->driver, "file") == 0 || - strcmp(set->driver, "unix") == 0) { - /* no-op */ - } else { +#ifndef CONFIG_BINARY + set->parsed_transport = event_exporter_transport_find(set->driver); + if (set->parsed_transport == NULL) { *error_r = t_strdup_printf("Unknown evente_exporter_driver: %s", set->driver); return FALSE; } +#endif if (!parse_format_args(set, error_r)) return FALSE; diff --git a/src/stats/stats-settings.h b/src/stats/stats-settings.h index f74a8c4c11..b5590b5760 100644 --- a/src/stats/stats-settings.h +++ b/src/stats/stats-settings.h @@ -74,6 +74,7 @@ struct stats_exporter_settings { /* parsed values */ enum event_exporter_time_fmt parsed_time_format; + const struct event_exporter_transport *parsed_transport; }; /* */ -- 2.47.3