Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions include/fluent-bit/flb_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
#ifndef FLB_MACROS_H
#define FLB_MACROS_H

#include <stdbool.h>
#include <monkey/mk_core.h>

#define FLB_FALSE 0
#define FLB_TRUE !FLB_FALSE
#define FLB_FALSE false
#define FLB_TRUE true

/* Return values */
#define FLB_ERROR 0
Expand Down
11 changes: 11 additions & 0 deletions include/fluent-bit/flb_metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@
#include <cmetrics/cmt_decode_statsd.h>
#include <cmetrics/cmt_filter.h>

/*
* v1 HTTP endpoint metrics
* ------------------------
* These are functions that are not part of the CMetrics library, its' the old interface
* to ship internal metrics which is part of the v1 HTTP endpoint.
*/

/* Metrics IDs for general purpose (used by core and Plugins */
#define FLB_METRIC_N_RECORDS 0
#define FLB_METRIC_N_BYTES 1
Expand Down Expand Up @@ -85,6 +92,10 @@ int flb_metrics_dump_values(char **out_buf, size_t *out_size,
struct flb_metrics *me);
int flb_metrics_destroy(struct flb_metrics *metrics);
int flb_metrics_fluentbit_add(struct flb_config *ctx, struct cmt *cmt);
/* ! end of v1 HTTP endpoint metrics */

/* General metrics utilities */
bool flb_metrics_is_empty(struct cmt *cmt);

#endif
#endif /* FLB_HAVE_METRICS */
80 changes: 54 additions & 26 deletions include/fluent-bit/flb_output.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <fluent-bit/flb_event.h>
#include <fluent-bit/flb_processor.h>

#include <cfl/cfl.h>
#include <cmetrics/cmetrics.h>
#include <cmetrics/cmt_gauge.h>
#include <cmetrics/cmt_counter.h>
Expand Down Expand Up @@ -669,6 +670,13 @@ static FLB_INLINE void output_pre_cb_flush(void)
flb_debug("[output] skipping flush for event chunk with zero records.");
FLB_OUTPUT_RETURN(FLB_OK);
}
/* Skip flush if processed event chunk has no data (empty after processing) */
else if (persisted_params.event_chunk &&
persisted_params.event_chunk->type == FLB_EVENT_TYPE_METRICS &&
persisted_params.event_chunk->size == 0) {
flb_debug("[output] skipping flush for event chunk with no data after processing.");
FLB_OUTPUT_RETURN(FLB_OK);
}

