Skip to content

Commit e9fee7d

Browse files
committed
loki_out: add stuctured_metadata_map_keys
* Adds stuctured_metadata_map_keys config to dynamically populate stuctured_metadata from a map Signed-off-by: Greg Eales <[email protected]>
1 parent e7c3e93 commit e9fee7d

File tree

3 files changed

+318
-22
lines changed

3 files changed

+318
-22
lines changed

plugins/out_loki/loki.c

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ static void flb_loki_kv_exit(struct flb_loki *ctx)
298298
mk_list_foreach_safe(head, tmp, &ctx->structured_metadata_list) {
299299
kv = mk_list_entry(head, struct flb_loki_kv, _head);
300300

301+
/* unlink and destroy */
302+
mk_list_del(&kv->_head);
303+
flb_loki_kv_destroy(kv);
304+
}
305+
mk_list_foreach_safe(head, tmp, &ctx->structured_metadata_map_keys_list) {
306+
kv = mk_list_entry(head, struct flb_loki_kv, _head);
307+
301308
/* unlink and destroy */
302309
mk_list_del(&kv->_head);
303310
flb_loki_kv_destroy(kv);
@@ -416,6 +423,87 @@ static void pack_kv(struct flb_loki *ctx,
416423
}
417424
}
418425

426+
/* Similar to pack_kv above, except will only use msgpack_objects of type
427+
* MSGPACK_OBJECT_MAP, and will iterate over the keys adding each entry as a separate
428+
* item. Non-string map values are serialised to JSON, as Loki requires all values to be
429+
* strings.
430+
*/
431+
static void pack_maps(struct flb_loki *ctx,
432+
msgpack_packer *mp_pck,
433+
char *tag, int tag_len,
434+
msgpack_object *map,
435+
struct flb_mp_map_header *mh,
436+
struct mk_list *list)
437+
{
438+
struct mk_list *head;
439+
struct flb_loki_kv *kv;
440+
441+
msgpack_object *start_key;
442+
msgpack_object *out_key;
443+
msgpack_object *out_val;
444+
445+
msgpack_object_map accessed_map;
446+
uint32_t accessed_map_index;
447+
msgpack_object_kv accessed_map_kv;
448+
449+
char *accessed_map_val_json;
450+
451+
mk_list_foreach(head, list) {
452+
/* get the flb_loki_kv for this iteration of the loop */
453+
kv = mk_list_entry(head, struct flb_loki_kv, _head);
454+
455+
/* record accessor key/value pair */
456+
// TODO what if kv->ra_val != NULL?
457+
if (kv->ra_key != NULL && kv->ra_val == NULL) {
458+
459+
/* try to get the value for the record accessor */
460+
if (flb_ra_get_kv_pair(kv->ra_key, *map, &start_key, &out_key, &out_val)
461+
== MSGPACK_UNPACK_CONTINUE) {
462+
463+
/* we require the value to be a map, or it doesn't make sense as this is
464+
* adding a map's key / values */
465+
if (out_val->type != MSGPACK_OBJECT_MAP || out_val->via.map.size <= 0) {
466+
flb_plg_debug(ctx->ins, "No valid map data found for key %s",
467+
kv->ra_key->pattern);
468+
} else {
469+
accessed_map = out_val->via.map;
470+
471+
/* for each entry in the accessed map... */
472+
for (accessed_map_index = 0; accessed_map_index < accessed_map.size;
473+
++accessed_map_index) {
474+
475+
/* get the entry */
476+
accessed_map_kv = accessed_map.ptr[accessed_map_index];
477+
478+
/* Pack the key and value */
479+
flb_mp_map_header_append(mh);
480+
481+
pack_label_key(mp_pck, (char*) accessed_map_kv.key.via.str.ptr,
482+
accessed_map_kv.key.via.str.size);
483+
/* Does this need optimising? For example, to handle bool as
484+
* non-JSON? */
485+
if (accessed_map_kv.val.type == MSGPACK_OBJECT_STR) {
486+
msgpack_pack_str_with_body(mp_pck,
487+
accessed_map_kv.val.via.str.ptr,
488+
accessed_map_kv.val.via.str.size);
489+
}
490+
/* convert value to JSON, as Loki expects a string value */
491+
else {
492+
accessed_map_val_json = flb_msgpack_to_json_str(1024,
493+
&accessed_map_kv.val);
494+
if (accessed_map_val_json) {
495+
msgpack_pack_str_with_body(mp_pck, accessed_map_val_json,
496+
strlen(accessed_map_val_json));
497+
flb_free(accessed_map_val_json);
498+
}
499+
}
500+
}
501+
}
502+
}
503+
}
504+
}
505+
}
506+
419507
static flb_sds_t pack_structured_metadata(struct flb_loki *ctx,
420508
msgpack_packer *mp_pck,
421509
char *tag, int tag_len,
@@ -424,7 +512,13 @@ static flb_sds_t pack_structured_metadata(struct flb_loki *ctx,
424512
struct flb_mp_map_header mh;
425513
/* Initialize dynamic map header */
426514
flb_mp_map_header_init(&mh, mp_pck);
427-
pack_kv(ctx, mp_pck, tag, tag_len, map, &mh, &ctx->structured_metadata_list);
515+
if (ctx->structured_metadata_map_keys) {
516+
pack_maps(ctx, mp_pck, tag, tag_len, map, &mh, &ctx->structured_metadata_map_keys_list);
517+
}
518+
// explicit structured_metadata entries override structured_metadata_map_keys entries
519+
if (ctx->structured_metadata) {
520+
pack_kv(ctx, mp_pck, tag, tag_len, map, &mh, &ctx->structured_metadata_list);
521+
}
428522
flb_mp_map_header_end(&mh);
429523
return 0;
430524
}
@@ -788,6 +882,7 @@ static int parse_labels(struct flb_loki *ctx)
788882

789883
flb_loki_kv_init(&ctx->labels_list);
790884
flb_loki_kv_init(&ctx->structured_metadata_list);
885+
flb_loki_kv_init(&ctx->structured_metadata_map_keys_list);
791886

792887
if (ctx->structured_metadata) {
793888
ret = parse_kv(ctx, ctx->structured_metadata, &ctx->structured_metadata_list, &ra_used);
@@ -796,6 +891,26 @@ static int parse_labels(struct flb_loki *ctx)
796891
}
797892
}
798893

