/src/fluent-bit/plugins/in_prometheus_scrape/prom_scrape.c
Line | Count | Source |
1 | | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* Fluent Bit |
4 | | * ========== |
5 | | * Copyright (C) 2015-2026 The Fluent Bit Authors |
6 | | * |
7 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | * you may not use this file except in compliance with the License. |
9 | | * You may obtain a copy of the License at |
10 | | * |
11 | | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | | * |
13 | | * Unless required by applicable law or agreed to in writing, software |
14 | | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | * See the License for the specific language governing permissions and |
17 | | * limitations under the License. |
18 | | */ |
19 | | |
20 | | #include <fluent-bit/flb_input_plugin.h> |
21 | | #include <fluent-bit/flb_http_client.h> |
22 | | #include <fluent-bit/flb_upstream.h> |
23 | | |
24 | | #include <cmetrics/cmt_decode_prometheus.h> |
25 | | |
26 | | #include "prom_scrape.h" |
27 | | |
28 | | static struct prom_scrape *prom_scrape_create(struct flb_input_instance *ins, |
29 | | struct flb_config *config) |
30 | 0 | { |
31 | 0 | int ret; |
32 | 0 | int upstream_flags; |
33 | 0 | struct prom_scrape *ctx; |
34 | 0 | struct flb_upstream *upstream; |
35 | |
|
36 | 0 | if (ins->host.name == NULL) { |
37 | 0 | ins->host.name = flb_sds_create("localhost"); |
38 | 0 | } |
39 | 0 | if (ins->host.port == 0) { |
40 | 0 | ins->host.port = 9100; |
41 | 0 | } |
42 | |
|
43 | 0 | ctx = flb_calloc(1, sizeof(struct prom_scrape)); |
44 | 0 | if (!ctx) { |
45 | 0 | flb_errno(); |
46 | 0 | return NULL; |
47 | 0 | } |
48 | 0 | ctx->ins = ins; |
49 | | |
50 | | /* Load the config map */ |
51 | 0 | ret = flb_input_config_map_set(ins, (void *) ctx); |
52 | 0 | if (ret == -1) { |
53 | 0 | flb_free(ctx); |
54 | 0 | return NULL; |
55 | 0 | } |
56 | | |
57 | 0 | upstream_flags = FLB_IO_TCP; |
58 | |
|
59 | 0 | if (ins->use_tls) { |
60 | 0 | upstream_flags |= FLB_IO_TLS; |
61 | 0 | } |
62 | |
|
63 | 0 | upstream = flb_upstream_create(config, ins->host.name, ins->host.port, |
64 | 0 | upstream_flags, ins->tls); |
65 | |
|
66 | 0 | if (!upstream) { |
67 | 0 | flb_plg_error(ins, "upstream initialization error"); |
68 | 0 | flb_free(ctx); |
69 | 0 | return NULL; |
70 | 0 | } |
71 | 0 | ctx->upstream = upstream; |
72 | |
|
73 | 0 | return ctx; |
74 | 0 | } |
75 | | |
76 | | static int collect_metrics(struct prom_scrape *ctx) |
77 | 0 | { |
78 | 0 | int ret = -1; |
79 | 0 | char errbuf[1024]; |
80 | 0 | size_t b_sent; |
81 | 0 | struct flb_http_client *c; |
82 | 0 | struct flb_connection *u_conn; |
83 | 0 | struct cmt *cmt = NULL; |
84 | 0 | struct cmt_decode_prometheus_parse_opts opts = {0}; |
85 | | |
86 | | /* get upstream connection */ |
87 | 0 | u_conn = flb_upstream_conn_get(ctx->upstream); |
88 | 0 | if (!u_conn) { |
89 | 0 | flb_plg_error(ctx->ins, "could not get an upstream connection to %s:%u", |
90 | 0 | ctx->ins->host.name, ctx->ins->host.port); |
91 | 0 | return -1; |
92 | 0 | } |
93 | | |
94 | 0 | c = flb_http_client(u_conn, FLB_HTTP_GET, ctx->metrics_path, |
95 | 0 | NULL, 0, |
96 | 0 | ctx->ins->host.name, ctx->ins->host.port, NULL, 0); |
97 | 0 | if (!c) { |
98 | 0 | flb_plg_error(ctx->ins, "unable to create http client"); |
99 | 0 | goto client_error; |
100 | 0 | } |
101 | | |
102 | 0 | flb_http_buffer_size(c, ctx->buffer_max_size); |
103 | | |
104 | | /* Auth headers */ |
105 | 0 | if (ctx->http_user && ctx->http_passwd) { /* Basic */ |
106 | 0 | flb_http_basic_auth(c, ctx->http_user, ctx->http_passwd); |
107 | 0 | } else if (ctx->bearer_token) { /* Bearer token */ |
108 | 0 | flb_http_bearer_auth(c, ctx->bearer_token); |
109 | 0 | } |
110 | | |
111 | | /* Add User-Agent */ |
112 | 0 | flb_http_add_header(c, "User-Agent", 10, "Fluent-Bit", 10); |
113 | |
|
114 | 0 | ret = flb_http_do(c, &b_sent); |
115 | 0 | if (ret != 0) { |
116 | 0 | flb_plg_error(ctx->ins, "http do error"); |
117 | 0 | goto http_error; |
118 | 0 | } |
119 | | |
120 | 0 | if (c->resp.status != 200) { |
121 | 0 | flb_plg_error(ctx->ins, "http status code error: [%s] %d", |
122 | 0 | ctx->metrics_path, c->resp.status); |
123 | 0 | goto http_error; |
124 | 0 | } |
125 | | |
126 | 0 | if (c->resp.payload_size <= 0) { |
127 | 0 | flb_plg_error(ctx->ins, "empty response"); |
128 | 0 | goto http_error; |
129 | 0 | } |
130 | | |
131 | | /* configure prometheus decoder options */ |
132 | 0 | opts.default_timestamp = cfl_time_now(); |
133 | 0 | opts.errbuf = errbuf; |
134 | 0 | opts.errbuf_size = sizeof(errbuf); |
135 | | |
136 | | /* convert Prometheus Text to CMetrics */ |
137 | 0 | ret = cmt_decode_prometheus_create(&cmt, |
138 | 0 | c->resp.payload, |
139 | 0 | c->resp.payload_size, |
140 | 0 | &opts); |
141 | 0 | if (ret == 0) { |
142 | | /* Append the updated metrics */ |
143 | 0 | ret = flb_input_metrics_append(ctx->ins, NULL, 0, cmt); |
144 | 0 | if (ret != 0) { |
145 | 0 | flb_plg_error(ctx->ins, "could not append metrics"); |
146 | 0 | } |
147 | 0 | cmt_destroy(cmt); |
148 | 0 | } |
149 | 0 | else { |
150 | 0 | flb_plg_error(ctx->ins, "error decoding Prometheus Text format"); |
151 | 0 | } |
152 | |
|
153 | 0 | http_error: |
154 | 0 | flb_http_client_destroy(c); |
155 | 0 | client_error: |
156 | 0 | flb_upstream_conn_release(u_conn); |
157 | |
|
158 | 0 | return ret; |
159 | 0 | } |
160 | | |
161 | | static int cb_prom_scrape_collect(struct flb_input_instance *ins, |
162 | | struct flb_config *config, void *in_context) |
163 | 0 | { |
164 | 0 | int rc; |
165 | 0 | struct prom_scrape *ctx = (struct prom_scrape *) in_context; |
166 | |
|
167 | 0 | rc = collect_metrics(ctx); |
168 | 0 | FLB_INPUT_RETURN(rc); |
169 | 0 | } |
170 | | |
171 | | static int cb_prom_scrape_init(struct flb_input_instance *ins, |
172 | | struct flb_config *config, void *data) |
173 | 0 | { |
174 | 0 | struct prom_scrape *ctx; |
175 | | |
176 | | /* Allocate space for the configuration */ |
177 | 0 | ctx = prom_scrape_create(ins, config); |
178 | 0 | if (!ctx) { |
179 | 0 | return -1; |
180 | 0 | } |
181 | | |
182 | 0 | flb_input_set_context(ins, ctx); |
183 | 0 | ctx->coll_id = flb_input_set_collector_time(ins, |
184 | 0 | cb_prom_scrape_collect, |
185 | 0 | ctx->scrape_interval, |
186 | 0 | 0, config); |
187 | 0 | return 0; |
188 | 0 | } |
189 | | |
190 | | static int prom_scrape_destroy(struct prom_scrape *ctx) |
191 | 0 | { |
192 | 0 | if (ctx->upstream) { |
193 | 0 | flb_upstream_destroy(ctx->upstream); |
194 | 0 | } |
195 | 0 | flb_free(ctx); |
196 | |
|
197 | 0 | return 0; |
198 | 0 | } |
199 | | |
200 | | static int cb_prom_scrape_exit(void *data, struct flb_config *config) |
201 | 0 | { |
202 | 0 | struct prom_scrape *ctx = (struct prom_scrape *) data; |
203 | |
|
204 | 0 | if (!ctx) { |
205 | 0 | return 0; |
206 | 0 | } |
207 | | |
208 | 0 | prom_scrape_destroy(ctx); |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | | /* Configuration properties map */ |
213 | | static struct flb_config_map config_map[] = { |
214 | | { |
215 | | FLB_CONFIG_MAP_TIME, "scrape_interval", "10s", |
216 | | 0, FLB_TRUE, offsetof(struct prom_scrape, scrape_interval), |
217 | | "Scraping interval." |
218 | | }, |
219 | | |
220 | | { |
221 | | FLB_CONFIG_MAP_SIZE, "buffer_max_size", HTTP_BUFFER_MAX_SIZE, |
222 | | 0, FLB_TRUE, offsetof(struct prom_scrape, buffer_max_size), |
223 | | "Set the maximum buffer size for the HTTP response." |
224 | | }, |
225 | | |
226 | | { |
227 | | FLB_CONFIG_MAP_STR, "metrics_path", DEFAULT_URI, |
228 | | 0, FLB_TRUE, offsetof(struct prom_scrape, metrics_path), |
229 | | "Set the metrics URI endpoint, it must start with a forward slash." |
230 | | }, |
231 | | |
232 | | { |
233 | | FLB_CONFIG_MAP_STR, "http_user", NULL, |
234 | | 0, FLB_TRUE, offsetof(struct prom_scrape, http_user), |
235 | | "Set HTTP auth user" |
236 | | }, |
237 | | |
238 | | { |
239 | | FLB_CONFIG_MAP_STR, "http_passwd", "", |
240 | | 0, FLB_TRUE, offsetof(struct prom_scrape, http_passwd), |
241 | | "Set HTTP auth password" |
242 | | }, |
243 | | |
244 | | { |
245 | | FLB_CONFIG_MAP_STR, "bearer_token", NULL, |
246 | | 0, FLB_TRUE, offsetof(struct prom_scrape, bearer_token), |
247 | | "Set bearer token auth" |
248 | | }, |
249 | | |
250 | | /* EOF */ |
251 | | {0} |
252 | | }; |
253 | | |
254 | | /* Plugin reference */ |
255 | | struct flb_input_plugin in_prometheus_scrape_plugin = { |
256 | | .name = "prometheus_scrape", |
257 | | .description = "Scrape metrics from Prometheus Endpoint", |
258 | | .cb_init = cb_prom_scrape_init, |
259 | | .cb_pre_run = NULL, |
260 | | .cb_collect = cb_prom_scrape_collect, |
261 | | .cb_flush_buf = NULL, |
262 | | .cb_exit = cb_prom_scrape_exit, |
263 | | .config_map = config_map, |
264 | | .flags = FLB_INPUT_NET | FLB_INPUT_CORO, |
265 | | }; |