Skip to content

Conversation

@Lusitaniae
Copy link

@Lusitaniae Lusitaniae commented Dec 29, 2025

It allows the prometheus exporter to serve compressed responses when the appropriate Accept Encoding header is set by the client.

Closes #11320


Testing

fluent-bit.conf

[SERVICE]
    Flush        1
    Daemon       Off
    Log_Level    info
    Parsers_File parsers.conf

    HTTP_Server  On
    HTTP_Listen  0.0.0.0
    HTTP_Port    2020

[INPUT]
    Name        dummy
    Dummy       {"message":"dummy", "kubernetes":{"namespace_name": "default", "docker_id": "abc123", "pod_name": "pod1", "container_name": "mycontainer", "pod_id": "def456", "labels":{"app": "app1"}}, "duration": 20, "color": "red", "shape": "circle"}
    Tag         dummy.log

[INPUT]
    Name        dummy
    Dummy       {"message":"hello", "kubernetes":{"namespace_name": "default", "docker_id": "abc123", "pod_name": "pod1", "container_name": "mycontainer", "pod_id": "def456", "labels":{"app": "app1"}}, "duration": 60, "color": "blue", "shape": "square"}
    Tag         dummy.log2

[FILTER]
    name               log_to_metrics
    match              dummy.log*
    tag                test_metric
    metric_mode        counter
    metric_name        count_all_dummy_messages
    metric_description This metric counts dummy messages

[OUTPUT]
    name    prometheus_exporter
    match   *
    host    0.0.0.0
    port    9999

test requests