894+
/* Append structured metadata map keys set in the configuration */
895+
if (ctx->structured_metadata_map_keys) {
896+
mk_list_foreach(head, ctx->structured_metadata_map_keys) {
897+
entry = mk_list_entry(head, struct flb_slist_entry, _head);
898+
if (entry->str[0] != '$') {
899+
flb_plg_error(ctx->ins,
900+
"invalid structured metadata map key, the name must start with '$'");
901+
return -1;
902+
}
903+
904+
ret = flb_loki_kv_append(ctx, &ctx->structured_metadata_map_keys_list, entry->str, NULL);
905+
if (ret == -1) {
906+
return -1;
907+
}
908+
else if (ret > 0) {
909+
ra_used++;
910+
}
911+
}
912+
}
913+
799914
if (ctx->labels) {
800915
ret = parse_kv(ctx, ctx->labels, &ctx->labels_list, &ra_used);
801916
if (ret == -1) {
@@ -971,6 +1086,7 @@ static struct flb_loki *loki_config_create(struct flb_output_instance *ins,
9711086
ctx->ins = ins;
9721087
flb_loki_kv_init(&ctx->labels_list);
9731088
flb_loki_kv_init(&ctx->structured_metadata_list);
1089+
flb_loki_kv_init(&ctx->structured_metadata_map_keys_list);
9741090

9751091
/* Register context with plugin instance */
9761092
flb_output_set_context(ins, ctx);
@@ -1539,12 +1655,13 @@ static flb_sds_t loki_compose_payload(struct flb_loki *ctx,
15391655
while ((ret = flb_log_event_decoder_next(
15401656
&log_decoder,
15411657
&log_event)) == FLB_EVENT_DECODER_SUCCESS) {
1542-
msgpack_pack_array(&mp_pck, ctx->structured_metadata ? 3 : 2);
1658+
msgpack_pack_array(&mp_pck, ctx->structured_metadata ||
1659+
ctx->structured_metadata_map_keys ? 3 : 2);
15431660

15441661
/* Append the timestamp */
15451662
pack_timestamp(&mp_pck, &log_event.timestamp);
15461663
pack_record(ctx, &mp_pck, log_event.body, dynamic_tenant_id);
1547-
if (ctx->structured_metadata) {
1664+
if (ctx->structured_metadata || ctx->structured_metadata_map_keys) {
15481665
pack_structured_metadata(ctx, &mp_pck, tag, tag_len, NULL);
15491666
}
15501667
}
@@ -1575,12 +1692,13 @@ static flb_sds_t loki_compose_payload(struct flb_loki *ctx,
15751692
msgpack_pack_str_body(&mp_pck, "values", 6);
15761693
msgpack_pack_array(&mp_pck, 1);
15771694

1578-
msgpack_pack_array(&mp_pck, ctx->structured_metadata ? 3 : 2);
1695+
msgpack_pack_array(&mp_pck, ctx->structured_metadata ||
1696+
ctx->structured_metadata_map_keys ? 3 : 2);
15791697

15801698
/* Append the timestamp */
15811699
pack_timestamp(&mp_pck, &log_event.timestamp);
15821700
pack_record(ctx, &mp_pck, log_event.body, dynamic_tenant_id);
1583-
if (ctx->structured_metadata) {
1701+
if (ctx->structured_metadata || ctx->structured_metadata_map_keys) {
15841702
pack_structured_metadata(ctx, &mp_pck, tag, tag_len, log_event.body);
15851703
}
15861704
}
@@ -1905,6 +2023,13 @@ static struct flb_config_map config_map[] = {
19052023
0, FLB_TRUE, offsetof(struct flb_loki, structured_metadata),
19062024
"optional structured metadata fields for API requests."
19072025
},
2026+
2027+
{
2028+
FLB_CONFIG_MAP_CLIST, "structured_metadata_map_keys", NULL,
2029+
0, FLB_TRUE, offsetof(struct flb_loki, structured_metadata_map_keys),
2030+
"optional structured metadata fields, as derived dynamically from configured maps "
2031+
"keys, for API requests."
2032+
},
19082033

19092034
{
19102035
FLB_CONFIG_MAP_BOOL, "auto_kubernetes_labels", "false",

plugins/out_loki/loki.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct flb_loki {
7878
struct mk_list *labels;
7979
struct mk_list *label_keys;
8080
struct mk_list *structured_metadata;
81+
struct mk_list *structured_metadata_map_keys;
8182
struct mk_list *remove_keys;
8283

8384
flb_sds_t label_map_path;
@@ -87,13 +88,14 @@ struct flb_loki {
8788
char *tcp_host;
8889
int out_line_format;
8990
int out_drop_single_key;
90-
int ra_used; /* number of record accessor label keys */
91-
struct flb_record_accessor *ra_k8s; /* kubernetes record accessor */
92-
struct mk_list labels_list; /* list of flb_loki_kv nodes */
93-
struct mk_list structured_metadata_list; /* list of flb_loki_kv nodes */
94-
struct mk_list remove_keys_derived; /* remove_keys with label RAs */
95-
struct flb_mp_accessor *remove_mpa; /* remove_keys multi-pattern accessor */
96-
struct flb_record_accessor *ra_tenant_id_key; /* dynamic tenant id key */
91+
int ra_used; /* number of record accessor label keys */
92+
struct flb_record_accessor *ra_k8s; /* kubernetes record accessor */
93+
struct mk_list labels_list; /* list of flb_loki_kv nodes */
94+
struct mk_list structured_metadata_list; /* list of flb_loki_kv nodes */
95+
struct mk_list structured_metadata_map_keys_list; /* list of flb_loki_kv nodes */
96+
struct mk_list remove_keys_derived; /* remove_keys with label RAs */
97+
struct flb_mp_accessor *remove_mpa; /* remove_keys multi-pattern accessor */
98+
struct flb_record_accessor *ra_tenant_id_key; /* dynamic tenant id key */
9799

98100
struct cfl_list dynamic_tenant_list;
99101
pthread_mutex_t dynamic_tenant_list_lock;

0 commit comments

Comments
 (0)