From: Aki Tuomi Date: Tue, 3 Oct 2023 12:01:56 +0000 (+0300) Subject: stats: Add file transport X-Git-Tag: 2.4.0~2480 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=901a3b623896426a30d2b86c8a2f2a89074ae0f8;p=thirdparty%2Fdovecot%2Fcore.git stats: Add file transport --- diff --git a/src/stats/Makefile.am b/src/stats/Makefile.am index 0d6f598286..f538cb9a6a 100644 --- a/src/stats/Makefile.am +++ b/src/stats/Makefile.am @@ -42,6 +42,7 @@ libstats_local_la_SOURCES = \ event-exporter-transport-drop.c \ event-exporter-transport-http-post.c \ event-exporter-transport-log.c \ + event-exporter-transport-file.c \ $(stats_services) \ stats-service.c \ stats-event-category.c \ diff --git a/src/stats/event-exporter-transport-file.c b/src/stats/event-exporter-transport-file.c new file mode 100644 index 0000000000..41cfe2d7b1 --- /dev/null +++ b/src/stats/event-exporter-transport-file.c @@ -0,0 +1,118 @@ +/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "eacces-error.h" +#include "ostream.h" +#include "event-exporter.h" + +#include +#include +#include + +struct exporter_file { + struct exporter_file *next; + char *fname; + struct ostream *output; + int fd; + time_t last_error; +}; + +#define EXPORTER_LAST_ERROR_DELAY 60 + +static struct exporter_file *exporter_file_list_head = NULL; + +static void exporter_file_close(struct exporter_file *node) +{ + if (o_stream_finish(node->output) < 0) { + i_error("write(%s) failed: %s", node->fname, + o_stream_get_error(node->output)); + node->last_error = ioloop_time; + } + o_stream_destroy(&node->output); + i_close_fd(&node->fd); +} + +static void exporter_file_destroy(struct exporter_file **_node) +{ + struct exporter_file *node = *_node; + if (node == NULL) + return; + *_node = NULL; + + exporter_file_close(node); + i_free(node->fname); + i_free(node); +} + +void event_export_transport_file_deinit(void) +{ + struct exporter_file *node, *next = exporter_file_list_head; + exporter_file_list_head = NULL; + while (next != NULL) { + node = next; + next = node->next; + exporter_file_destroy(&node); + } +} + +static struct exporter_file *exporter_file_init(const struct exporter *exporter) +{ + struct exporter_file *node; + node = i_new(struct exporter_file, 1); + node->fname = i_strdup(t_strcut(exporter->transport_args, ' ')); + node->fd = -1; + node->next = exporter_file_list_head; + exporter_file_list_head = node; + event_export_transport_assign_context(exporter, node); + return node; +} + +static void exporter_file_open_error(struct exporter_file *node, const char *func) +{ + if (errno != EACCES) + i_error("%s(%s) failed: %m", func, node->fname); + else + i_error("%s", eacces_error_get_creating(func, node->fname)); + node->last_error = ioloop_time; +} + +static bool exporter_file_open(struct exporter_file *node) +{ + if (likely(node->output != NULL && !node->output->closed)) + return TRUE; + o_stream_destroy(&node->output); + i_close_fd(&node->fd); + node->fd = open(node->fname, O_CREAT|O_APPEND|O_WRONLY, 0600); + if (node->fd == -1) { + if (ioloop_time - node->last_error > EXPORTER_LAST_ERROR_DELAY) + exporter_file_open_error(node, "open"); + return FALSE; + } + node->output = o_stream_create_fd_file(node->fd, UOFF_T_MAX, FALSE); + o_stream_set_name(node->output, node->fname); + return TRUE; +} + +void event_export_transport_file(const struct exporter *exporter, + const buffer_t *buf) +{ + struct exporter_file *node = exporter->transport_context; + if (node == NULL) + node = exporter_file_init(exporter); + if (!exporter_file_open(node)) + return; + const struct const_iovec vec[] = { + { .iov_base = buf->data, .iov_len = buf->used }, + { .iov_base = "\n", .iov_len = 1 } + }; + if (o_stream_sendv(node->output, vec, N_ELEMENTS(vec)) < 0) { + if (ioloop_time - node->last_error > EXPORTER_LAST_ERROR_DELAY) { + i_error("write(%s): %s", o_stream_get_name(node->output), + o_stream_get_error(node->output)); + node->last_error = ioloop_time; + } + o_stream_close(node->output); + } +} diff --git a/src/stats/event-exporter.h b/src/stats/event-exporter.h index 2d6c94f746..8fdfeb85a5 100644 --- a/src/stats/event-exporter.h +++ b/src/stats/event-exporter.h @@ -13,6 +13,8 @@ void event_export_transport_drop(const struct exporter *exporter, const buffer_t 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_file_deinit(void); /* append a microsecond resolution RFC3339 UTC timestamp */ void event_export_helper_fmt_rfc3339_time(string_t *dest, const struct timeval *time); diff --git a/src/stats/stats-metrics.c b/src/stats/stats-metrics.c index 4fd2f3a1bb..ff5bce6643 100644 --- a/src/stats/stats-metrics.c +++ b/src/stats/stats-metrics.c @@ -72,6 +72,8 @@ static void stats_exporters_add_set(struct stats_metrics *metrics, exporter->transport = event_export_transport_log; exporter->format_max_field_len = LOG_EXPORTER_LONG_FIELD_TRUNCATE_LEN; + } else if (strcmp(set->transport, "file") == 0) { + exporter->transport = event_export_transport_file; } else { i_unreached(); } @@ -317,6 +319,7 @@ 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) diff --git a/src/stats/stats-settings.c b/src/stats/stats-settings.c index a05aaa3146..276212f84d 100644 --- a/src/stats/stats-settings.c +++ b/src/stats/stats-settings.c @@ -288,7 +288,8 @@ static bool stats_exporter_settings_check(void *_set, pool_t pool ATTR_UNUSED, return FALSE; } else if (strcmp(set->transport, "drop") == 0 || strcmp(set->transport, "http-post") == 0 || - strcmp(set->transport, "log") == 0) { + strcmp(set->transport, "log") == 0 || + strcmp(set->transport, "file") == 0) { /* no-op */ } else { *error_r = t_strdup_printf("Unknown transport type '%s'",