|
| 1 | +/* |
| 2 | + * Copyright (c) 2025 One Identity |
| 3 | + * |
| 4 | + * This library is free software; you can redistribute it and/or |
| 5 | + * modify it under the terms of the GNU Lesser General Public |
| 6 | + * License as published by the Free Software Foundation; either |
| 7 | + * version 2.1 of the License, or (at your option) any later version. |
| 8 | + * |
| 9 | + * This library is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | + * Lesser General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU Lesser General Public |
| 15 | + * License along with this library; if not, write to the Free Software |
| 16 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | + * |
| 18 | + * As an additional exemption you are allowed to compile & link against the |
| 19 | + * OpenSSL libraries as published by the OpenSSL project. See the file |
| 20 | + * COPYING for details. |
| 21 | + * |
| 22 | + */ |
| 23 | +#include "logproto/logproto-prometheus-scraper-responder.h" |
| 24 | +#include "stats/stats-prometheus.h" |
| 25 | +#include "stats/stats-query-commands.h" |
| 26 | +#include "messages.h" |
| 27 | + |
| 28 | +static void |
| 29 | +_generate_batched_response(const gchar *record, gpointer user_data) |
| 30 | +{ |
| 31 | + gpointer *args = (gpointer *) user_data; |
| 32 | + //LogProtoPrometheusScraperResponder *self = (LogProtoPrometheusScraperResponder *)args[0]; |
| 33 | + GString **batch = (GString **) args[1]; |
| 34 | + |
| 35 | + g_string_append_printf(*batch, "%s", record); |
| 36 | +} |
| 37 | + |
| 38 | +static GString * |
| 39 | +_get_stats(LogProtoPrometheusScraperResponder *self) |
| 40 | +{ |
| 41 | + GString *stats = NULL; |
| 42 | + gboolean cancelled = FALSE; |
| 43 | + |
| 44 | + if (self->options->stat_type == STT_STAT) |
| 45 | + { |
| 46 | + gpointer args[] = {self, &stats}; |
| 47 | + gboolean with_legacy = TRUE; |
| 48 | + stats_generate_prometheus(_generate_batched_response, args, with_legacy, &cancelled); |
| 49 | + } |
| 50 | + else |
| 51 | + stats = stats_execute_query_command("QUERY GET prometheus *", self, &cancelled); |
| 52 | + |
| 53 | + return stats; |
| 54 | +} |
| 55 | + |
| 56 | +static gboolean |
| 57 | +_check_headers(gchar *buffer_start, gsize buffer_bytes) |
| 58 | +{ |
| 59 | + // TODO: add a generic header pareser to LogProtoHTTPServer and use it here |
| 60 | + gchar **lines = g_strsplit(buffer_start, "\n", 1); |
| 61 | + |
| 62 | + // First line must be like 'GET /metrics HTTP/1.1\x0d\x0a' |
| 63 | + gchar *line = lines && lines[0] ? lines[0] : buffer_start; |
| 64 | + gchar **tokens = g_strsplit(line, " ", 3); |
| 65 | + |
| 66 | + gboolean result = (tokens == NULL || tokens[0] == NULL || strcmp(tokens[0], "GET") |
| 67 | + || tokens[1] == NULL || strcmp(tokens[1], "/metrics")); |
| 68 | + |
| 69 | + // TODO: Check further headers as well to support options like compression, etc. |
| 70 | + if (FALSE == result) |
| 71 | + msg_error("Unknown request", evt_tag_str("http-server-response", line)); |
| 72 | + |
| 73 | + g_strfreev(tokens); |
| 74 | + g_strfreev(lines); |
| 75 | + return result; |
| 76 | +} |
| 77 | + |
| 78 | +static GString * |
| 79 | +_process_data(LogProtoHTTPServer *s, LogProtoBufferedServerState *state, |
| 80 | + const guchar *buffer_start, gsize buffer_bytes) |
| 81 | +{ |
| 82 | + LogProtoPrometheusScraperResponder *self = (LogProtoPrometheusScraperResponder *)s; |
| 83 | + GString *response_data = NULL; |
| 84 | + |
| 85 | + if (_check_headers((gchar *)buffer_start, buffer_bytes)) |
| 86 | + response_data = _get_stats(self); |
| 87 | + return response_data; |
| 88 | +} |
| 89 | + |
| 90 | +void |
| 91 | +log_proto_prometheus_scraper_responer_init(LogProtoPrometheusScraperResponder *self, LogTransport *transport, |
| 92 | + const LogProtoPrometheusScraperResponderOptions *options) |
| 93 | +{ |
| 94 | + log_proto_http_server_init((LogProtoHTTPServer *)self, transport, &options->super); |
| 95 | + self->options = options; |
| 96 | + self->super.data_processor = _process_data; |
| 97 | +} |
| 98 | + |
| 99 | +LogProtoServer * |
| 100 | +log_proto_prometheus_scraper_responer_new(LogTransport *transport, |
| 101 | + const LogProtoPrometheusScraperResponderOptions *options) |
| 102 | +{ |
| 103 | + LogProtoPrometheusScraperResponder *self = g_new0(LogProtoPrometheusScraperResponder, 1); |
| 104 | + |
| 105 | + log_proto_prometheus_scraper_responer_init(self, transport, options); |
| 106 | + return &self->super.super.super.super; |
| 107 | +} |
| 108 | + |
| 109 | +/* Options */ |
| 110 | + |
| 111 | +void |
| 112 | +log_proto_prometheus_scraper_responer_options_defaults(LogProtoPrometheusScraperResponderOptions *options) |
| 113 | +{ |
| 114 | + memset(options, 0, sizeof(*options)); |
| 115 | + options->stat_type = STT_STAT; |
| 116 | +} |
| 117 | + |
| 118 | +void |
| 119 | +log_proto_prometheus_scraper_responer_options_init(LogProtoPrometheusScraperResponderOptions *options, |
| 120 | + GlobalConfig *cfg) |
| 121 | +{ |
| 122 | + if (options->initialized) |
| 123 | + return; |
| 124 | + log_proto_server_options_init(&options->super, cfg); |
| 125 | + options->initialized = TRUE; |
| 126 | +} |
| 127 | + |
| 128 | +void |
| 129 | +log_proto_prometheus_scraper_responer_destroy(LogProtoPrometheusScraperResponderOptions *options) |
| 130 | +{ |
| 131 | + // if (options->destroy) |
| 132 | + // options->destroy(options); |
| 133 | + options->initialized = FALSE; |
| 134 | +} |
0 commit comments