Skip to content

Commit 5c28512

Browse files
committed
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 65722e8 commit 5c28512

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

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -643,13 +643,14 @@ static int cb_log_to_metrics_init(struct flb_filter_instance *f_ins,
643643
snprintf(metric_description, sizeof(metric_description) - 1, "%s",
644644
ctx->metric_description);
645645

646-
/* Value field only needed for modes gauge and histogram */
647-
if (ctx->mode > 0) {
648-
if (ctx->value_field == NULL || strlen(ctx->value_field) == 0) {
649-
flb_plg_error(f_ins, "value_field is not set");
650-
log_to_metrics_destroy(ctx);
651-
return -1;
646+
if (ctx->value_field == NULL || strlen(ctx->value_field) == 0) {
647+
/* require value field for modes gauge and histogram */
648+
if (ctx->mode > 0) {
649+
flb_plg_error(f_ins, "value_field is not set");
650+
log_to_metrics_destroy(ctx);
651+
return -1;
652652
}
653+
} else {
653654
snprintf(value_field, sizeof(value_field) - 1, "%s",
654655
ctx->value_field);
655656

@@ -836,6 +837,7 @@ static int cb_log_to_metrics_filter(const void *data, size_t bytes,
836837
char **label_values = NULL;
837838
int label_count = 0;
838839
int i;
840+
double counter_value = 0;
839841
double gauge_value = 0;
840842
double histogram_value = 0;
841843
char kubernetes_label_values
@@ -912,8 +914,50 @@ static int cb_log_to_metrics_filter(const void *data, size_t bytes,
912914
/* Calculating and setting metric depending on the mode */
913915
switch (ctx->mode) {
914916
case FLB_LOG_TO_METRICS_COUNTER:
915-
ret = cmt_counter_inc(ctx->c, ts, label_count,
916-
label_values);
917+
918+
// If value_field is not set, increment counter by 1
919+
if (ctx->value_field == NULL || strlen(ctx->value_field) == 0) {
920+
ret = cmt_counter_inc(ctx->c, ts, label_count,
921+
label_values);
922+
break;
923+
}
924+
// If value_field is set, increment counter by value
925+
ra = flb_ra_create(ctx->value_field, FLB_TRUE);
926+
if (!ra) {
927+
flb_plg_error(ctx->ins, "invalid record accessor key, aborting");
928+
break;
929+
}
930+
931+
rval = flb_ra_get_value_object(ra, map);
932+
933+
if (!rval) {
934+
flb_warn("given value field is empty or not existent");
935+
break;
936+
}
937+
if (rval->type == FLB_RA_STRING) {
938+
sscanf(rval->val.string, "%lf", &counter_value);
939+
}
940+
else if (rval->type == FLB_RA_FLOAT) {
941+
counter_value = rval->val.f64;
942+
}
943+
else if (rval->type == FLB_RA_INT) {
944+
counter_value = (double)rval->val.i64;
945+
}
946+
else {
947+
flb_plg_error(f_ins,
948+
"cannot convert given value to metric");
949+
break;
950+
}
951+
ret = cmt_counter_add(ctx->c, ts, counter_value,
952+
label_count, label_values);
953+
if (rval) {
954+
flb_ra_key_value_destroy(rval);
955+
rval = NULL;
956+
}
957+
if (ra) {
958+
flb_ra_destroy(ra);
959+
ra = NULL;
960+
}
917961
break;
918962

919963
case FLB_LOG_TO_METRICS_GAUGE:
@@ -1057,7 +1101,7 @@ static struct flb_config_map config_map[] = {
10571101
{
10581102
FLB_CONFIG_MAP_STR, "value_field", NULL,
10591103
0, FLB_TRUE, offsetof(struct log_to_metrics_ctx, value_field),
1060-
"Numeric field to use for gauge or histogram"
1104+
"Numeric field to use for gauge, histogram or counter"
10611105
},
10621106
{
10631107
FLB_CONFIG_MAP_STR, "metric_name", "a",

tests/runtime/filter_log_to_metrics.c

Lines changed: 70 additions & 0 deletions
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)