i@i:~/workspace/triton/fluent-bit/build$ curl localhost:9999/metrics -H "Accept-Encoding: gzip" -v --compressed
* Host localhost:9999 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:9999...
* connect to ::1 port 9999 from ::1 port 49998 failed: Connection refused
*   Trying 127.0.0.1:9999...
* Connected to localhost (127.0.0.1) port 9999
> GET /metrics HTTP/1.1
> Host: localhost:9999
> User-Agent: curl/8.5.0
> Accept: */*
> Accept-Encoding: gzip
> 
< HTTP/1.1 200 OK
< Server: Monkey/1.8.6
< Date: Mon, 29 Dec 2025 10:42:38 GMT
< Transfer-Encoding: chunked
< Content-Type: text/plain; version=0.0.4
< Content-Encoding: gzip
< 
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 21
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 22
* Connection #0 to host localhost left intact
i@i:~/workspace/triton/fluent-bit/build$ curl localhost:9999/metrics -v
* Host localhost:9999 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:9999...
* connect to ::1 port 9999 from ::1 port 50006 failed: Connection refused
*   Trying 127.0.0.1:9999...
* Connected to localhost (127.0.0.1) port 9999
> GET /metrics HTTP/1.1
> Host: localhost:9999
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Monkey/1.8.6
< Date: Mon, 29 Dec 2025 10:42:41 GMT
< Transfer-Encoding: chunked
< Content-Type: text/plain; version=0.0.4
< 
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 27
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 28
* Connection #0 to host localhost left intact

valgrind

valgrind --leak-check=full bin/fluent-bit --config fluent-bit.conf 
==378238== Memcheck, a memory error detector
==378238== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==378238== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==378238== Command: bin/fluent-bit --config fluent-bit.conf
==378238== 
Fluent Bit v4.2.3
* Copyright (C) 2015-2025 The Fluent Bit Authors
* Fluent Bit is a CNCF graduated project under the Fluent organization
* https://fluentbit.io

______ _                  _    ______ _ _             ___   _____ 
|  ___| |                | |   | ___ (_) |           /   | / __  \
| |_  | |_   _  ___ _ __ | |_  | |_/ /_| |_  __   __/ /| | `' / /'
|  _| | | | | |/ _ \ '_ \| __| | ___ \ | __| \ \ / / /_| |   / /  
| |   | | |_| |  __/ | | | |_  | |_/ / | |_   \ V /\___  |_./ /___
\_|   |_|\__,_|\___|_| |_|\__| \____/|_|\__|   \_/     |_(_)_____/
                                                                  
             Fluent Bit v4.2 – Direct Routes Ahead
         Celebrating 10 Years of Open, Fluent Innovation!

[2025/12/29 17:42:26.859033176] [error] [/home/i/workspace/triton/fluent-bit/src/config_format/flb_cf_fluentbit.c:289 errno=2] No such file or directory
[2025/12/29 17:42:26.873744505] [error] file=/home/i/workspace/triton/fluent-bit/build/parsers.conf
[2025/12/29 17:42:26.942204959] [ info] [fluent bit] version=4.2.3, commit=7b0c1aebb6, pid=378238
[2025/12/29 17:42:26.954875222] [ info] [storage] ver=1.5.4, type=memory, sync=normal, checksum=off, max_chunks_up=128
[2025/12/29 17:42:26.955273358] [ info] [simd    ] disabled
[2025/12/29 17:42:26.955536705] [ info] [cmetrics] version=1.0.6
[2025/12/29 17:42:26.955774331] [ info] [ctraces ] version=0.6.6
[2025/12/29 17:42:26.969658549] [ info] [input:dummy:dummy.0] initializing
[2025/12/29 17:42:26.970305614] [ info] [input:dummy:dummy.0] storage_strategy='memory' (memory only)
[2025/12/29 17:42:26.990696434] [ info] [input:dummy:dummy.1] initializing
[2025/12/29 17:42:26.990853866] [ info] [input:dummy:dummy.1] storage_strategy='memory' (memory only)
[2025/12/29 17:42:27.4379214] [ info] [input:emitter:emitter_for_log_to_metrics.0] initializing
[2025/12/29 17:42:27.4518352] [ info] [input:emitter:emitter_for_log_to_metrics.0] storage_strategy='memory' (memory only)
[2025/12/29 17:42:27.95259058] [ info] [output:prometheus_exporter:prometheus_exporter.0] listening iface=0.0.0.0 tcp_port=9999
[2025/12/29 17:42:27.158827405] [ info] [http_server] listen iface=0.0.0.0 tcp_port=2020
[2025/12/29 17:42:27.160001703] [ info] [sp] stream processor started
[2025/12/29 17:42:27.162278621] [ info] [engine] Shutdown Grace Period=5, Shutdown Input Grace Period=2
==378238== Warning: client switching stacks?  SP change: 0x63ac198 --> 0x59ebdb0
==378238==          to suppress, use: --max-stackframe=10224616 or greater
==378238== Warning: client switching stacks?  SP change: 0x59ebc98 --> 0x63ac198
==378238==          to suppress, use: --max-stackframe=10224896 or greater
==378238== Warning: client switching stacks?  SP change: 0x63ac4f8 --> 0x59ebc98
==378238==          to suppress, use: --max-stackframe=10225760 or greater
==378238==          further instances of this message will not be shown.
^C[2025/12/29 17:42:55] [engine] caught signal (SIGINT)
[2025/12/29 17:42:55.552831886] [ warn] [engine] service will shutdown in max 5 seconds
[2025/12/29 17:42:55.554362770] [ info] [engine] pausing all inputs..
[2025/12/29 17:42:55.556146059] [ info] [input] pausing dummy.0
[2025/12/29 17:42:55.559915898] [ info] [input] pausing dummy.1
[2025/12/29 17:42:55.560153776] [ info] [input] pausing emitter_for_log_to_metrics.0
[2025/12/29 17:42:56.448458227] [ info] [engine] service has stopped (0 pending tasks)
[2025/12/29 17:42:56.448966294] [ info] [input] pausing dummy.0
[2025/12/29 17:42:56.449161069] [ info] [input] pausing dummy.1
[2025/12/29 17:42:56.449256903] [ info] [input] pausing emitter_for_log_to_metrics.0
==378238== 
==378238== HEAP SUMMARY:
==378238==     in use at exit: 0 bytes in 0 blocks
==378238==   total heap usage: 77,249 allocs, 77,249 frees, 31,956,228 bytes allocated
==378238== 
==378238== All heap blocks were freed -- no leaks are possible
==378238== 
==378238== For lists of detected and suppressed errors, rerun with: -s
==378238== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.

Summary by CodeRabbit

  • New Features

    • Metrics endpoint now supports per-request gzip compression. When clients request gzip, responses are sent compressed to reduce bandwidth and improve transfer performance.
  • Bug Fixes / Reliability

    • Endpoint now returns a proper 404 when metrics are unavailable and gracefully falls back to uncompressed responses if compression fails.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Per-request gzip negotiation added to the Prometheus exporter's /metrics handler: the server inspects Accept-Encoding for a gzip token, conditionally compresses the metrics payload with flb_gzip_compress(), sets Content-Encoding: gzip on success, and falls back to uncompressed responses when gzip is not accepted or compression fails.

Changes

Cohort / File(s) Summary
Prometheus metrics endpoint (gzip support)
plugins/out_prometheus_exporter/prom_http.c
Added client_accepts_gzip() to parse Accept-Encoding for a gzip token (case-insensitive, token-delimited). Updated cb_metrics() to determine use_gzip, call flb_gzip_compress() when appropriate, set Content-Encoding: gzip on successful compression, handle compression failures by sending the uncompressed payload, and free compressed buffers after send. Added includes: flb_gzip.h, monkey/mk_http_parser.h, and string.h.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as cb_metrics()
    participant Parser as client_accepts_gzip()
    participant Compressor as flb_gzip_compress()
    participant Response

    Client->>Server: GET /metrics (Accept-Encoding: ...)
    Server->>Parser: Inspect Accept-Encoding header
    Parser-->>Server: gzip accepted? (true/false)
    alt gzip accepted
        Server->>Compressor: Compress metrics payload
        alt compression success
            Compressor-->>Server: compressed payload
            Note right of Server: Set Content-Encoding: gzip
            Server->>Response: Send compressed payload
        else compression fails
            Compressor-->>Server: error
            Server->>Response: Send uncompressed payload
        end
    else gzip not accepted
        Server->>Response: Send uncompressed payload
    end
    Response-->>Client: HTTP/1.1 200 (+ headers) + body
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I sniff the headers, find gzip in the breeze,
I squeeze the metrics down with quiet expertise,
If packing trips, I hop back to plain,
Small, tidy bytes — the rabbit's gain.

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding gzip compression support to the Prometheus exporter.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #11320 requirements: detects Accept-Encoding header, returns gzip-compressed responses when requested, and reduces payload size.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing gzip compression for the Prometheus exporter metrics endpoint; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 833ab79 and e710905.

📒 Files selected for processing (1)
  • plugins/out_prometheus_exporter/prom_http.c
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/out_prometheus_exporter/prom_http.c

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
plugins/out_prometheus_exporter/prom_http.c (2)

22-24: Consider removing unused string.h include.

The flb_gzip.h and mk_http_parser.h includes are necessary for the new functionality. However, string.h doesn't appear to be used by the new code—no new calls to strcpy, strcmp, strlen, or similar functions are introduced.

🔎 Proposed fix
 #include <fluent-bit/flb_gzip.h>
 #include <monkey/mk_http_parser.h>
-#include <string.h>

163-220: Token parsing looks correct; consider handling q=0 quality value (optional).

The token boundary detection logic is sound. However, per RFC 7231, a quality value of q=0 explicitly means the encoding is not acceptable (e.g., gzip;q=0, deflate). The current implementation would still return 1 for such cases.

This is a minor RFC compliance gap that's unlikely to affect real Prometheus scrapers, but worth noting.

If you want full compliance, you could extend the check to parse the quality value after detecting "gzip":

/* After finding "gzip" token, check for q=0 */
const char *semi = p + 4;
while (semi < end && (*semi == ' ' || *semi == '\t')) semi++;
if (semi < end && *semi == ';') {
    /* Parse quality value - skip if q=0 */
    // ... (parse "q=" and value)
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b0c1ae and b68c4ee.

📒 Files selected for processing (1)
  • plugins/out_prometheus_exporter/prom_http.c
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-29T06:25:27.250Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
📚 Learning: 2025-08-29T06:25:02.561Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:7-7
Timestamp: 2025-08-29T06:25:02.561Z
Learning: In Fluent Bit, ZSTD (zstandard) compression library is bundled directly in the source tree at `lib/zstd-1.5.7` and is built unconditionally as a static library. Unlike optional external dependencies, ZSTD does not use conditional compilation guards like `FLB_HAVE_ZSTD` and is always available. Headers like `<fluent-bit/flb_zstd.h>` can be included directly without guards.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
🧬 Code graph analysis (1)
plugins/out_prometheus_exporter/prom_http.c (3)
lib/monkey/mk_server/mk_http.c (1)
  • mk_http_header_get (1499-1528)
src/flb_gzip.c (1)
  • flb_gzip_compress (157-252)
include/fluent-bit/flb_mem.h (1)
  • flb_free (126-128)
🔇 Additional comments (1)
plugins/out_prometheus_exporter/prom_http.c (1)

241-269: Compression logic and memory handling look correct.

The fallback to uncompressed on compression failure is a good resilience pattern. Memory management is correct: flb_gzip_compress allocates internally and the caller frees with flb_free().

Note: The check on lines 251-254 is defensive—flb_gzip_compress doesn't set *out_data on failure paths, so compressed_data should remain NULL when ret != 0. The defensive check is fine to keep.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
plugins/out_prometheus_exporter/prom_http.c (1)

163-220: Consider simplifying token parsing with standard library functions.

The manual case-insensitive search and boundary validation work correctly but could be more maintainable. Consider using strncasecmp() or similar standard functions to simplify the logic, though the current implementation handles edge cases properly (e.g., preventing matches within words like "notgzip").

Alternative approach using standard functions
/* Check if client accepts gzip encoding */
static int client_accepts_gzip(mk_request_t *request)
{
    struct mk_http_header *header;
    char *p, *token;
    char *saveptr = NULL;
    char buf[256];
    
    /* Get Accept-Encoding header */
    header = mk_http_header_get(MK_HEADER_ACCEPT_ENCODING, request, NULL, 0);
    if (!header || header->val.len == 0 || header->val.len >= sizeof(buf)) {
        return 0;
    }
    
    /* Copy header value for tokenization */
    memcpy(buf, header->val.data, header->val.len);
    buf[header->val.len] = '\0';
    
    /* Parse comma-separated encoding list */
    token = strtok_r(buf, ",", &saveptr);
    while (token) {
        /* Skip leading whitespace */
        while (*token == ' ' || *token == '\t') token++;
        
        /* Check for gzip (case-insensitive) */
        if (strncasecmp(token, "gzip", 4) == 0) {
            char c = token[4];
            if (c == '\0' || c == ' ' || c == '\t' || c == ';') {
                return 1;
            }
        }
        token = strtok_r(NULL, ",", &saveptr);
    }
    
    return 0;
}

Note: This assumes a reasonable header length limit. Adjust if needed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b68c4ee and 833ab79.

📒 Files selected for processing (1)
  • plugins/out_prometheus_exporter/prom_http.c
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.
📚 Learning: 2025-08-29T06:25:27.250Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
📚 Learning: 2025-08-29T06:25:02.561Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:7-7
Timestamp: 2025-08-29T06:25:02.561Z
Learning: In Fluent Bit, ZSTD (zstandard) compression library is bundled directly in the source tree at `lib/zstd-1.5.7` and is built unconditionally as a static library. Unlike optional external dependencies, ZSTD does not use conditional compilation guards like `FLB_HAVE_ZSTD` and is always available. Headers like `<fluent-bit/flb_zstd.h>` can be included directly without guards.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
🔇 Additional comments (3)
plugins/out_prometheus_exporter/prom_http.c (3)

226-256: LGTM - Solid compression implementation with proper fallback.

The compression logic is well-structured:

  • Proper initialization of compressed_data to NULL enables safe cleanup
  • Client negotiation via client_accepts_gzip() respects HTTP standards
  • Graceful fallback to uncompressed response on compression failure
  • Correct memory management in error paths

The per-request compression is appropriate given this is an opt-in feature controlled by the client.


22-22: No changes needed to the gzip includes.

GZIP compression is unconditionally available in Fluent Bit and included without guards throughout the codebase (e.g., src/flb_http_common.c, src/http_server/flb_http_server.c, tests/runtime/in_forward.c). The direct include of flb_gzip.h is correct and consistent with existing patterns.

Likely an incorrect or invalid review comment.


258-273: No changes needed. The code at lines 258–273 is correct.

Lines 260 and 269 do not have trailing whitespace—these are clean blank lines. Additionally, the buf->users reference counting is thread-safe by design: buffers are stored in thread-local storage (via pthread_getspecific), so each Monkey HTTP worker thread maintains its own separate metrics list. The counter is only modified and read within the same thread context, eliminating race conditions.

Likely an incorrect or invalid review comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prometheus exporter should support compressed responses

1 participant