Skip to content

Commit 1009138

Browse files
iandrewtedsiper
authored andcommitted
out_loki: allow sending unquoted strings
This patch adds a third value to `drop_single_key` - `raw`, which allows sending unquoted strings to Loki when using JSON as the `line_format`. While yes, for the output to be valid JSON, quotes would be expected, Loki does not support reading a plain quoted string with its JSON parser, complaining that it cannot find a `}` character. Instead, you need to use a combination of regexp and line_format expressions to unquote the log before running any other parsers over it. By adding a third value of `raw`, this ensures backwards compatibility for anyone that is already relying on the existing behaviour. Signed-off-by: Andrew Titmuss <[email protected]>
1 parent b20df67 commit 1009138

File tree

3 files changed

+242
-15
lines changed

3 files changed

+242
-15
lines changed

plugins/out_loki/loki.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -903,10 +903,12 @@ static struct flb_loki *loki_config_create(struct flb_output_instance *ins,
903903
struct flb_config *config)
904904
{
905905
int ret;
906+
int tmp;
906907
int io_flags = 0;
907908
struct flb_loki *ctx;
908909
struct flb_upstream *upstream;
909910
char *compress;
911+
char *drop_single_key;
910912

911913
/* Create context */
912914
ctx = flb_calloc(1, sizeof(struct flb_loki));
@@ -962,6 +964,29 @@ static struct flb_loki *loki_config_create(struct flb_output_instance *ins,
962964
}
963965
}
964966

