diff --git a/plugins/processor_content_modifier/CMakeLists.txt b/plugins/processor_content_modifier/CMakeLists.txt index d3ea9d24a10..681d2cd58df 100644 --- a/plugins/processor_content_modifier/CMakeLists.txt +++ b/plugins/processor_content_modifier/CMakeLists.txt @@ -1,7 +1,10 @@ set(src cm_config.c cm_logs.c + cm_metrics.c cm_traces.c + cm_opentelemetry.c + cm_utils.c cm.c ) diff --git a/plugins/processor_content_modifier/cm.c b/plugins/processor_content_modifier/cm.c index 4a2cf444793..4c56d659ff6 100644 --- a/plugins/processor_content_modifier/cm.c +++ b/plugins/processor_content_modifier/cm.c @@ -98,6 +98,24 @@ static int cb_process_traces(struct flb_processor_instance *ins, } +static int cb_process_metrics(struct flb_processor_instance *ins, + struct cmt *in_cmt, + struct cmt **out_cmt, + const char *tag, + int tag_len) +{ + int ret; + struct content_modifier_ctx *ctx; + + if (!ins->context) { + return FLB_PROCESSOR_FAILURE; + } + ctx = ins->context; + + ret = cm_metrics_process(ins, ctx, in_cmt, out_cmt, tag, tag_len); + return ret; +} + static struct flb_config_map config_map[] = { { FLB_CONFIG_MAP_STR, "context", NULL, @@ -144,7 +162,7 @@ struct flb_processor_plugin processor_content_modifier_plugin = { .description = "Modify the content of Logs, Metrics and Traces", .cb_init = cb_init, .cb_process_logs = cb_process_logs, - .cb_process_metrics = NULL, + .cb_process_metrics = cb_process_metrics, .cb_process_traces = cb_process_traces, .cb_exit = cb_exit, .config_map = config_map, diff --git a/plugins/processor_content_modifier/cm.h b/plugins/processor_content_modifier/cm.h index c6cae0b6b34..181872b0754 100644 --- a/plugins/processor_content_modifier/cm.h +++ b/plugins/processor_content_modifier/cm.h @@ -116,5 +116,10 @@ int cm_traces_process(struct flb_processor_instance *ins, struct ctrace *traces_context, const char *tag, int tag_len); +int cm_metrics_process(struct flb_processor_instance *ins, + struct content_modifier_ctx *ctx, + struct cmt *in_cmt, + struct cmt **out_cmt, + const char *tag, int tag_len); #endif diff --git a/plugins/processor_content_modifier/cm_config.c b/plugins/processor_content_modifier/cm_config.c index 487977cb113..70ba9915715 100644 --- a/plugins/processor_content_modifier/cm_config.c +++ b/plugins/processor_content_modifier/cm_config.c @@ -218,6 +218,53 @@ static int set_context(struct content_modifier_ctx *ctx) strcasecmp(ctx->context_str, "attributes") == 0) { context = CM_CONTEXT_METRIC_LABELS; } + + /* + * OpenTelemetry contexts + * ---------------------- + */ + else if (strcasecmp(ctx->context_str, "otel_resource_attributes") == 0) { + context = CM_CONTEXT_OTEL_RESOURCE_ATTR; + } + else if (strcasecmp(ctx->context_str, "otel_scope_attributes") == 0) { + context = CM_CONTEXT_OTEL_SCOPE_ATTR; + } + else if (strcasecmp(ctx->context_str, "otel_scope_name") == 0) { + /* + * scope name is restricted to specific actions, make sure the user + * cannot messed it up + * + * action allowed ? + * ----------------------------- + * CM_ACTION_INSERT Yes + * CM_ACTION_UPSERT Yes + * CM_ACTION_DELETE Yes + * CM_ACTION_RENAME No + * CM_ACTION_HASH Yes + * CM_ACTION_EXTRACT No + * CM_ACTION_CONVERT No + */ + + if (ctx->action_type == CM_ACTION_RENAME || + ctx->action_type == CM_ACTION_EXTRACT || + ctx->action_type == CM_ACTION_CONVERT) { + flb_plg_error(ctx->ins, "action '%s' is not allowed for context '%s'", + ctx->action_str, ctx->context_str); + return -1; + } + + /* check that 'name' is the key set */ + if (!ctx->key) { + ctx->key = flb_sds_create("name"); + } + else if (strcasecmp(ctx->key, "name") != 0) { + flb_plg_error(ctx->ins, "context '%s' requires the name of the key to be 'name', no '%s'", + ctx->context_str, ctx->key); + return -1; + } + + context = CM_CONTEXT_OTEL_SCOPE_NAME; + } else { flb_plg_error(ctx->ins, "unknown metrics context '%s'", ctx->context_str); return -1; diff --git a/plugins/processor_content_modifier/cm_logs.c b/plugins/processor_content_modifier/cm_logs.c index 278540039b5..49e6e2fe08d 100644 --- a/plugins/processor_content_modifier/cm_logs.c +++ b/plugins/processor_content_modifier/cm_logs.c @@ -22,345 +22,15 @@ #include #include #include -#include #include #include #include #include "cm.h" #include "cm_utils.h" +#include "cm_opentelemetry.h" #include -#include - -static int hex_encode(unsigned char *input_buffer, - size_t input_length, - cfl_sds_t *output_buffer) -{ - const char hex[] = "0123456789abcdef"; - cfl_sds_t result; - size_t index; - - if (cfl_sds_alloc(*output_buffer) <= (input_length * 2)) { - result = cfl_sds_increase(*output_buffer, - (input_length * 2) - - cfl_sds_alloc(*output_buffer)); - - if (result == NULL) { - return FLB_FALSE; - } - - *output_buffer = result; - } - - for (index = 0; index < input_length; index++) { - (*output_buffer)[index * 2 + 0] = hex[(input_buffer[index] >> 4) & 0xF]; - (*output_buffer)[index * 2 + 1] = hex[(input_buffer[index] >> 0) & 0xF]; - } - - cfl_sds_set_len(*output_buffer, input_length * 2); - - (*output_buffer)[index * 2] = '\0'; - - return FLB_TRUE; -} - -static int hash_transformer(void *context, struct cfl_variant *value) -{ - unsigned char digest_buffer[32]; - struct cfl_variant *converted_value; - cfl_sds_t encoded_hash; - int result; - - if (value == NULL) { - return FLB_FALSE; - } - - result = cfl_variant_convert(value, - &converted_value, - CFL_VARIANT_STRING); - - if (result != FLB_TRUE) { - return FLB_FALSE; - } - - if (cfl_variant_size_get(converted_value) == 0) { - cfl_variant_destroy(converted_value); - return FLB_TRUE; - } - - result = flb_hash_simple(FLB_HASH_SHA256, - (unsigned char *) converted_value->data.as_string, - cfl_sds_len(converted_value->data.as_string), - digest_buffer, - sizeof(digest_buffer)); - - if (result != FLB_CRYPTO_SUCCESS) { - cfl_variant_destroy(converted_value); - - return FLB_FALSE; - } - - result = hex_encode(digest_buffer, - sizeof(digest_buffer), - &converted_value->data.as_string); - - if (result != FLB_TRUE) { - cfl_variant_destroy(converted_value); - - return FLB_FALSE; - } - - encoded_hash = cfl_sds_create(converted_value->data.as_string); - cfl_variant_destroy(converted_value); - if (encoded_hash == NULL) { - return FLB_FALSE; - } - - /* NOTE: this part does a manual modification of the variant content */ - if (value->type == CFL_VARIANT_STRING || - value->type == CFL_VARIANT_BYTES) { - if (value->referenced == CFL_FALSE) { - cfl_sds_destroy(value->data.as_string); - } - } - else if (value->type == CFL_VARIANT_ARRAY) { - cfl_array_destroy(value->data.as_array); - } - else if (value->type == CFL_VARIANT_KVLIST) { - cfl_kvlist_destroy(value->data.as_kvlist); - } - - value->type = CFL_VARIANT_STRING; - value->data.as_string = encoded_hash; - value->referenced = CFL_FALSE; - - cfl_variant_size_set(value, cfl_sds_len(encoded_hash)); - - return FLB_TRUE; -} - -cfl_sds_t cfl_variant_convert_to_json(struct cfl_variant *value) -{ - cfl_sds_t json_result; - mpack_writer_t writer; - char *data; - size_t size; - - data = NULL; - size = 0; - - mpack_writer_init_growable(&writer, &data, &size); - - pack_cfl_variant(&writer, value); - - mpack_writer_destroy(&writer); - - json_result = flb_msgpack_raw_to_json_sds(data, size); - - return json_result; -} - -int cfl_variant_convert(struct cfl_variant *input_value, - struct cfl_variant **output_value, - int output_type) -{ - int ret; - int errno_backup; - int64_t as_int; - double as_double; - char buf[64]; - char *str = NULL; - char *converstion_canary = NULL; - struct cfl_variant *tmp = NULL; - - errno_backup = errno; - - /* input: string, bytes or reference */ - if (input_value->type == CFL_VARIANT_STRING || input_value->type == CFL_VARIANT_BYTES || - input_value->type == CFL_VARIANT_REFERENCE) { - - if (output_type == CFL_VARIANT_STRING || - output_type == CFL_VARIANT_BYTES) { - - tmp = cfl_variant_create_from_string_s(input_value->data.as_string, - cfl_variant_size_get(input_value), - CFL_FALSE); - if (!tmp) { - return CFL_FALSE; - } - } - else if (output_type == CFL_VARIANT_BOOL) { - as_int = CFL_FALSE; - - if (cfl_variant_size_get(input_value) == 4 && - strncasecmp(input_value->data.as_string, "true", 4) == 0) { - as_int = CFL_TRUE; - } - else if (cfl_variant_size_get(input_value) == 5 && - strncasecmp(input_value->data.as_string, "false", 5) == 0) { - as_int = CFL_FALSE; - } - - tmp = cfl_variant_create_from_bool(as_int); - } - else if (output_type == CFL_VARIANT_INT) { - errno = 0; - - if (input_value->referenced) { - tmp = cfl_variant_create_from_string_s(input_value->data.as_string, - cfl_variant_size_get(input_value), - CFL_FALSE); - if (!tmp) { - return CFL_FALSE; - } - str = tmp->data.as_string; - } - else { - str = input_value->data.as_string; - } - - as_int = strtoimax(str, &converstion_canary, 10); - if (errno == ERANGE || errno == EINVAL) { - errno = errno_backup; - if (tmp) { - cfl_variant_destroy(tmp); - } - return CFL_FALSE; - } - - if (tmp) { - cfl_variant_destroy(tmp); - } - - tmp = cfl_variant_create_from_int64(as_int); - } - else if (output_type == CFL_VARIANT_DOUBLE) { - errno = 0; - converstion_canary = NULL; - - if (input_value->referenced) { - tmp = cfl_variant_create_from_string_s(input_value->data.as_string, - cfl_variant_size_get(input_value), - CFL_FALSE); - if (!tmp) { - return CFL_FALSE; - } - str = tmp->data.as_string; - } - else { - str = input_value->data.as_string; - } - - as_double = strtod(str, &converstion_canary); - if (errno == ERANGE) { - errno = errno_backup; - if (tmp) { - cfl_variant_destroy(tmp); - } - return CFL_FALSE; - } - - if (tmp) { - cfl_variant_destroy(tmp); - } - - if (as_double == 0 && converstion_canary == input_value->data.as_string) { - errno = errno_backup; - return CFL_FALSE; - } - - tmp = cfl_variant_create_from_double(as_double); - } - else { - return CFL_FALSE; - } - } - /* input: int */ - else if (input_value->type == CFL_VARIANT_INT) { - if (output_type == CFL_VARIANT_STRING || output_type == CFL_VARIANT_BYTES) { - ret = snprintf(buf, sizeof(buf), "%" PRIi64, input_value->data.as_int64); - if (ret < 0 || ret >= sizeof(buf)) { - return CFL_FALSE; - } - tmp = cfl_variant_create_from_string_s(buf, ret, CFL_FALSE); - } - else if (output_type == CFL_VARIANT_BOOL) { - as_int = CFL_FALSE; - if (input_value->data.as_int64 != 0) { - as_int = CFL_TRUE; - } - - tmp = cfl_variant_create_from_bool(as_int); - } - else if (output_type == CFL_VARIANT_INT) { - /* same type, do nothing */ - } - else if (output_type == CFL_VARIANT_DOUBLE) { - as_double = (double) input_value->data.as_int64; - tmp = cfl_variant_create_from_double(as_double); - } - else { - return CFL_FALSE; - } - } - else if (input_value->type == CFL_VARIANT_DOUBLE) { - if (output_type == CFL_VARIANT_STRING || - output_type == CFL_VARIANT_BYTES) { - - ret = snprintf(buf, sizeof(buf), "%.17g", input_value->data.as_double); - if (ret < 0 || ret >= sizeof(buf)) { - return CFL_FALSE; - } - tmp = cfl_variant_create_from_string_s(buf, ret, CFL_FALSE); - } - else if (output_type == CFL_VARIANT_BOOL) { - as_int = CFL_FALSE; - - if (input_value->data.as_double != 0) { - as_int = CFL_TRUE; - } - - tmp = cfl_variant_create_from_bool(as_int); - } - else if (output_type == CFL_VARIANT_INT) { - as_int = (int64_t) round(input_value->data.as_double); - tmp = cfl_variant_create_from_int64(as_int); - } - else if (output_type == CFL_VARIANT_DOUBLE) { - as_double = input_value->data.as_int64; - tmp = cfl_variant_create_from_double(as_double); - } - else { - return CFL_FALSE; - } - } - else if (input_value->type == CFL_VARIANT_NULL) { - if (output_type == CFL_VARIANT_STRING || - output_type == CFL_VARIANT_BYTES) { - - tmp = cfl_variant_create_from_string_s("null", 4, CFL_FALSE); - } - else if (output_type == CFL_VARIANT_BOOL) { - tmp = cfl_variant_create_from_bool(CFL_FALSE); - } - else if (output_type == CFL_VARIANT_INT) { - tmp = cfl_variant_create_from_int64(0); - } - else if (output_type == CFL_VARIANT_DOUBLE) { - tmp = cfl_variant_create_from_double(0); - } - else { - return CFL_FALSE; - } - } - else { - return CFL_FALSE; - } - - *output_value = tmp; - return FLB_TRUE; -} static struct cfl_kvpair *cfl_object_kvpair_get(struct cfl_object *obj, cfl_sds_t key) { @@ -501,7 +171,7 @@ static int run_action_hash(struct content_modifier_ctx *ctx, return 0; } - ret = hash_transformer(NULL, kvpair->val); + ret = cm_utils_hash_transformer(NULL, kvpair->val); if (ret == FLB_FALSE) { return -1; } @@ -581,7 +251,7 @@ static int run_action_convert(struct content_modifier_ctx *ctx, /* convert the value */ v = kvpair->val; - ret = cfl_variant_convert(v, &converted, converted_type); + ret = cm_utils_variant_convert(v, &converted, converted_type); if (ret != FLB_TRUE) { return -1; } @@ -599,134 +269,29 @@ static int run_action_convert(struct content_modifier_ctx *ctx, return 0; } -static struct cfl_variant *otel_get_or_create_attributes(struct cfl_kvlist *kvlist) -{ - int ret; - struct cfl_list *head; - struct cfl_list *tmp; - struct cfl_kvpair *kvpair; - struct cfl_variant *val; - struct cfl_kvlist *kvlist_tmp; - - /* iterate resource to find the attributes field */ - cfl_list_foreach_safe(head, tmp, &kvlist->list) { - kvpair = cfl_list_entry(head, struct cfl_kvpair, _head); - if (cfl_sds_len(kvpair->key) != 10) { - continue; - } - - if (strncmp(kvpair->key, "attributes", 10) == 0) { - val = kvpair->val; - if (val->type != CFL_VARIANT_KVLIST) { - return NULL; - } - - return val; - } - } - - /* create an empty kvlist as the value of attributes */ - kvlist_tmp = cfl_kvlist_create(); - if (!kvlist_tmp) { - return NULL; - } - - /* create the attributes kvpair */ - ret = cfl_kvlist_insert_kvlist_s(kvlist, "attributes", 10, kvlist_tmp); - if (ret != 0) { - cfl_kvlist_destroy(kvlist_tmp); - return NULL; - } - - /* get the last kvpair from the list */ - kvpair = cfl_list_entry_last(&kvlist->list, struct cfl_kvpair, _head); - if (!kvpair) { - return NULL; - } - - return kvpair->val; -} - static struct cfl_variant *otel_get_attributes(int context, struct flb_mp_chunk_record *record) { - int key_len; - const char *key_buf; - struct cfl_list *head; struct cfl_object *obj = NULL; - struct cfl_variant *val; struct cfl_kvlist *kvlist; - struct cfl_kvpair *kvpair; - struct cfl_variant *var_attr; - - if (context == CM_CONTEXT_OTEL_RESOURCE_ATTR) { - key_buf = "resource"; - key_len = 8; - } - else if (context == CM_CONTEXT_OTEL_SCOPE_ATTR) { - key_buf = "scope"; - key_len = 5; - } - else { - return NULL; - } obj = record->cobj_record; kvlist = obj->variant->data.as_kvlist; - cfl_list_foreach(head, &kvlist->list) { - kvpair = cfl_list_entry(head, struct cfl_kvpair, _head); - - if (cfl_sds_len(kvpair->key) != key_len) { - continue; - } - - if (strncmp(kvpair->key, key_buf, key_len) == 0) { - val = kvpair->val; - if (val->type != CFL_VARIANT_KVLIST) { - return NULL; - } - var_attr = otel_get_or_create_attributes(val->data.as_kvlist); - if (!var_attr) { - return NULL; - } - - return var_attr; - } - } - - return NULL; + return cm_otel_get_attributes(CM_TELEMETRY_LOGS, context, kvlist); } static struct cfl_variant *otel_get_scope(struct flb_mp_chunk_record *record) { - struct cfl_list *head; struct cfl_object *obj; - struct cfl_variant *val; struct cfl_kvlist *kvlist; - struct cfl_kvpair *kvpair; obj = record->cobj_record; kvlist = obj->variant->data.as_kvlist; - cfl_list_foreach(head, &kvlist->list) { - kvpair = cfl_list_entry(head, struct cfl_kvpair, _head); - if (cfl_sds_len(kvpair->key) != 5) { - continue; - } - - if (strncmp(kvpair->key, "scope", 5) == 0) { - val = kvpair->val; - if (val->type != CFL_VARIANT_KVLIST) { - return NULL; - } - - return val; - } - } - - return NULL; + return cm_otel_get_scope_metadata(CM_TELEMETRY_LOGS, kvlist); } + int cm_logs_process(struct flb_processor_instance *ins, struct content_modifier_ctx *ctx, struct flb_mp_chunk_cobj *chunk_cobj, diff --git a/plugins/processor_content_modifier/cm_metrics.c b/plugins/processor_content_modifier/cm_metrics.c new file mode 100644 index 00000000000..d4e97aec533 --- /dev/null +++ b/plugins/processor_content_modifier/cm_metrics.c @@ -0,0 +1,346 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "cm.h" +#include "cm_utils.h" +#include "cm_opentelemetry.h" + + +static struct cfl_kvpair *kvlist_get_kvpair(struct cfl_kvlist *kvlist, cfl_sds_t key) +{ + struct cfl_list *head; + struct cfl_kvpair *kvpair; + + cfl_list_foreach(head, &kvlist->list) { + kvpair = cfl_list_entry(head, struct cfl_kvpair, _head); + + if (cfl_sds_len(key) != cfl_sds_len(kvpair->key)) { + continue; + } + + if (strncmp(key, kvpair->key, cfl_sds_len(key)) == 0) { + return kvpair; + } + } + + return NULL; +} + +static int run_action_insert(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key, cfl_sds_t value) +{ + int ret; + struct cfl_kvpair *kvpair; + + /* check that the key don't exists */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (kvpair) { + /* Insert requires the key don't exists, we fail silently */ + return 0; + } + + /* insert the new value */ + ret = cfl_kvlist_insert_string_s(kvlist, key, cfl_sds_len(key), value, cfl_sds_len(value), + CFL_FALSE); + if (ret != 0) { + flb_plg_debug(ctx->ins, "[action: insert] failed to insert key: %s", key); + return -1; + } + return 0; +} + +static int run_action_upsert(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key, cfl_sds_t value) +{ + int ret; + struct cfl_kvpair *kvpair; + + /* if the kv pair already exists, remove it from the list */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (kvpair) { + cfl_kvpair_destroy(kvpair); + } + + /* insert the key with the updated value */ + ret = cfl_kvlist_insert_string_s(kvlist, key, cfl_sds_len(key), value, cfl_sds_len(value), + CFL_FALSE); + if (ret != 0) { + return -1; + } + + return 0; +} + +static int run_action_delete(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key) +{ + struct cfl_kvpair *kvpair; + + /* if the kv pair already exists, remove it from the list */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (kvpair) { + cfl_kvpair_destroy(kvpair); + return 0; + } + + flb_plg_debug(ctx->ins, "[action: delete] key '%s' not found", key); + + /* if the kvpair was not found, it's ok, we return zero */ + return 0; +} + +static int run_action_rename(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key, cfl_sds_t value) +{ + cfl_sds_t tmp; + struct cfl_kvpair *kvpair; + + /* if the kv pair already exists, remove it from the list */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (!kvpair) { + flb_plg_debug(ctx->ins, "[action: rename] key '%s' not found", key); + return 0; + } + + tmp = kvpair->key; + + kvpair->key = cfl_sds_create_len(value, cfl_sds_len(value)); + if (!kvpair->key) { + /* restore previous value */ + kvpair->key = tmp; + return -1; + } + + /* destroy previous value */ + cfl_sds_destroy(tmp); + return 0; +} + +static int run_action_hash(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key) +{ + int ret; + struct cfl_kvpair *kvpair; + + /* if the kv pair already exists, remove it from the list */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (!kvpair) { + /* the key was not found, so it's ok */ + return 0; + } + + ret = cm_utils_hash_transformer(NULL, kvpair->val); + if (ret == FLB_FALSE) { + return -1; + } + + return 0; +} + +static void cb_extract_regex(const char *name, const char *value, size_t value_length, void *context) +{ + + struct cfl_kvlist *kvlist = (struct cfl_kvlist *) context; + + if (cfl_kvlist_contains(kvlist, (char *) name)) { + cfl_kvlist_remove(kvlist, (char *) name); + } + + cfl_kvlist_insert_string_s(kvlist, (char *) name, strlen(name), (char *) value, value_length, + CFL_FALSE); +} + +static int run_action_extract(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key, struct flb_regex *regex) +{ + int ret; + int match_count; + struct flb_regex_search match_list; + struct cfl_kvpair *kvpair; + struct cfl_variant *v; + + /* if the kv pair already exists, remove it from the list */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (!kvpair) { + return -1; + } + + v = kvpair->val; + if (v->type != CFL_VARIANT_STRING) { + return -1; + } + + match_count = flb_regex_do(regex, + v->data.as_string, + cfl_variant_size_get(v), &match_list); + if (match_count <= 0) { + return -1; + } + + ret = flb_regex_parse(regex, &match_list, cb_extract_regex, kvlist); + if (ret == -1) { + return -1; + } + + return 0; +} + +static int run_action_convert(struct content_modifier_ctx *ctx, + struct cfl_kvlist *kvlist, + const char *tag, int tag_len, + cfl_sds_t key, int converted_type) +{ + int ret; + struct cfl_variant *v; + struct cfl_kvpair *kvpair; + struct cfl_variant *converted; + + /* if the kv pair already exists, remove it from the list */ + kvpair = kvlist_get_kvpair(kvlist, key); + if (!kvpair) { + return -1; + } + + /* convert the value */ + v = kvpair->val; + ret = cm_utils_variant_convert(v, &converted, converted_type); + if (ret != FLB_TRUE) { + return -1; + } + + /* remove the old kvpair */ + cfl_kvpair_destroy(kvpair); + + ret = cfl_kvlist_insert_s(kvlist, key, cfl_sds_len(key), converted); + if (ret != 0) { + cfl_variant_destroy(converted); + return -1; + } + + return 0; +} + +int cm_metrics_process(struct flb_processor_instance *ins, + struct content_modifier_ctx *ctx, + struct cmt *in_cmt, + struct cmt **out_cmt, + const char *tag, int tag_len) +{ + int ret = -1; + struct cfl_variant *var = NULL; + + printf("\n\n==== BEFORE =====\n"); + cfl_kvlist_print(stdout, in_cmt->internal_metadata); + printf("\n"); + printf("-----external----\n"); + cfl_kvlist_print(stdout, in_cmt->external_metadata); + fflush(stdout); + + + if (ctx->context_type == CM_CONTEXT_OTEL_RESOURCE_ATTR) { + /* Internal metadata must be valid */ + var = cfl_kvlist_fetch(in_cmt->internal_metadata, "producer"); + if (!var) { + return FLB_PROCESSOR_FAILURE; + } + + if (var->type != CFL_VARIANT_STRING) { + return FLB_PROCESSOR_FAILURE; + } + + /* validate that the value is 'opentelemetry' */ + if (strcmp(var->data.as_string, "opentelemetry") != 0) { + return FLB_PROCESSOR_FAILURE; + } + + /* Now check the external metadata */ + if (!in_cmt->external_metadata) { + return FLB_PROCESSOR_FAILURE; + } + + var = NULL; + + var = cm_otel_get_attributes(CM_TELEMETRY_METRICS, ctx->context_type, in_cmt->external_metadata); + if (!var) { + return FLB_PROCESSOR_FAILURE; + } + } + else if (ctx->context_type == CM_CONTEXT_OTEL_SCOPE_ATTR) { + var = cm_otel_get_attributes(CM_TELEMETRY_METRICS, ctx->context_type, in_cmt->external_metadata); + } + else if ((ctx->context_type == CM_CONTEXT_OTEL_SCOPE_NAME || ctx->context_type == CM_CONTEXT_OTEL_SCOPE_VERSION)) { + var = cm_otel_get_scope_metadata(CM_TELEMETRY_METRICS, in_cmt->external_metadata); + } + + if (!var) { + return FLB_PROCESSOR_FAILURE; + } + + if (ctx->action_type == CM_ACTION_INSERT) { + ret = run_action_insert(ctx, var->data.as_kvlist, tag, tag_len, ctx->key, ctx->value); + } + else if (ctx->action_type == CM_ACTION_UPSERT) { + ret = run_action_upsert(ctx, var->data.as_kvlist, tag, tag_len, ctx->key, ctx->value); + } + else if (ctx->action_type == CM_ACTION_DELETE) { + ret = run_action_delete(ctx, var->data.as_kvlist, tag, tag_len, ctx->key); + } + else if (ctx->action_type == CM_ACTION_RENAME) { + ret = run_action_rename(ctx, var->data.as_kvlist, tag, tag_len, ctx->key, ctx->value); + } + else if (ctx->action_type == CM_ACTION_HASH) { + ret = run_action_hash(ctx, var->data.as_kvlist, tag, tag_len, ctx->key); + } + else if (ctx->action_type == CM_ACTION_EXTRACT) { + ret = run_action_extract(ctx, var->data.as_kvlist, tag, tag_len, ctx->key, ctx->regex); + } + else if (ctx->action_type == CM_ACTION_CONVERT) { + ret = run_action_convert(ctx, var->data.as_kvlist, tag, tag_len, ctx->key, ctx->converted_type); + } + + if (ret != 0) { + return FLB_PROCESSOR_FAILURE; + } + + printf("\n\n==== AFTER =====\n"); + cfl_kvlist_print(stdout, in_cmt->internal_metadata); + printf("\n"); + printf("-----external----\n"); + cfl_kvlist_print(stdout, in_cmt->external_metadata); + fflush(stdout); + + return FLB_PROCESSOR_SUCCESS; +} diff --git a/plugins/processor_content_modifier/cm_opentelemetry.c b/plugins/processor_content_modifier/cm_opentelemetry.c new file mode 100644 index 00000000000..0a37a89a2b5 --- /dev/null +++ b/plugins/processor_content_modifier/cm_opentelemetry.c @@ -0,0 +1,272 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cm.h" +#include "cm_utils.h" + +#include + +struct cfl_variant *cm_otel_get_or_create_attributes(struct cfl_kvlist *kvlist) +{ + int ret; + struct cfl_list *head; + struct cfl_list *tmp; + struct cfl_kvpair *kvpair; + struct cfl_variant *val; + struct cfl_kvlist *kvlist_tmp; + + /* iterate resource to find the attributes field */ + cfl_list_foreach_safe(head, tmp, &kvlist->list) { + kvpair = cfl_list_entry(head, struct cfl_kvpair, _head); + if (cfl_sds_len(kvpair->key) != 10) { + continue; + } + + if (strncmp(kvpair->key, "attributes", 10) == 0) { + val = kvpair->val; + if (val->type != CFL_VARIANT_KVLIST) { + return NULL; + } + + return val; + } + } + + /* create an empty kvlist as the value of attributes */ + kvlist_tmp = cfl_kvlist_create(); + if (!kvlist_tmp) { + return NULL; + } + + /* create the attributes kvpair */ + ret = cfl_kvlist_insert_kvlist_s(kvlist, "attributes", 10, kvlist_tmp); + if (ret != 0) { + cfl_kvlist_destroy(kvlist_tmp); + return NULL; + } + + /* get the last kvpair from the list */ + kvpair = cfl_list_entry_last(&kvlist->list, struct cfl_kvpair, _head); + if (!kvpair) { + return NULL; + } + + return kvpair->val; +} + +static struct cfl_variant *otel_get_or_create_attributes(struct cfl_kvlist *kvlist) +{ + int ret; + struct cfl_list *head; + struct cfl_list *tmp; + struct cfl_kvpair *kvpair; + struct cfl_variant *val = NULL; + struct cfl_kvlist *kvlist_tmp; + + /* iterate resource to find the attributes field */ + cfl_list_foreach_safe(head, tmp, &kvlist->list) { + kvpair = cfl_list_entry(head, struct cfl_kvpair, _head); + if (cfl_sds_len(kvpair->key) != 10) { + continue; + } + + if (strncmp(kvpair->key, "attributes", 10) == 0) { + val = kvpair->val; + if (val->type != CFL_VARIANT_KVLIST) { + return NULL; + } + return val; + } + } + + /* create an empty kvlist as the value of attributes */ + kvlist_tmp = cfl_kvlist_create(); + if (!kvlist_tmp) { + return NULL; + } + + /* create the attributes kvpair */ + ret = cfl_kvlist_insert_kvlist_s(kvlist, "attributes", 10, kvlist_tmp); + if (ret != 0) { + cfl_kvlist_destroy(kvlist_tmp); + return NULL; + } + + /* get the last kvpair from the list */ + kvpair = cfl_list_entry_last(&kvlist->list, struct cfl_kvpair, _head); + if (!kvpair) { + return NULL; + } + + return kvpair->val; +} + +/* + * get attributes for resources and scope, context must be one of: + * + * - CM_CONTEXT_OTEL_RESOURCE_ATTR + * - CM_CONTEXT_OTEL_SCOPE_ATTR + */ +struct cfl_variant *cm_otel_get_attributes(int telemetry_type, int context, struct cfl_kvlist *kvlist) +{ + int key_len; + const char *key_buf; + struct cfl_variant *var; + struct cfl_variant *var_attr = NULL; + + if (context == CM_CONTEXT_OTEL_RESOURCE_ATTR) { + key_buf = "resource"; + key_len = 8; + } + else if (context == CM_CONTEXT_OTEL_SCOPE_ATTR) { + key_buf = "scope"; + key_len = 5; + } + else { + return NULL; + } + + var = cfl_kvlist_fetch_s(kvlist, (char *) key_buf, key_len); + if (!var) { + return NULL; + } + + if (var->type != CFL_VARIANT_KVLIST) { + return NULL; + } + + var_attr = otel_get_or_create_attributes(var->data.as_kvlist); + if (!var_attr) { + return NULL; + } + + return var_attr; +} + +static struct cfl_variant *otel_get_or_create_scope_metadata(int telemetry_type, struct cfl_kvlist *kvlist) +{ + int ret; + struct cfl_variant *var; + struct cfl_kvpair *kvpair; + struct cfl_kvlist *kvlist_tmp; + + /* kvlist is the value of 'scope', lookup for scope->metadata */ + + var = cfl_kvlist_fetch(kvlist, "metadata"); + if (var) { + if (var->type != CFL_VARIANT_KVLIST) { + return NULL; + } + + return var; + } + + /* metadata don't exists, create it */ + kvlist_tmp = cfl_kvlist_create(); + if (!kvlist_tmp) { + return NULL; + } + + ret = cfl_kvlist_insert_kvlist_s(kvlist, "metadata", 8, kvlist_tmp); + if (ret != 0) { + cfl_kvlist_destroy(kvlist_tmp); + return NULL; + } + + kvpair = cfl_list_entry_last(&kvlist->list, struct cfl_kvpair, _head); + if (!kvpair) { + return NULL; + } + + return kvpair->val; +} + +/* + * Retrieve the kvlist that contains the scope metadata such as name and version, + * based on the telemetry type, the kvlist is expected to be in the following format: + * + * - Logs: scope -> {name, version} + * - Metrics: scope -> metadata -> {name, version} + * + * If the paths are not found, those are "created". + */ +struct cfl_variant *cm_otel_get_scope_metadata(int telemetry_type, struct cfl_kvlist *kvlist) +{ + int ret; + struct cfl_variant *var = NULL; + struct cfl_kvpair *kvpair = NULL; + struct cfl_kvlist *kvlist_tmp; + + if (!kvlist) { + return NULL; + } + + /* retrieve the scope if exists */ + var = cfl_kvlist_fetch(kvlist, "scope"); + if (var) { + if (var->type != CFL_VARIANT_KVLIST) { + /* if exists and is not valid just fail */ + return NULL; + } + } + else { + /* create "scope" inside kvlist */ + kvlist_tmp = cfl_kvlist_create(); + if (!kvlist_tmp) { + return NULL; + } + + ret = cfl_kvlist_insert_kvlist_s(kvlist, "scope", 5, kvlist_tmp); + if (ret != 0) { + cfl_kvlist_destroy(kvlist_tmp); + return NULL; + } + + kvpair = cfl_list_entry_last(&kvlist->list, struct cfl_kvpair, _head); + if (!kvpair) { + return NULL; + } + + var = kvpair->val; + } + + /* + * 'var' points to the value of 'scope', for logs telemetry data, just return + * the current variant, for metrics lookup for 'metadata' kvpair (or create it) + */ + + if (telemetry_type == CM_TELEMETRY_LOGS) { + return var; + } + else if (telemetry_type == CM_TELEMETRY_METRICS) { + var = otel_get_or_create_scope_metadata(telemetry_type, var->data.as_kvlist); + } + + return var; +} diff --git a/plugins/processor_content_modifier/cm_opentelemetry.h b/plugins/processor_content_modifier/cm_opentelemetry.h new file mode 100644 index 00000000000..ddd8823f61b --- /dev/null +++ b/plugins/processor_content_modifier/cm_opentelemetry.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLB_PROCESSOR_CONTENT_MODIFIER_OTEL_H +#define FLB_PROCESSOR_CONTENT_MODIFIER_OTEL_H + +#include + +struct cfl_variant *cm_otel_get_attributes(int telemetry_type, int context, struct cfl_kvlist *kvlist); +struct cfl_variant *cm_otel_get_scope_metadata(int telemetry_type, struct cfl_kvlist *kvlist); + +#endif \ No newline at end of file diff --git a/plugins/processor_content_modifier/cm_traces.c b/plugins/processor_content_modifier/cm_traces.c index 24783ba37fc..54d4469bcb6 100644 --- a/plugins/processor_content_modifier/cm_traces.c +++ b/plugins/processor_content_modifier/cm_traces.c @@ -23,8 +23,6 @@ #include #include -//#include "variant_utils.h" - #include "cm.h" #include "cm_utils.h" @@ -117,9 +115,9 @@ static int span_convert_attribute(struct ctrace_span *span, return FLB_FALSE; } - ret = cfl_variant_convert(attribute, - &converted_attribute, - new_type); + ret = cm_utils_variant_convert(attribute, + &converted_attribute, + new_type); if (ret != FLB_TRUE) { return FLB_FALSE; @@ -262,107 +260,6 @@ static int context_contains_attribute(struct ctrace *traces_context, return FLB_FALSE; } -static int hex_encode(unsigned char *input_buffer, - size_t input_length, - cfl_sds_t *output_buffer) -{ - const char hex[] = "0123456789abcdef"; - cfl_sds_t result; - size_t index; - - if (cfl_sds_alloc(*output_buffer) <= (input_length * 2)) { - result = cfl_sds_increase(*output_buffer, - (input_length * 2) - - cfl_sds_alloc(*output_buffer)); - - if (result == NULL) { - return FLB_FALSE; - } - - *output_buffer = result; - } - - for (index = 0; index < input_length; index++) { - (*output_buffer)[index * 2 + 0] = hex[(input_buffer[index] >> 4) & 0xF]; - (*output_buffer)[index * 2 + 1] = hex[(input_buffer[index] >> 0) & 0xF]; - } - - cfl_sds_set_len(*output_buffer, input_length * 2); - - (*output_buffer)[index * 2] = '\0'; - - return FLB_TRUE; -} - -static int hash_transformer(void *context, struct cfl_variant *value) -{ - unsigned char digest_buffer[32]; - struct cfl_variant *converted_value; - cfl_sds_t encoded_hash; - int result; - - if (value == NULL) { - return FLB_FALSE; - } - - result = cfl_variant_convert(value, - &converted_value, - CFL_VARIANT_STRING); - - if (result != FLB_TRUE) { - return FLB_FALSE; - } - - if (cfl_sds_len(converted_value->data.as_string) == 0) { - cfl_variant_destroy(converted_value); - return FLB_TRUE; - } - - result = flb_hash_simple(FLB_HASH_SHA256, - (unsigned char *) converted_value->data.as_string, - cfl_sds_len(converted_value->data.as_string), - digest_buffer, - sizeof(digest_buffer)); - - if (result != FLB_CRYPTO_SUCCESS) { - cfl_variant_destroy(converted_value); - return FLB_FALSE; - } - - result = hex_encode(digest_buffer, - sizeof(digest_buffer), - &converted_value->data.as_string); - - if (result != FLB_TRUE) { - cfl_variant_destroy(converted_value); - return FLB_FALSE; - } - - encoded_hash = cfl_sds_create(converted_value->data.as_string); - if (encoded_hash == NULL) { - cfl_variant_destroy(converted_value); - return FLB_FALSE; - } - cfl_variant_destroy(converted_value); - - - if (value->type == CFL_VARIANT_STRING || - value->type == CFL_VARIANT_BYTES) { - cfl_sds_destroy(value->data.as_string); - } - else if (value->type == CFL_VARIANT_ARRAY) { - cfl_array_destroy(value->data.as_array); - } - else if (value->type == CFL_VARIANT_KVLIST) { - cfl_kvlist_destroy(value->data.as_kvlist); - } - - value->type = CFL_VARIANT_STRING; - value->data.as_string = encoded_hash; - - return FLB_TRUE; -} - static int traces_context_hash_attribute(struct ctrace *traces_context, char *name) { @@ -374,7 +271,7 @@ static int traces_context_hash_attribute(struct ctrace *traces_context, struct ctrace_span, _head_global); if (span_contains_attribute(span, name) == FLB_TRUE) { - if (span_transform_attribute(span, name, hash_transformer) != FLB_TRUE) { + if (span_transform_attribute(span, name, cm_utils_hash_transformer) != FLB_TRUE) { return FLB_FALSE; } } diff --git a/plugins/processor_content_modifier/cm_utils.c b/plugins/processor_content_modifier/cm_utils.c new file mode 100644 index 00000000000..7d762f6c4bc --- /dev/null +++ b/plugins/processor_content_modifier/cm_utils.c @@ -0,0 +1,357 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "cm.h" +#include "cm_utils.h" + +#include +static int hex_encode(unsigned char *input_buffer, size_t input_length, cfl_sds_t *output_buffer) +{ + const char hex[] = "0123456789abcdef"; + cfl_sds_t result; + size_t index; + + if (cfl_sds_alloc(*output_buffer) <= (input_length * 2)) { + result = cfl_sds_increase(*output_buffer, + (input_length * 2) - + cfl_sds_alloc(*output_buffer)); + + if (result == NULL) { + return FLB_FALSE; + } + + *output_buffer = result; + } + + for (index = 0; index < input_length; index++) { + (*output_buffer)[index * 2 + 0] = hex[(input_buffer[index] >> 4) & 0xF]; + (*output_buffer)[index * 2 + 1] = hex[(input_buffer[index] >> 0) & 0xF]; + } + + cfl_sds_set_len(*output_buffer, input_length * 2); + + (*output_buffer)[index * 2] = '\0'; + + return FLB_TRUE; +} + + +int cm_utils_hash_transformer(void *context, struct cfl_variant *value) +{ + unsigned char digest_buffer[32]; + struct cfl_variant *converted_value; + cfl_sds_t encoded_hash; + int result; + + if (value == NULL) { + return FLB_FALSE; + } + + result = cm_utils_variant_convert(value, + &converted_value, + CFL_VARIANT_STRING); + + if (result != FLB_TRUE) { + return FLB_FALSE; + } + + if (cfl_variant_size_get(converted_value) == 0) { + cfl_variant_destroy(converted_value); + return FLB_TRUE; + } + + result = flb_hash_simple(FLB_HASH_SHA256, + (unsigned char *) converted_value->data.as_string, + cfl_sds_len(converted_value->data.as_string), + digest_buffer, + sizeof(digest_buffer)); + + if (result != FLB_CRYPTO_SUCCESS) { + cfl_variant_destroy(converted_value); + + return FLB_FALSE; + } + + result = hex_encode(digest_buffer, + sizeof(digest_buffer), + &converted_value->data.as_string); + + if (result != FLB_TRUE) { + cfl_variant_destroy(converted_value); + + return FLB_FALSE; + } + + encoded_hash = cfl_sds_create(converted_value->data.as_string); + cfl_variant_destroy(converted_value); + if (encoded_hash == NULL) { + return FLB_FALSE; + } + + /* NOTE: this part does a manual modification of the variant content */ + if (value->type == CFL_VARIANT_STRING || + value->type == CFL_VARIANT_BYTES) { + if (value->referenced == CFL_FALSE) { + cfl_sds_destroy(value->data.as_string); + } + } + else if (value->type == CFL_VARIANT_ARRAY) { + cfl_array_destroy(value->data.as_array); + } + else if (value->type == CFL_VARIANT_KVLIST) { + cfl_kvlist_destroy(value->data.as_kvlist); + } + + value->type = CFL_VARIANT_STRING; + value->data.as_string = encoded_hash; + value->referenced = CFL_FALSE; + + cfl_variant_size_set(value, cfl_sds_len(encoded_hash)); + + return FLB_TRUE; +} + +cfl_sds_t cm_utils_variant_convert_to_json(struct cfl_variant *value) +{ + cfl_sds_t json_result; + mpack_writer_t writer; + char *data; + size_t size; + + data = NULL; + size = 0; + + mpack_writer_init_growable(&writer, &data, &size); + + pack_cfl_variant(&writer, value); + + mpack_writer_destroy(&writer); + + json_result = flb_msgpack_raw_to_json_sds(data, size); + + return json_result; +} + +int cm_utils_variant_convert(struct cfl_variant *input_value, + struct cfl_variant **output_value, + int output_type) +{ + int ret; + int errno_backup; + int64_t as_int; + double as_double; + char buf[64]; + char *str = NULL; + char *converstion_canary = NULL; + struct cfl_variant *tmp = NULL; + + errno_backup = errno; + + /* input: string, bytes or reference */ + if (input_value->type == CFL_VARIANT_STRING || input_value->type == CFL_VARIANT_BYTES || + input_value->type == CFL_VARIANT_REFERENCE) { + + if (output_type == CFL_VARIANT_STRING || + output_type == CFL_VARIANT_BYTES) { + + tmp = cfl_variant_create_from_string_s(input_value->data.as_string, + cfl_variant_size_get(input_value), + CFL_FALSE); + if (!tmp) { + return CFL_FALSE; + } + } + else if (output_type == CFL_VARIANT_BOOL) { + as_int = CFL_FALSE; + + if (cfl_variant_size_get(input_value) == 4 && + strncasecmp(input_value->data.as_string, "true", 4) == 0) { + as_int = CFL_TRUE; + } + else if (cfl_variant_size_get(input_value) == 5 && + strncasecmp(input_value->data.as_string, "false", 5) == 0) { + as_int = CFL_FALSE; + } + + tmp = cfl_variant_create_from_bool(as_int); + } + else if (output_type == CFL_VARIANT_INT) { + errno = 0; + + if (input_value->referenced) { + tmp = cfl_variant_create_from_string_s(input_value->data.as_string, + cfl_variant_size_get(input_value), + CFL_FALSE); + if (!tmp) { + return CFL_FALSE; + } + str = tmp->data.as_string; + } + else { + str = input_value->data.as_string; + } + + as_int = strtoimax(str, &converstion_canary, 10); + if (errno == ERANGE || errno == EINVAL) { + errno = errno_backup; + if (tmp) { + cfl_variant_destroy(tmp); + } + return CFL_FALSE; + } + + if (tmp) { + cfl_variant_destroy(tmp); + } + + tmp = cfl_variant_create_from_int64(as_int); + } + else if (output_type == CFL_VARIANT_DOUBLE) { + errno = 0; + converstion_canary = NULL; + + if (input_value->referenced) { + tmp = cfl_variant_create_from_string_s(input_value->data.as_string, + cfl_variant_size_get(input_value), + CFL_FALSE); + if (!tmp) { + return CFL_FALSE; + } + str = tmp->data.as_string; + } + else { + str = input_value->data.as_string; + } + + as_double = strtod(str, &converstion_canary); + if (errno == ERANGE) { + errno = errno_backup; + if (tmp) { + cfl_variant_destroy(tmp); + } + return CFL_FALSE; + } + + if (tmp) { + cfl_variant_destroy(tmp); + } + + if (as_double == 0 && converstion_canary == input_value->data.as_string) { + errno = errno_backup; + return CFL_FALSE; + } + + tmp = cfl_variant_create_from_double(as_double); + } + else { + return CFL_FALSE; + } + } + /* input: int */ + else if (input_value->type == CFL_VARIANT_INT) { + if (output_type == CFL_VARIANT_STRING || output_type == CFL_VARIANT_BYTES) { + ret = snprintf(buf, sizeof(buf), "%" PRIi64, input_value->data.as_int64); + if (ret < 0 || ret >= sizeof(buf)) { + return CFL_FALSE; + } + tmp = cfl_variant_create_from_string_s(buf, ret, CFL_FALSE); + } + else if (output_type == CFL_VARIANT_BOOL) { + as_int = CFL_FALSE; + if (input_value->data.as_int64 != 0) { + as_int = CFL_TRUE; + } + + tmp = cfl_variant_create_from_bool(as_int); + } + else if (output_type == CFL_VARIANT_INT) { + /* same type, do nothing */ + } + else if (output_type == CFL_VARIANT_DOUBLE) { + as_double = (double) input_value->data.as_int64; + tmp = cfl_variant_create_from_double(as_double); + } + else { + return CFL_FALSE; + } + } + else if (input_value->type == CFL_VARIANT_DOUBLE) { + if (output_type == CFL_VARIANT_STRING || + output_type == CFL_VARIANT_BYTES) { + + ret = snprintf(buf, sizeof(buf), "%.17g", input_value->data.as_double); + if (ret < 0 || ret >= sizeof(buf)) { + return CFL_FALSE; + } + tmp = cfl_variant_create_from_string_s(buf, ret, CFL_FALSE); + } + else if (output_type == CFL_VARIANT_BOOL) { + as_int = CFL_FALSE; + + if (input_value->data.as_double != 0) { + as_int = CFL_TRUE; + } + + tmp = cfl_variant_create_from_bool(as_int); + } + else if (output_type == CFL_VARIANT_INT) { + as_int = (int64_t) round(input_value->data.as_double); + tmp = cfl_variant_create_from_int64(as_int); + } + else if (output_type == CFL_VARIANT_DOUBLE) { + as_double = input_value->data.as_int64; + tmp = cfl_variant_create_from_double(as_double); + } + else { + return CFL_FALSE; + } + } + else if (input_value->type == CFL_VARIANT_NULL) { + if (output_type == CFL_VARIANT_STRING || + output_type == CFL_VARIANT_BYTES) { + + tmp = cfl_variant_create_from_string_s("null", 4, CFL_FALSE); + } + else if (output_type == CFL_VARIANT_BOOL) { + tmp = cfl_variant_create_from_bool(CFL_FALSE); + } + else if (output_type == CFL_VARIANT_INT) { + tmp = cfl_variant_create_from_int64(0); + } + else if (output_type == CFL_VARIANT_DOUBLE) { + tmp = cfl_variant_create_from_double(0); + } + else { + return CFL_FALSE; + } + } + else { + return CFL_FALSE; + } + + *output_value = tmp; + return FLB_TRUE; +} diff --git a/plugins/processor_content_modifier/cm_utils.h b/plugins/processor_content_modifier/cm_utils.h index 7116172603c..bec26741b07 100644 --- a/plugins/processor_content_modifier/cm_utils.h +++ b/plugins/processor_content_modifier/cm_utils.h @@ -629,5 +629,10 @@ int cfl_variant_convert(struct cfl_variant *input_value, int output_type); +int cm_utils_hash_transformer(void *context, struct cfl_variant *value); +cfl_sds_t cm_utils_variant_convert_to_json(struct cfl_variant *value); +int cm_utils_variant_convert(struct cfl_variant *input_value, + struct cfl_variant **output_value, + int output_type); #endif