Skip to content

Commit db182ed

Browse files
committedMar 9, 2025··
logs_to_metrics: Support optional value_field for counters
Support incrementing counters by a specified value. If a `value_field` is specified for a counter, the counter is incremented by the given value instead of just counting the number of records. This allows tracking different metrics, such as the total number of bytes sent or received in an access log. Signed-off-by: Fabian Ruff <[email protected]>
1 parent 9de4d3a commit db182ed

File tree

2 files changed

+123
-9
lines changed

2 files changed

+123
-9
lines changed
 

‎plugins/filter_log_to_metrics/log_to_metrics.c

+53-9
Original file line numberDiff line numberDiff line change
@@ -639,13 +639,14 @@ static int cb_log_to_metrics_init(struct flb_filter_instance *f_ins,
639639
snprintf(metric_description, sizeof(metric_description) - 1, "%s",
640640
ctx->metric_description);
641641

642-
/* Value field only needed for modes gauge and histogram */
643-
if (ctx->mode > 0) {
644-
if (ctx->value_field == NULL || strlen(ctx->value_field) == 0) {
645-
flb_plg_error(f_ins, "value_field is not set");
646-
log_to_metrics_destroy(ctx);
647-
return -1;
642+
if (ctx->value_field == NULL || strlen(ctx->value_field) == 0) {
643+
/* require value field for modes gauge and histogram */
644+
if (ctx->mode > 0) {
645+
flb_plg_error(f_ins, "value_field is not set");
646+
log_to_metrics_destroy(ctx);
647+
return -1;
648648
}
649+
} else {
649650
snprintf(value_field, sizeof(value_field) - 1, "%s",
650651
ctx->value_field);
651652
}
@@ -825,6 +826,7 @@ static int cb_log_to_metrics_filter(const void *data, size_t bytes,
825826
char **label_values = NULL;
826827
int label_count = 0;
827828
int i;
829+
double counter_value = 0;
828830
double gauge_value = 0;
829831
double histogram_value = 0;
830832
char kubernetes_label_values
@@ -901,8 +903,50 @@ static int cb_log_to_metrics_filter(const void *data, size_t bytes,
901903
/* Calculating and setting metric depending on the mode */
902904
switch (ctx->mode) {
903905
case FLB_LOG_TO_METRICS_COUNTER:
904-
ret = cmt_counter_inc(ctx->c, ts, label_count,
905-
label_values);
906+
907+
// If value_field is not set, increment counter by 1
908+
if (ctx->value_field == NULL || strlen(ctx->value_field) == 0) {
909+
ret = cmt_counter_inc(ctx->c, ts, label_count,
910+
label_values);
911+
break;
912+
}
913+
// If value_field is set, increment counter by value
914+
ra = flb_ra_create(ctx->value_field, FLB_TRUE);
915+
if (!ra) {
916+
flb_plg_error(ctx->ins, "invalid record accessor key, aborting");
917+
break;
918+
}
919+
920+
rval = flb_ra_get_value_object(ra, map);
921+
922+
if (!rval) {
923+
flb_warn("given value field is empty or not existent");
924+
break;
925+
}
926+
if (rval->type == FLB_RA_STRING) {
927+
sscanf(rval->val.string, "%lf", &counter_value);
928+
}
929+
else if (rval->type == FLB_RA_FLOAT) {
930+
counter_value = rval->val.f64;
931+
}
932+
else if (rval->type == FLB_RA_INT) {
933+
counter_value = (double)rval->val.i64;
934+
}
935+
else {
936+
flb_plg_error(f_ins,
937+
"cannot convert given value to metric");
938+
break;
939+
}
940+
ret = cmt_counter_add(ctx->c, ts, counter_value,
941+
label_count, label_values);
942+
if (rval) {
943+
flb_ra_key_value_destroy(rval);
944+
rval = NULL;
945+
}
946+
if (ra) {
947+
flb_ra_destroy(ra);
948+
ra = NULL;
949+
}
906950
break;
907951

908952
case FLB_LOG_TO_METRICS_GAUGE:
@@ -1066,7 +1110,7 @@ static struct flb_config_map config_map[] = {
10661110
{
10671111
FLB_CONFIG_MAP_STR, "value_field", NULL,
10681112
0, FLB_TRUE, offsetof(struct log_to_metrics_ctx, value_field),
1069-
"Numeric field to use for gauge or histogram"
1113+
"Numeric field to use for gauge, histogram or counter"
10701114
},
10711115
{
10721116
FLB_CONFIG_MAP_STR, "metric_name", "a",

‎tests/runtime/filter_log_to_metrics.c

+70
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
/* Test functions */
5858
void flb_test_log_to_metrics_counter_k8s(void);
5959
void flb_test_log_to_metrics_counter(void);
60+
void flb_test_log_to_metrics_counter_value_field(void);
6061
void flb_test_log_to_metrics_counter_k8s_two_tuples(void);
6162
void flb_test_log_to_metrics_gauge(void);
6263
void flb_test_log_to_metrics_histogram(void);
@@ -118,6 +119,7 @@ void flb_test_log_to_metrics_label(void);
118119
TEST_LIST = {
119120
{"counter_k8s", flb_test_log_to_metrics_counter_k8s },
120121
{"counter", flb_test_log_to_metrics_counter },
122+
{"counter_value_field", flb_test_log_to_metrics_counter_value_field },
121123
{"counter_k8s_two_tuples", flb_test_log_to_metrics_counter_k8s_two_tuples },
122124
{"gauge", flb_test_log_to_metrics_gauge },
123125
{"histogram", flb_test_log_to_metrics_histogram },
@@ -321,6 +323,74 @@ void flb_test_log_to_metrics_counter(void)
321323

322324
}
323325

326+
void flb_test_log_to_metrics_counter_value_field(void)
327+
{
328+
int ret;
329+
int i;
330+
flb_ctx_t *ctx;
331+
int in_ffd;
332+
int filter_ffd;
333+
int out_ffd;
334+
char *result = NULL;
335+
struct flb_lib_out_cb cb_data;
336+
char *input = JSON_MSG1;
337+
char finalString[32768] = "";
338+
const char *expected = "\"value\":100.0,\"labels\":[\"red\",\"right\"]";
339+
const char *expected2 = "{\"ns\":\"myns\",\"ss\":\"subsystem\","
340+
"\"name\":\"test\",\"desc\":\"Counts durations\"}";
341+
342+
ctx = flb_create();
343+
flb_service_set(ctx, "Flush", "0.200000000", "Grace", "1", "Log_Level",
344+
"error", NULL);
345+
346+
cb_data.cb = callback_test;
347+
cb_data.data = NULL;
348+
349+
in_ffd = flb_input(ctx, (char *) "lib", NULL);
350+
TEST_CHECK(in_ffd >= 0);
351+
flb_input_set(ctx, in_ffd, "tag", "test", NULL);
352+
353+
filter_ffd = flb_filter(ctx, (char *) "log_to_metrics", NULL);
354+
TEST_CHECK(filter_ffd >= 0);
355+
ret = flb_filter_set(ctx, filter_ffd,
356+
"Match", "*",
357+
"Tag", "test_metric",
358+
"metric_mode", "counter",
359+
"metric_name", "test",
360+
"metric_description", "Counts durations",
361+
"metric_subsystem", "subsystem",
362+
"metric_namespace", "myns",
363+
"kubernetes_mode", "off",
364+
"label_field", "color",
365+
"label_field", "direction",
366+
"value_field", "duration",
367+
NULL);
368+
369+
out_ffd = flb_output(ctx, (char *) "lib", (void *)&cb_data);
370+
TEST_CHECK(out_ffd >= 0);
371+
flb_output_set(ctx, out_ffd,
372+
"match", "*",
373+
"format", "json",
374+
NULL);
375+
ret = flb_start(ctx);
376+
TEST_CHECK(ret == 0);
377+
378+
for (i = 0; i < 5; i++){
379+
flb_lib_push(ctx, in_ffd, input, strlen(input));
380+
}
381+
wait_with_timeout(2000, finalString);
382+
result = strstr(finalString, expected);
383+
if (!TEST_CHECK(result != NULL)) {
384+
TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
385+
}
386+
result = strstr(finalString, expected2);
387+
if (!TEST_CHECK(result != NULL)) {
388+
TEST_MSG("expected substring:\n%s\ngot:\n%s\n", expected, finalString);
389+
}
390+
filter_test_destroy(ctx);
391+
392+
}
393+
324394
void flb_test_log_to_metrics_counter_k8s_two_tuples(void)
325395
{
326396
int ret;

0 commit comments

Comments
 (0)