/* Continue, we will resume later */
out_p = persisted_params.out_plugin;
Expand Down Expand Up @@ -708,7 +716,7 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
{
int ret;
size_t records;
void *p_buf;
void *p_buf = NULL;
size_t p_size;
size_t stack_size;
struct flb_coro *coro;
Expand All @@ -725,8 +733,10 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
struct ctrace *trace_context;
struct cprof *profile_context;
size_t chunk_offset;
struct cmt *encode_context = NULL;
struct cmt *cmt_out_context = NULL;


/* Custom output coroutine info */
out_flush = (struct flb_output_flush *) flb_calloc(1, sizeof(struct flb_output_flush));
if (!out_flush) {
Expand Down Expand Up @@ -756,6 +766,7 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,

/* Logs processor */
evc = task->event_chunk;

if (flb_processor_is_active(o_ins->processor)) {
if (evc->type == FLB_EVENT_TYPE_LOGS) {
/* run the processor */
Expand Down Expand Up @@ -786,10 +797,8 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,

if (p_buf == NULL) {
flb_errno();

flb_coro_destroy(coro);
flb_free(out_flush);

return NULL;
}

Expand All @@ -803,6 +812,7 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
(char *) evc->data,
evc->size,
&chunk_offset)) == CMT_DECODE_MSGPACK_SUCCESS) {

ret = flb_processor_run(o_ins->processor,
0,
FLB_PROCESSOR_METRICS,
Expand All @@ -814,6 +824,22 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
NULL);

if (ret == 0) {
if (cmt_out_context) {
encode_context = cmt_out_context;
}
else {
encode_context = metrics_context;
}

/* if the cmetrics context lack of time series just skip it */
if (flb_metrics_is_empty(encode_context)) {
if (encode_context != metrics_context) {
cmt_destroy(encode_context);
}
cmt_destroy(metrics_context);
continue;
}

if (cmt_out_context != NULL) {
ret = cmt_encode_msgpack_create(cmt_out_context,
&serialized_context_buffer,
Expand All @@ -822,7 +848,6 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
if (cmt_out_context != metrics_context) {
cmt_destroy(cmt_out_context);
}

}
else {
ret = cmt_encode_msgpack_create(metrics_context,
Expand All @@ -836,23 +861,17 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
flb_coro_destroy(coro);
flb_free(out_flush);
flb_free(p_buf);

return NULL;
}

if ((serialization_buffer_offset +
serialized_context_size) > p_size) {
resized_serialization_buffer = \
flb_realloc(p_buf, p_size + serialized_context_size);

if ((serialization_buffer_offset + serialized_context_size) > p_size) {
resized_serialization_buffer = flb_realloc(p_buf, p_size + serialized_context_size);
if (resized_serialization_buffer == NULL) {
flb_errno();

cmt_encode_msgpack_destroy(serialized_context_buffer);
flb_coro_destroy(coro);
flb_free(out_flush);
flb_free(p_buf);

return NULL;
}

Expand All @@ -871,26 +890,35 @@ struct flb_output_flush *flb_output_flush_create(struct flb_task *task,
}

if (serialization_buffer_offset == 0) {
flb_coro_destroy(coro);
flb_free(out_flush);
flb_debug("[output] skipping flush for metrics event chunk with zero metrics after processing.");
flb_free(p_buf);

return NULL;
p_buf = NULL; /* Mark as freed to avoid double-free */

/* Create an empty processed event chunk to signal success */
out_flush->processed_event_chunk = flb_event_chunk_create(
evc->type,
0,
evc->tag,
flb_sds_len(evc->tag),
NULL,
0);
}
else {
out_flush->processed_event_chunk = flb_event_chunk_create(
evc->type,
0,
evc->tag,
flb_sds_len(evc->tag),
p_buf,
p_size);
}

out_flush->processed_event_chunk = flb_event_chunk_create(
evc->type,
0,
evc->tag,
flb_sds_len(evc->tag),
p_buf,
p_size);

if (out_flush->processed_event_chunk == NULL) {
flb_coro_destroy(coro);
flb_free(out_flush);
flb_free(p_buf);

if (p_buf != NULL) {
flb_free(p_buf);
}
return NULL;
}
}
Expand Down
36 changes: 20 additions & 16 deletions src/flb_input_metric.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <fluent-bit/flb_input_chunk.h>
#include <fluent-bit/flb_input_metric.h>
#include <fluent-bit/flb_input_plugin.h>
#include <cfl/cfl.h>

static int input_metrics_append(struct flb_input_instance *ins,
size_t processor_starting_stage,
Expand All @@ -33,6 +34,7 @@ static int input_metrics_append(struct flb_input_instance *ins,
size_t mt_size;
int processor_is_active;
struct cmt *out_context = NULL;
struct cmt *encode_context;

processor_is_active = flb_processor_is_active(ins->processor);
if (processor_is_active) {
Expand Down Expand Up @@ -60,29 +62,31 @@ static int input_metrics_append(struct flb_input_instance *ins,
}
}

if (out_context) {
encode_context = out_context;
}
else {
encode_context = cmt;
}

if (out_context != NULL) {
/* Convert metrics to msgpack */
ret = cmt_encode_msgpack_create(out_context, &mt_buf, &mt_size);

if (out_context != cmt) {
/* Drop the context if it contains no metrics */
if (encode_context == NULL || flb_metrics_is_empty(encode_context)) {
if (out_context && out_context != cmt) {
cmt_destroy(out_context);
}
return 0;
}

if (ret != 0) {
flb_plg_error(ins, "could not encode metrics");
/* Convert metrics to msgpack */
ret = cmt_encode_msgpack_create(encode_context, &mt_buf, &mt_size);

return -1;
}
if (out_context && out_context != cmt) {
cmt_destroy(out_context);
}
else {
/* Convert metrics to msgpack */
ret = cmt_encode_msgpack_create(cmt, &mt_buf, &mt_size);
if (ret != 0) {
flb_plg_error(ins, "could not encode metrics");
return -1;

}
if (ret != 0) {
flb_plg_error(ins, "could not encode metrics");
return -1;
}

/* Append packed metrics */
Expand Down
9 changes: 9 additions & 0 deletions src/flb_metrics.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,12 @@ int flb_metrics_fluentbit_add(struct flb_config *ctx, struct cmt *cmt)

return 0;
}

bool flb_metrics_is_empty(struct cmt *cmt)
{
return cfl_list_is_empty(&cmt->counters) &&
cfl_list_is_empty(&cmt->gauges) &&
cfl_list_is_empty(&cmt->histograms) &&
cfl_list_is_empty(&cmt->summaries) &&
cfl_list_is_empty(&cmt->untypeds);
}
Comment on lines +380 to +387
Copy link

@coderabbitai coderabbitai bot Aug 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against NULL input in flb_metrics_is_empty()

If a caller ever passes a NULL struct cmt *, the function will seg-fault.
A defensive early-return costs nothing and prevents a crash path that’s very hard to diagnose at runtime.

 bool flb_metrics_is_empty(struct cmt *cmt)
 {
-    return cfl_list_is_empty(&cmt->counters) &&
+    if (cmt == NULL) {
+        return true;
+    }
+
+    return cfl_list_is_empty(&cmt->counters) &&
            ...
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool flb_metrics_is_empty(struct cmt *cmt)
{
return cfl_list_is_empty(&cmt->counters) &&
cfl_list_is_empty(&cmt->gauges) &&
cfl_list_is_empty(&cmt->histograms) &&
cfl_list_is_empty(&cmt->summaries) &&
cfl_list_is_empty(&cmt->untypeds);
}
bool flb_metrics_is_empty(struct cmt *cmt)
{
if (cmt == NULL) {
return true;
}
return cfl_list_is_empty(&cmt->counters) &&
cfl_list_is_empty(&cmt->gauges) &&
cfl_list_is_empty(&cmt->histograms) &&
cfl_list_is_empty(&cmt->summaries) &&
cfl_list_is_empty(&cmt->untypeds);
}
🤖 Prompt for AI Agents
In src/flb_metrics.c around lines 380 to 387, the function flb_metrics_is_empty
does not check if the input pointer cmt is NULL, which can cause a segmentation
fault. Add a defensive check at the start of the function to return true (or an
appropriate value) immediately if cmt is NULL to prevent crashes.

Copy link
Member Author

Choose a reason for hiding this comment

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

use FLB_TRUE instead true

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current definition of FLB_TRUE and FLB_FALSE
cat include/fluent-bit/flb_macros.h | grep -A2 -B2 "FLB_TRUE\|FLB_FALSE"

Length of output: 173


🏁 Script executed:

#!/bin/bash
# Check how other functions handle NULL checks with boolean returns
rg -A3 "if \(.*== NULL\)" src/flb_metrics.c | head -20

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Look for other bool-returning functions that use FLB_TRUE
rg "bool.*\(" include/fluent-bit/ -A5 | grep -B3 "FLB_TRUE\|return true"

Length of output: 74


@edsiper, you're right! For consistency with the rest of the codebase, here's the updated suggestion using FLB_TRUE:

bool flb_metrics_is_empty(struct cmt *cmt)
{
+    if (cmt == NULL) {
+        return FLB_TRUE;
+    }
+
    return cfl_list_is_empty(&cmt->counters) &&
           cfl_list_is_empty(&cmt->gauges) &&
           cfl_list_is_empty(&cmt->histograms) &&
           cfl_list_is_empty(&cmt->summaries) &&
           cfl_list_is_empty(&cmt->untypeds);
}

This maintains consistency with the project's macro usage pattern, even though FLB_TRUE is now defined as the boolean literal true.


🧬 Code Graph Analysis Results

Relevant snippets from src/flb_metrics.c (lines 22-35)

static int id_exists(int id, struct flb_metrics *metrics)
{
    struct mk_list *head;
    struct flb_metric *metric;

    mk_list_foreach(head, &metrics->list) {
        metric = mk_list_entry(head, struct flb_metric, _head);
        if (metric->id == id) {
            return FLB_TRUE;
        }
    }

    return FLB_FALSE;
}
  • This function checks if a metric ID already exists in the metrics list.
  • It returns FLB_TRUE if the ID exists, otherwise FLB_FALSE.
  • The user comment about using FLB_TRUE instead of true is directly relevant here since this function returns FLB_TRUE and FLB_FALSE macros instead of standard C true/false.

Relevant snippets from src/flb_metrics.c (lines 37-51)

static int id_get(struct flb_metrics *metrics)
{
    int id;
    int ret = FLB_FALSE;

    /* Try to use 'count' as an id */
    id = metrics->count;

    while ((ret = id_exists(id, metrics)) == FLB_TRUE) {
        id++;
    }

    return id;
}
  • This function generates a new unique metric ID.
  • It uses FLB_TRUE and FLB_FALSE macros for boolean logic.
  • This is another place where the user comment about using FLB_TRUE instead of true is relevant.

Relevant snippet from include/fluent-bit/flb_mem.h (lines 84-96)

void *flb_calloc(size_t n, const size_t size) {
    if (size == 0) {
        return NULL;
    }
#ifdef FLB_HAVE_TESTS_OSSFUZZ
   // Add chance of failure. Used by fuzzing to test error-handling code.
   if (flb_fuzz_get_probability(1)) {
     return NULL;
   }
#endif

    return calloc(n, size);
}
  • This snippet shows the flb_calloc function used in flb_metrics_create for memory allocation.
  • It is relevant for understanding memory allocation and error handling in the metrics code.

Relevant snippet from src/flb_sds.c (lines 389-399)

void flb_sds_destroy(flb_sds_t s)
{
    struct flb_sds *head;

    if (!s) {
        return;
    }

    head = FLB_SDS_HEADER(s);
    flb_free(head);
}
  • This snippet shows how flb_sds_destroy frees string data structures used for metric titles.
  • Relevant for understanding resource cleanup in flb_metrics_destroy.

Relevant snippet from src/flb_sds.c (lines 58-76)

flb_sds_t flb_sds_create_len(const char *str, int len)
{
    flb_sds_t s;
    struct flb_sds *head;

    s = sds_alloc(len);
    if (!s) {
        return NULL;
    }

    if (str) {
        memcpy(s, str, len);
        s[len] = '\0';

        head = FLB_SDS_HEADER(s);
        head->len = len;
    }
    return s;
}
  • This snippet shows how metric titles are created with a length limit.
  • Relevant for understanding how titles are handled in flb_metrics_add and flb_metrics_title.

Relevant snippet from lib/cmetrics/src/cmt_counter.c (lines 26-81)

struct cmt_counter *cmt_counter_create(struct cmt *cmt,
                                       char *ns, char *subsystem,
                                       char *name, char *help,
                                       int label_count, char **label_keys)
{
    int ret;
    struct cmt_counter *counter;

    if (!ns) {
        cmt_log_error(cmt, "null ns not allowed");
        return NULL;
    }

    if (!subsystem) {
        cmt_log_error(cmt, "null subsystem not allowed");
        return NULL;
    }

    if (!name || strlen(name) == 0) {
        cmt_log_error(cmt, "undefined name");
        return NULL;
    }

    if (!help || strlen(help) == 0) {
        cmt_log_error(cmt, "undefined help");
        return NULL;
    }

    counter = calloc(1, sizeof(struct cmt_counter));
    if (!counter) {
        cmt_errno();
        return NULL;
    }
    cfl_list_add(&counter->_head, &cmt->counters);

    ret = cmt_opts_init(&counter->opts, ns, subsystem, name, help);
    if (ret == -1) {
        cmt_log_error(cmt, "unable to initialize options for counter");
        cmt_counter_destroy(counter);
        return NULL;
    }

    /* Create the map */
    counter->map = cmt_map_create(CMT_COUNTER, &counter->opts, label_count, label_keys,
                                  (void *) counter);
    if (!counter->map) {
        cmt_log_error(cmt, "unable to allocate map for counter");
        cmt_counter_destroy(counter);
        return NULL;
    }
    /* set default counter aggregation type to cumulative */
    counter->aggregation_type = CMT_AGGREGATION_TYPE_CUMULATIVE;

    counter->cmt = cmt;
    return counter;
}
  • This snippet shows how counters are created in the cmetrics library, which is used in flb_metrics.c for attaching metrics.
  • Relevant for understanding the creation of counters in functions like attach_uptime.

Relevant snippet from lib/cmetrics/src/cmt_gauge.c (lines 27-81)

struct cmt_gauge *cmt_gauge_create(struct cmt *cmt,
                                   char *ns, char *subsystem, char *name,
                                   char *help, int label_count, char **label_keys)
{
    int ret;
    struct cmt_gauge *gauge;

    if (!ns) {
        cmt_log_error(cmt, "null ns not allowed");
        return NULL;
    }

    if (!subsystem) {
        cmt_log_error(cmt, "null subsystem not allowed");
        return NULL;
    }

    if (!name || strlen(name) == 0) {
        cmt_log_error(cmt, "undefined name");
        return NULL;
    }

    if (!help || strlen(help) == 0) {
        cmt_log_error(cmt, "undefined help");
        return NULL;
    }

    gauge = calloc(1, sizeof(struct cmt_gauge));
    if (!gauge) {
        cmt_errno();
        return NULL;
    }
    cfl_list_add(&gauge->_head, &cmt->gauges);

    /* Initialize options */
    ret = cmt_opts_init(&gauge->opts, ns, subsystem, name, help);
    if (ret == -1) {
        cmt_log_error(cmt, "unable to initialize options for gauge");
        cmt_gauge_destroy(gauge);
        return NULL;
    }

    /* Create the map */
    gauge->map = cmt_map_create(CMT_GAUGE, &gauge->opts, label_count, label_keys,
                                (void *) gauge);
    if (!gauge->map) {
        cmt_log_error(cmt, "unable to allocate map for gauge");
        cmt_gauge_destroy(gauge);
        return NULL;
    }

    gauge->cmt = cmt;

    return gauge;
}
  • This snippet shows how gauges are created in the cmetrics library, used in flb_metrics.c for attaching metrics like process start time and build info.
  • Relevant for understanding the metric attachment functions.

Relevant snippet from lib/cmetrics/src/cmt_gauge.c (lines 94-109)

int cmt_gauge_set(struct cmt_gauge *gauge, uint64_t timestamp, double val,
                  int labels_count, char **label_vals)
{
    struct cmt_metric *metric;

    metric = cmt_map_metric_get(&gauge->opts, gauge->map, labels_count, label_vals,
                                CMT_TRUE);
    if (!metric) {
        cmt_log_error(gauge->cmt, "unable to retrieve metric for gauge %s_%s_%s",
                      gauge->opts.ns, gauge->opts.subsystem,
                      gauge->opts.name);
        return -1;
    }
    cmt_metric_set(metric, timestamp, val);
    return 0;
}
  • This snippet shows how gauge values are set, using CMT_TRUE macro for boolean.
  • Relevant for understanding the boolean macro usage in metric setting functions.

[End of relevant snippets.]

Loading