967+
/* Drop Single Key */
968+
drop_single_key = flb_output_get_property("drop_single_key", ins);
969+
ctx->out_drop_single_key = FLB_LOKI_DROP_SINGLE_KEY_OFF;
970+
if (drop_single_key) {
971+
if (strcasecmp(drop_single_key, "raw") == 0) {
972+
ctx->out_drop_single_key = FLB_LOKI_DROP_SINGLE_KEY_ON | FLB_LOKI_DROP_SINGLE_KEY_RAW;
973+
}
974+
else {
975+
tmp = flb_utils_bool(drop_single_key);
976+
if (tmp == FLB_TRUE) {
977+
ctx->out_drop_single_key = FLB_LOKI_DROP_SINGLE_KEY_ON;
978+
}
979+
else if (tmp == FLB_FALSE) {
980+
ctx->out_drop_single_key = FLB_LOKI_DROP_SINGLE_KEY_OFF;
981+
}
982+
else {
983+
flb_plg_error(ctx->ins, "invalid 'drop_single_key' value: %s",
984+
ctx->drop_single_key);
985+
return NULL;
986+
}
987+
}
988+
}
989+
965990
/* Line Format */
966991
if (strcasecmp(ctx->line_format, "json") == 0) {
967992
ctx->out_line_format = FLB_LOKI_FMT_JSON;
@@ -1206,12 +1231,28 @@ static int pack_record(struct flb_loki *ctx,
12061231
}
12071232

12081233
/* Drop single key */
1209-
if (ctx->drop_single_key == FLB_TRUE && rec->type == MSGPACK_OBJECT_MAP && rec->via.map.size == 1) {
1234+
if (ctx->out_drop_single_key & FLB_LOKI_DROP_SINGLE_KEY_ON &&
1235+
rec->type == MSGPACK_OBJECT_MAP && rec->via.map.size == 1) {
1236+
val = rec->via.map.ptr[0].val;
1237+
12101238
if (ctx->out_line_format == FLB_LOKI_FMT_JSON) {
1211-
rec = &rec->via.map.ptr[0].val;
1212-
} else if (ctx->out_line_format == FLB_LOKI_FMT_KV) {
1213-
val = rec->via.map.ptr[0].val;
1239+
if (val.type == MSGPACK_OBJECT_STR &&
1240+
ctx->out_drop_single_key & FLB_LOKI_DROP_SINGLE_KEY_RAW) {
1241+
msgpack_pack_str(mp_pck, val.via.str.size);
1242+
msgpack_pack_str_body(mp_pck, val.via.str.ptr, val.via.str.size);
1243+
1244+
msgpack_unpacked_destroy(&mp_buffer);
1245+
if (tmp_sbuf_data) {
1246+
flb_free(tmp_sbuf_data);
1247+
}
12141248

1249+
return 0;
1250+
}
1251+
else {
1252+
rec = &val;
1253+
}
1254+
}
1255+
else if (ctx->out_line_format == FLB_LOKI_FMT_KV) {
12151256
if (val.type == MSGPACK_OBJECT_STR) {
12161257
msgpack_pack_str(mp_pck, val.via.str.size);
12171258
msgpack_pack_str_body(mp_pck, val.via.str.ptr, val.via.str.size);
@@ -1774,10 +1815,11 @@ static struct flb_config_map config_map[] = {
17741815
},
17751816

17761817
{
1777-
FLB_CONFIG_MAP_BOOL, "drop_single_key", "false",
1818+
FLB_CONFIG_MAP_STR, "drop_single_key", NULL,
17781819
0, FLB_TRUE, offsetof(struct flb_loki, drop_single_key),
17791820
"If set to true and only a single key remains, the log line sent to Loki "
1780-
"will be the value of that key.",
1821+
"will be the value of that key. If set to 'raw' and the log line is "
1822+
"a string, the log line will be sent unquoted.",
17811823
},
17821824

17831825
{

plugins/out_loki/loki.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
#define FLB_LOKI_FMT_JSON 0
4242
#define FLB_LOKI_FMT_KV 1
4343

44+
/* Drop single key */
45+
#define FLB_LOKI_DROP_SINGLE_KEY_OFF (((uint64_t) 1) << 0)
46+
#define FLB_LOKI_DROP_SINGLE_KEY_ON (((uint64_t) 1) << 1)
47+
#define FLB_LOKI_DROP_SINGLE_KEY_RAW (((uint64_t) 1) << 2)
48+
4449
struct flb_loki_kv {
4550
int val_type; /* FLB_LOKI_KV_STR or FLB_LOKI_KV_RA */
4651
flb_sds_t key; /* string key */
@@ -54,8 +59,8 @@ struct flb_loki_kv {
5459
struct flb_loki {
5560
/* Public configuration properties */
5661
int auto_kubernetes_labels;
57-
int drop_single_key;
5862

63+
flb_sds_t drop_single_key;
5964
flb_sds_t uri;
6065
flb_sds_t line_format;
6166
flb_sds_t tenant_id;
@@ -80,6 +85,7 @@ struct flb_loki {
8085
int tcp_port;
8186
char *tcp_host;
8287
int out_line_format;
88+
int out_drop_single_key;
8389
int ra_used; /* number of record accessor label keys */
8490
struct flb_record_accessor *ra_k8s; /* kubernetes record accessor */
8591
struct mk_list labels_list; /* list of flb_loki_kv nodes */

tests/runtime/out_loki.c

Lines changed: 187 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,182 @@ void flb_test_line_format()
285285
flb_destroy(ctx);
286286
}
287287

288+
static void cb_check_drop_single_key_off(void *ctx, int ffd,
289+
int res_ret, void *res_data, size_t res_size,
290+
void *data)
291+
{
292+
char *p;
293+
flb_sds_t out_js = res_data;
294+
char *index_line = "{\\\"key\\\":\\\"value\\\"}";
295+
296+
p = strstr(out_js, index_line);
297+
if (!TEST_CHECK(p != NULL)) {
298+
TEST_MSG("Given:%s", out_js);
299+
}
300+
301+
flb_sds_destroy(out_js);
302+
}
303+
304+
void flb_test_drop_single_key_off()
305+
{
306+
int ret;
307+
int size = sizeof(JSON_BASIC) - 1;
308+
flb_ctx_t *ctx;
309+
int in_ffd;
310+
int out_ffd;
311+
312+
/* Create context, flush every second (some checks omitted here) */
313+
ctx = flb_create();
314+
flb_service_set(ctx, "flush", "1", "grace", "1",
315+
"log_level", "error",
316+
NULL);
317+
318+
/* Lib input mode */
319+
in_ffd = flb_input(ctx, (char *) "lib", NULL);
320+
flb_input_set(ctx, in_ffd, "tag", "test", NULL);
321+
322+
/* Loki output */
323+
out_ffd = flb_output(ctx, (char *) "loki", NULL);
324+
flb_output_set(ctx, out_ffd,
325+
"match", "test",
326+
"drop_single_key", "off",
327+
NULL);
328+
329+
/* Enable test mode */
330+
ret = flb_output_set_test(ctx, out_ffd, "formatter",
331+
cb_check_drop_single_key_off,
332+
NULL, NULL);
333+
334+
/* Start */
335+
ret = flb_start(ctx);
336+
TEST_CHECK(ret == 0);
337+
338+
/* Ingest data sample */
339+
ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
340+
TEST_CHECK(ret >= 0);
341+
342+
sleep(2);
343+
flb_stop(ctx);
344+
flb_destroy(ctx);
345+
}
346+
347+
static void cb_check_drop_single_key_on(void *ctx, int ffd,
348+
int res_ret, void *res_data, size_t res_size,
349+
void *data)
350+
{
351+
char *p;
352+
flb_sds_t out_js = res_data;
353+
char *index_line = "\\\"value\\\"";
354+
355+
p = strstr(out_js, index_line);
356+
if (!TEST_CHECK(p != NULL)) {
357+
TEST_MSG("Given:%s", out_js);
358+
}
359+
360+
flb_sds_destroy(out_js);
361+
}
362+
363+
void flb_test_drop_single_key_on()
364+
{
365+
int ret;
366+
int size = sizeof(JSON_BASIC) - 1;
367+
flb_ctx_t *ctx;
368+
int in_ffd;
369+
int out_ffd;
370+
371+
/* Create context, flush every second (some checks omitted here) */
372+
ctx = flb_create();
373+
flb_service_set(ctx, "flush", "1", "grace", "1",
374+
"log_level", "error",
375+
NULL);
376+
377+
/* Lib input mode */
378+
in_ffd = flb_input(ctx, (char *) "lib", NULL);
379+
flb_input_set(ctx, in_ffd, "tag", "test", NULL);
380+
381+
/* Loki output */
382+
out_ffd = flb_output(ctx, (char *) "loki", NULL);
383+
flb_output_set(ctx, out_ffd,
384+
"match", "test",
385+
"drop_single_key", "on",
386+
NULL);
387+
388+
/* Enable test mode */
389+
ret = flb_output_set_test(ctx, out_ffd, "formatter",
390+
cb_check_drop_single_key_on,
391+
NULL, NULL);
392+
393+
/* Start */
394+
ret = flb_start(ctx);
395+
TEST_CHECK(ret == 0);
396+
397+
/* Ingest data sample */
398+
ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
399+
TEST_CHECK(ret >= 0);
400+
401+
sleep(2);
402+
flb_stop(ctx);
403+
flb_destroy(ctx);
404+
}
405+
406+
static void cb_check_drop_single_key_raw(void *ctx, int ffd,
407+
int res_ret, void *res_data, size_t res_size,
408+
void *data)
409+
{
410+
char *p;
411+
flb_sds_t out_js = res_data;
412+
char *index_line = "\"value\"";
413+
414+
p = strstr(out_js, index_line);
415+
if (!TEST_CHECK(p != NULL)) {
416+
TEST_MSG("Given:%s", out_js);
417+
}
418+
419+
flb_sds_destroy(out_js);
420+
}
421+
422+
void flb_test_drop_single_key_raw()
423+
{
424+
int ret;
425+
int size = sizeof(JSON_BASIC) - 1;
426+
flb_ctx_t *ctx;
427+
int in_ffd;
428+
int out_ffd;
429+
430+
/* Create context, flush every second (some checks omitted here) */
431+
ctx = flb_create();
432+
flb_service_set(ctx, "flush", "1", "grace", "1",
433+
"log_level", "error",
434+
NULL);
435+
436+
/* Lib input mode */
437+
in_ffd = flb_input(ctx, (char *) "lib", NULL);
438+
flb_input_set(ctx, in_ffd, "tag", "test", NULL);
439+
440+
/* Loki output */
441+
out_ffd = flb_output(ctx, (char *) "loki", NULL);
442+
flb_output_set(ctx, out_ffd,
443+
"match", "test",
444+
"drop_single_key", "raw",
445+
NULL);
446+
447+
/* Enable test mode */
448+
ret = flb_output_set_test(ctx, out_ffd, "formatter",
449+
cb_check_drop_single_key_raw,
450+
NULL, NULL);
451+
452+
/* Start */
453+
ret = flb_start(ctx);
454+
TEST_CHECK(ret == 0);
455+
456+
/* Ingest data sample */
457+
ret = flb_lib_push(ctx, in_ffd, (char *) JSON_BASIC, size);
458+
TEST_CHECK(ret >= 0);
459+
460+
sleep(2);
461+
flb_stop(ctx);
462+
flb_destroy(ctx);
463+
}
288464

289465
static void cb_check_line_format_remove_keys(void *ctx, int ffd,
290466
int res_ret, void *res_data,
@@ -611,13 +787,16 @@ void flb_test_float_value()
611787
/* Test list */
612788
TEST_LIST = {
613789
{"remove_keys_remove_map" , flb_test_remove_map},
614-
{"labels_ra" , flb_test_labels_ra },
615-
{"remove_keys" , flb_test_remove_keys },
616-
{"basic" , flb_test_basic },
617-
{"labels" , flb_test_labels },
618-
{"label_keys" , flb_test_label_keys },
619-
{"line_format" , flb_test_line_format },
620-
{"label_map_path" , flb_test_label_map_path},
621-
{"float_value" , flb_test_float_value},
790+
{"labels_ra" , flb_test_labels_ra },
791+
{"remove_keys" , flb_test_remove_keys },
792+
{"basic" , flb_test_basic },
793+
{"labels" , flb_test_labels },
794+
{"label_keys" , flb_test_label_keys },
795+
{"line_format" , flb_test_line_format },
796+
{"drop_single_key_off" , flb_test_drop_single_key_off },
797+
{"drop_single_key_on" , flb_test_drop_single_key_on },
798+
{"drop_single_key_raw" , flb_test_drop_single_key_raw },
799+
{"label_map_path" , flb_test_label_map_path},
800+
{"float_value" , flb_test_float_value},
622801
{NULL, NULL}
623802
};

0 commit comments

Comments
 (0)