Coverage Report

Created: 2026-08-09 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
};