diff --git a/include/fluent-bit/flb_opentelemetry.h b/include/fluent-bit/flb_opentelemetry.h index 8919f0ab5a8..cc521862dfd 100644 --- a/include/fluent-bit/flb_opentelemetry.h +++ b/include/fluent-bit/flb_opentelemetry.h @@ -24,8 +24,9 @@ #include #include -/* Error code values from flb_opentelemetry_logs.c */ +/* Error code values from flb_opentelemetry logs/traces helpers */ #define FLB_OTEL_LOGS_ERR_GENERIC_ERROR -1 +#define FLB_OTEL_TRACES_ERR_GENERIC_ERROR -1 enum { @@ -50,7 +51,31 @@ enum { FLB_OTEL_LOGS_ERR_ENCODER_FAILURE, FLB_OTEL_LOGS_ERR_APPEND_BODY_FAILURE, FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID, - FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID + FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID, + + /* trace specific errors */ + FLB_OTEL_TRACES_ERR_UNEXPECTED_ROOT_OBJECT_TYPE, + FLB_OTEL_TRACES_ERR_INVALID_JSON, + FLB_OTEL_TRACES_ERR_RESOURCE_SPANS_MISSING, + FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_TYPE, + FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_ENTRY_TYPE, + FLB_OTEL_TRACES_ERR_SCOPE_SPANS_MISSING, + FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_TYPE, + FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_ENTRY_TYPE, + FLB_OTEL_TRACES_ERR_SPANS_MISSING, + FLB_OTEL_TRACES_ERR_UNEXPECTED_SPANS_TYPE, + FLB_OTEL_TRACES_ERR_UNEXPECTED_SPAN_ENTRY_TYPE, + FLB_OTEL_TRACES_ERR_SPAN_NAME_MISSING, + FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES, + FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID, + FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID, + FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID, + FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY, + FLB_OTEL_TRACES_ERR_INVALID_EVENT_TIMESTAMP, + FLB_OTEL_TRACES_ERR_INVALID_LINK_ENTRY, + FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID, + FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID, + FLB_OTEL_TRACES_ERR_STATUS_FAILURE }; @@ -88,7 +113,31 @@ static struct flb_otel_error_map otel_error_map[] = { {"FLB_OTEL_LOGS_ERR_APPEND_BODY_FAILURE", FLB_OTEL_LOGS_ERR_APPEND_BODY_FAILURE}, {"FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID", FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID}, {"FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID", FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID}, + + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_ROOT_OBJECT_TYPE", FLB_OTEL_TRACES_ERR_UNEXPECTED_ROOT_OBJECT_TYPE}, + {"FLB_OTEL_TRACES_ERR_INVALID_JSON", FLB_OTEL_TRACES_ERR_INVALID_JSON}, + {"FLB_OTEL_TRACES_ERR_RESOURCE_SPANS_MISSING", FLB_OTEL_TRACES_ERR_RESOURCE_SPANS_MISSING}, + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_TYPE", FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_TYPE}, + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_ENTRY_TYPE", FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_ENTRY_TYPE}, + {"FLB_OTEL_TRACES_ERR_SCOPE_SPANS_MISSING", FLB_OTEL_TRACES_ERR_SCOPE_SPANS_MISSING}, + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_TYPE", FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_TYPE}, + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_ENTRY_TYPE",FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_ENTRY_TYPE}, + {"FLB_OTEL_TRACES_ERR_SPANS_MISSING", FLB_OTEL_TRACES_ERR_SPANS_MISSING}, + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_SPANS_TYPE", FLB_OTEL_TRACES_ERR_UNEXPECTED_SPANS_TYPE}, + {"FLB_OTEL_TRACES_ERR_UNEXPECTED_SPAN_ENTRY_TYPE", FLB_OTEL_TRACES_ERR_UNEXPECTED_SPAN_ENTRY_TYPE}, + {"FLB_OTEL_TRACES_ERR_SPAN_NAME_MISSING", FLB_OTEL_TRACES_ERR_SPAN_NAME_MISSING}, + {"FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES", FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES}, + {"FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID", FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID}, + {"FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID", FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID}, + {"FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID", FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID}, + {"FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY", FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY}, + {"FLB_OTEL_TRACES_ERR_INVALID_EVENT_TIMESTAMP", FLB_OTEL_TRACES_ERR_INVALID_EVENT_TIMESTAMP}, + {"FLB_OTEL_TRACES_ERR_INVALID_LINK_ENTRY", FLB_OTEL_TRACES_ERR_INVALID_LINK_ENTRY}, + {"FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID", FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID}, + {"FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID", FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID}, + {"FLB_OTEL_TRACES_ERR_STATUS_FAILURE", FLB_OTEL_TRACES_ERR_STATUS_FAILURE}, {"GENERIC_ERROR", FLB_OTEL_LOGS_ERR_GENERIC_ERROR}, + {"FLB_OTEL_TRACES_ERR_GENERIC_ERROR", FLB_OTEL_TRACES_ERR_GENERIC_ERROR}, /* ---- */ {"FLB_OTEL_LOGS_ERR_EMPTY_PAYLOAD", FLB_OTEL_LOGS_ERR_EMPTY_PAYLOAD}, @@ -124,6 +173,9 @@ int flb_opentelemetry_logs_json_to_msgpack(struct flb_log_event_encoder *encoder const char *logs_body_key, int *error_status); +struct ctrace *flb_opentelemetry_json_traces_to_ctrace(const char *body, size_t len, + int *error_status); + /* OpenTelemetry utils */ int flb_otel_utils_find_map_entry_by_key(msgpack_object_map *map, char *key, diff --git a/plugins/in_opentelemetry/opentelemetry_traces.c b/plugins/in_opentelemetry/opentelemetry_traces.c index 0e8e61b6a15..75fa1c7b8fd 100644 --- a/plugins/in_opentelemetry/opentelemetry_traces.c +++ b/plugins/in_opentelemetry/opentelemetry_traces.c @@ -20,13 +20,13 @@ #include #include #include +#include #include #include #include "opentelemetry.h" #include "opentelemetry_traces.h" -#include "opentelemetry_utils.h" int opentelemetry_traces_process_protobuf(struct flb_opentelemetry *ctx, flb_sds_t tag, @@ -51,1092 +51,29 @@ int opentelemetry_traces_process_protobuf(struct flb_opentelemetry *ctx, return result; } -static int process_attribute(struct ctrace_attributes *attr, - msgpack_object *key, msgpack_object *value, int type) -{ - int ret; - char *key_str; - char *value_str; - int64_t value_int; - double value_double; - int value_bool; - - if (key->type != MSGPACK_OBJECT_STR) { - return -1; - } - - /* temporary buffer for key since it needs to be NULL terminated */ - key_str = flb_sds_create_len(key->via.str.ptr, key->via.str.size); - if (key_str == NULL) { - return -1; - } - - /* - * the value of 'type' is set by the JSON wrapped value, we need to to convert it - * since msgpack_object *value is always a string - */ - switch (type) { - case MSGPACK_OBJECT_STR: - if (value->type != MSGPACK_OBJECT_STR) { - flb_sds_destroy(key_str); - return -1; - } - - value_str = flb_sds_create_len(value->via.str.ptr, value->via.str.size); - if (value_str == NULL) { - flb_sds_destroy(key_str); - return -1; - } - - ret = ctr_attributes_set_string(attr, key_str, value_str); - flb_sds_destroy(value_str); - break; - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - if (value->type == MSGPACK_OBJECT_STR) { - value_str = flb_sds_create_len(value->via.str.ptr, value->via.str.size); - if (value_str == NULL) { - flb_sds_destroy(key_str); - return -1; - } - value_int = strtoll(value_str, NULL, 10); - flb_sds_destroy(value_str); - } - else if (value->type == MSGPACK_OBJECT_POSITIVE_INTEGER || - value->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { - value_int = value->via.i64; - } - else { - flb_sds_destroy(key_str); - return -1; - } - - ret = ctr_attributes_set_int64(attr, key_str, value_int); - break; - case MSGPACK_OBJECT_FLOAT32: - case MSGPACK_OBJECT_FLOAT64: - if (value->type == MSGPACK_OBJECT_STR) { - value_str = flb_sds_create_len(value->via.str.ptr, value->via.str.size); - if (value_str == NULL) { - flb_sds_destroy(key_str); - return -1; - } - value_double = strtod(value_str, NULL); - flb_sds_destroy(value_str); - } - else if (value->type == MSGPACK_OBJECT_FLOAT32 || - value->type == MSGPACK_OBJECT_FLOAT64) { - value_double = value->via.f64; - } - else { - flb_sds_destroy(key_str); - return -1; - } - - ret = ctr_attributes_set_double(attr, key_str, value_double); - break; - case MSGPACK_OBJECT_BOOLEAN: - if (value->type != MSGPACK_OBJECT_BOOLEAN) { - flb_sds_destroy(key_str); - return -1; - } - - value_bool = value->via.boolean; - ret = ctr_attributes_set_bool(attr, key_str, value_bool); - break; - case MSGPACK_OBJECT_ARRAY: - /* - * The less fun part (OTel JSON encoding), the value can be an array and - * only allows values such as string, bool, int64, double. I am glad this - * don't support nested arrays or maps. - */ - ret = 0; - break; - default: - flb_sds_destroy(key_str); - return -1; - } - - flb_sds_destroy(key_str); - return ret; -} - -static int process_resource_unwrap_attribute(msgpack_object *attr, - msgpack_object *out_key, - msgpack_object *out_value, int *out_value_type) -{ - int ret; - int type; - msgpack_object key; - msgpack_object val; - msgpack_object *real_value; - - if (attr->type != MSGPACK_OBJECT_MAP) { - return -1; - } - - ret = find_map_entry_by_key(&attr->via.map, "key", 0, FLB_TRUE); - if (ret == -1) { - return -1; - } - - key = attr->via.map.ptr[ret].val; - if (key.type != MSGPACK_OBJECT_STR) { - return -1; - } - - ret = find_map_entry_by_key(&attr->via.map, "value", 0, FLB_TRUE); - if (ret == -1) { - return -1; - } - - val = attr->via.map.ptr[ret].val; - - ret = json_payload_get_wrapped_value(&val, &real_value, &type); - if (ret != 0) { - return -1; - } - - *out_key = key; - *out_value = *real_value; - *out_value_type = type; - - return 0; -} - -/* - * Convert a list of attributes in msgpack format to a cfl attributes by - * unwrapping JSON encoded value types. - */ -static struct ctrace_attributes *convert_attributes(struct flb_opentelemetry *ctx, - msgpack_object *attributes, - char *log_context) -{ - int i; - int ret; - int value_type; - msgpack_object key; - msgpack_object value; - struct ctrace_attributes *attr; - - attr = ctr_attributes_create(); - if (attr == NULL) { - return NULL; - } - - for (i = 0; i < attributes->via.array.size; i++) { - ret = process_resource_unwrap_attribute(&attributes->via.array.ptr[i], - &key, &value, &value_type); - if (ret == -1) { - flb_plg_warn(ctx->ins, "found invalid %s attribute, skipping", - log_context); - continue; - } - - /* set attribute */ - ret = process_attribute(attr, &key, &value, value_type); - if (ret == -1) { - flb_plg_warn(ctx->ins, "failed to set %s attribute, skipping", - log_context); - continue; - } - } - - return attr; -} - -static int process_resource_attributes(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_resource_span *resource_span, - msgpack_object *attributes) - -{ - struct ctrace_resource *resource; - struct ctrace_attributes *attr; - - attr = convert_attributes(ctx, attributes, "trace resource"); - if (!attr) { - return -1; - } - - resource = ctr_resource_span_get_resource(resource_span); - ctr_resource_set_attributes(resource, attr); - - return 0; -} - -static int process_scope_attributes(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_scope_span *scope_span, - msgpack_object *name, - msgpack_object *version, - msgpack_object *attributes, - msgpack_object *dropped_attributes_count) - -{ - int dropped = 0; - cfl_sds_t name_str = NULL; - cfl_sds_t version_str = NULL; - struct ctrace_attributes *attr = NULL; - struct ctrace_instrumentation_scope *ins_scope; - - if (attributes) { - attr = convert_attributes(ctx, attributes, "trace scope"); - if (!attr) { - return -1; - } - } - - if (name) { - name_str = cfl_sds_create_len(name->via.str.ptr, name->via.str.size); - } - if (version) { - version_str = cfl_sds_create_len(version->via.str.ptr, version->via.str.size); - } - - if (dropped_attributes_count) { - dropped = dropped_attributes_count->via.u64; - } - - ins_scope = ctr_instrumentation_scope_create(name_str, version_str, dropped, attr); - if (!ins_scope) { - if (name_str) { - cfl_sds_destroy(name_str); - } - if (version_str) { - cfl_sds_destroy(version_str); - } - if (attr) { - ctr_attributes_destroy(attr); - } - return -1; - } - - if (name_str) { - cfl_sds_destroy(name_str); - } - if (version_str) { - cfl_sds_destroy(version_str); - } - - ctr_scope_span_set_instrumentation_scope(scope_span, ins_scope); - return 0; -} - -static int process_events(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_span *span, - msgpack_object *events) -{ - int i; - int ret; - int len; - uint64_t ts = 0; - char tmp[64]; - cfl_sds_t name_str = NULL; - msgpack_object event; - msgpack_object *name = NULL; - msgpack_object *attr = NULL; - struct ctrace_span_event *ctr_event = NULL; - struct ctrace_attributes *ctr_attr = NULL; - - for (i = 0; i < events->via.array.size; i++) { - event = events->via.array.ptr[i]; - if (event.type != MSGPACK_OBJECT_MAP) { - flb_plg_error(ctx->ins, "unexpected event type"); - return -1; - } - - name_str = NULL; - ts = 0; - - /* name */ - ret = find_map_entry_by_key(&event.via.map, "name", 0, FLB_TRUE); - if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - name = &event.via.map.ptr[ret].val; - name_str = cfl_sds_create_len(name->via.str.ptr, name->via.str.size); - if (name_str == NULL) { - return -1; - } - } - - if (!name_str) { - flb_plg_warn(ctx->ins, "span event name is missing"); - return -1; - } - - /* time_unix_nano */ - ret = find_map_entry_by_key(&event.via.map, "timeUnixNano", 0, FLB_TRUE); - if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - /* convert to uint64_t */ - len = event.via.map.ptr[ret].val.via.str.size; - if (len > sizeof(tmp) - 1) { - len = sizeof(tmp) - 1; - memcpy(tmp, event.via.map.ptr[ret].val.via.str.ptr, len); - tmp[len] = '\0'; - - flb_plg_error(ctx->ins, "invalid timeUnixNano: '%s'", tmp); - if (name_str) { - cfl_sds_destroy(name_str); - } - return -1; - } - - memcpy(tmp, event.via.map.ptr[ret].val.via.str.ptr, len); - tmp[len] = '\0'; - - ts = strtoull(tmp, NULL, 10); - } - - ctr_event = ctr_span_event_add_ts(span, name_str, ts); - cfl_sds_destroy(name_str); - if (ctr_event == NULL) { - return -1; - } - - /* attributes */ - ret = find_map_entry_by_key(&event.via.map, "attributes", 0, FLB_TRUE); - if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - attr = &event.via.map.ptr[ret].val; - ctr_attr = convert_attributes(ctx, attr, "span event"); - if (ctr_attr) { - ctr_span_event_set_attributes(ctr_event, ctr_attr); - } - } - - /* dropped_attributes_count */ - ret = find_map_entry_by_key(&event.via.map, "droppedAttributesCount", 0, FLB_FALSE); - if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_span_event_set_dropped_attributes_count(ctr_event, event.via.map.ptr[ret].val.via.u64); - } - } - - return 0; -} - -static int process_links(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_span *span, - msgpack_object *links) -{ - int i; - int ret; - int len; - char tmp[64]; - char trace_id_bin[16]; - char span_id_bin[8]; - cfl_sds_t buf; - msgpack_object link; - msgpack_object *trace_id = NULL; - msgpack_object *span_id = NULL; - msgpack_object *trace_state = NULL; - msgpack_object *dropped_attr_count = NULL; - msgpack_object *flags = NULL; - msgpack_object *attr = NULL; - - struct ctrace_link *ctr_link = NULL; - struct ctrace_attributes *ctr_attr = NULL; - - for (i = 0; i < links->via.array.size; i++) { - link = links->via.array.ptr[i]; - if (link.type != MSGPACK_OBJECT_MAP) { - flb_plg_error(ctx->ins, "unexpected link type"); - return -1; - } - trace_id = NULL; - span_id = NULL; - trace_state = NULL; - dropped_attr_count = NULL; - flags = NULL; - ctr_attr = NULL; - /* traceId */ - ret = find_map_entry_by_key(&link.via.map, "traceId", 0, FLB_TRUE); - if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - trace_id = &link.via.map.ptr[ret].val; - /* trace_id is an hex of 32 bytes */ - if (trace_id->via.str.size != 32) { - len = trace_id->via.str.size; - if (len > sizeof(tmp) - 1) { - len = sizeof(tmp) - 1; - } - memcpy(tmp, trace_id->via.str.ptr, len); - tmp[len] = '\0'; - - flb_plg_error(ctx->ins, "invalid event traceId: '%s'", tmp); - return -1; - } - - /* decode the hex string (16 bytes) */ - hex_to_id((char *) trace_id->via.str.ptr, trace_id->via.str.size, - (unsigned char *) trace_id_bin, 16); - } - - if (!trace_id) { - flb_plg_error(ctx->ins, "link traceId is missing"); - return -1; - } - - /* spanId */ - ret = find_map_entry_by_key(&link.via.map, "spanId", 0, FLB_TRUE); - if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - span_id = &link.via.map.ptr[ret].val; - - /* span_id is an hex of 16 bytes */ - if (span_id->via.str.size != 16) { - len = span_id->via.str.size; - if (len > sizeof(tmp) - 1) { - len = sizeof(tmp) - 1; - } - memcpy(tmp, span_id->via.str.ptr, len); - tmp[len] = '\0'; - flb_plg_error(ctx->ins, "invalid spanId: '%s'", tmp); - return -1; - } - - /* decode the hex string (8 bytes) */ - memset(tmp, '\0', sizeof(tmp)); - hex_to_id((char *) span_id->via.str.ptr, span_id->via.str.size, - (unsigned char *) span_id_bin, 8); - } - - if (!span_id) { - flb_plg_error(ctx->ins, "link spanId is missing"); - return -1; - } - - /* traceState */ - ret = find_map_entry_by_key(&link.via.map, "traceState", 0, FLB_FALSE); - if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - trace_state = &link.via.map.ptr[ret].val; - } - - /* attributes */ - ret = find_map_entry_by_key(&link.via.map, "attributes", 0, FLB_FALSE); - if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - attr = &link.via.map.ptr[ret].val; - ctr_attr = convert_attributes(ctx, attr, "event link"); - } - - /* droped_attributes_count */ - ret = find_map_entry_by_key(&link.via.map, "droppedAttributesCount", 0, FLB_FALSE); - if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - dropped_attr_count = &link.via.map.ptr[ret].val; - } - - /* flags */ - ret = find_map_entry_by_key(&link.via.map, "flags", 0, FLB_FALSE); - if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - flags = &link.via.map.ptr[ret].val; - } - - ctr_link = ctr_link_create(span, - trace_id_bin, 16, - span_id_bin, 8); - - if (ctr_link == NULL) { - if (ctr_attr) { - ctr_attributes_destroy(ctr_attr); - } - return -1; - } - - if (trace_state) { - buf = cfl_sds_create_len(trace_state->via.str.ptr, trace_state->via.str.size); - if (buf) { - ctr_link_set_trace_state(ctr_link, buf); - cfl_sds_destroy(buf); - } - } - - if (ctr_attr) { - ctr_link_set_attributes(ctr_link, ctr_attr); - } - - if (dropped_attr_count) { - ctr_link_set_dropped_attr_count(ctr_link, dropped_attr_count->via.u64); - } - - if (flags) { - ctr_link_set_flags(ctr_link, flags->via.u64); - } - } - - return 0; -} - -static int process_span_status(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_span *span, - msgpack_object *status) -{ - int ret; - int code = 0; - cfl_sds_t tmp = NULL; - char *message = NULL; - - if (status->type != MSGPACK_OBJECT_MAP) { - flb_plg_error(ctx->ins, "unexpected status type"); - return -1; - } - - /* code */ - ret = find_map_entry_by_key(&status->via.map, "code", 0, FLB_TRUE); - if (ret >= 0 && status->via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - tmp = cfl_sds_create_len(status->via.map.ptr[ret].val.via.str.ptr, - status->via.map.ptr[ret].val.via.str.size); - if (!tmp) { - return -1; - } - - if (strcasecmp(tmp, "UNSET") == 0) { - code = CTRACE_SPAN_STATUS_CODE_UNSET; - } - else if (strcasecmp(tmp, "OK") == 0) { - code = CTRACE_SPAN_STATUS_CODE_OK; - } - else if (strcasecmp(tmp, "ERROR") == 0) { - code = CTRACE_SPAN_STATUS_CODE_ERROR; - } - else { - flb_plg_error(ctx->ins, "status code value is invalid: %s", tmp); - cfl_sds_destroy(tmp); - return -1; - } - cfl_sds_destroy(tmp); - } - else { - flb_plg_error(ctx->ins, "status code is missing"); - return -1; - } - - /* message */ - ret = find_map_entry_by_key(&status->via.map, "message", 0, FLB_FALSE); - if (ret >= 0 && status->via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - message = flb_sds_create_len(status->via.map.ptr[ret].val.via.str.ptr, - status->via.map.ptr[ret].val.via.str.size); - } - - ctr_span_set_status(span, code, message); - if (message) { - flb_sds_destroy(message); - } - - return 0; -} - -static int process_spans(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_scope_span *scope_span, - msgpack_object *spans) -{ - int i; - int ret; - int len; - uint64_t val; - char tmp[64]; - cfl_sds_t val_str = NULL; - msgpack_object span; - msgpack_object *name = NULL; - msgpack_object *attr = NULL; - struct ctrace_span *ctr_span = NULL; - struct ctrace_attributes *ctr_attr = NULL; - - for (i = 0; i < spans->via.array.size; i++) { - span = spans->via.array.ptr[i]; - if (span.type != MSGPACK_OBJECT_MAP) { - flb_plg_error(ctx->ins, "unexpected span type"); - return -1; - } - - val_str = NULL; - - /* name */ - ret = find_map_entry_by_key(&span.via.map, "name", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - name = &span.via.map.ptr[ret].val; - val_str = cfl_sds_create_len(name->via.str.ptr, name->via.str.size); - } - - if (!val_str) { - flb_plg_error(ctx->ins, "span name is missing"); - return -1; - } - - /* create the span */ - ctr_span = ctr_span_create(ctr, scope_span, val_str, NULL); - cfl_sds_destroy(val_str); - - if (ctr_span == NULL) { - return -1; - } - - /* traceId */ - ret = find_map_entry_by_key(&span.via.map, "traceId", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - /* trace_id is an hex of 32 bytes */ - if (span.via.map.ptr[ret].val.via.str.size != 32) { - len = span.via.map.ptr[ret].val.via.str.size; - if (len > sizeof(tmp) - 1) { - len = sizeof(tmp) - 1; - } - memcpy(tmp, span.via.map.ptr[ret].val.via.str.ptr, len); - tmp[len] = '\0'; - - flb_plg_error(ctx->ins, "invalid traceId: '%s'", tmp); - return -1; - } - - /* decode the hex string (16 bytes) */ - memset(tmp, '\0', sizeof(tmp)); - hex_to_id((char *) span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size, - (unsigned char *) tmp, 16); - ctr_span_set_trace_id(ctr_span, tmp, 16); - } - - /* spanId */ - ret = find_map_entry_by_key(&span.via.map, "spanId", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - /* span_id is an hex of 16 bytes */ - if (span.via.map.ptr[ret].val.via.str.size != 16) { - len = span.via.map.ptr[ret].val.via.str.size; - if (len > sizeof(tmp) - 1) { - len = sizeof(tmp) - 1; - } - memcpy(tmp, span.via.map.ptr[ret].val.via.str.ptr, len); - tmp[len] = '\0'; - flb_plg_error(ctx->ins, "invalid spanId: '%s'", tmp); - return -1; - } - - /* decode the hex string (8 bytes) */ - memset(tmp, '\0', sizeof(tmp)); - hex_to_id((char *) span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size, - (unsigned char *) tmp, 8); - ctr_span_set_span_id(ctr_span, tmp, 8); - } - - /* traceState */ - ret = find_map_entry_by_key(&span.via.map, "traceState", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - val_str = cfl_sds_create_len(span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size); - ctr_span->trace_state = val_str; - val_str = NULL; - } - - /* flags */ - ret = find_map_entry_by_key(&span.via.map, "flags", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - /* unsupported in CTraces */ - } - - /* parentSpanId */ - ret = find_map_entry_by_key(&span.via.map, "parentSpanId", 0, FLB_TRUE); - if (ret >= 0 && - span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR && - span.via.map.ptr[ret].val.via.str.size > 0) { - - /* parent_span_id is an hex of 16 bytes */ - if (span.via.map.ptr[ret].val.via.str.size != 16) { - len = span.via.map.ptr[ret].val.via.str.size; - if (len > sizeof(tmp) - 1) { - len = sizeof(tmp) - 1; - } - memcpy(tmp, span.via.map.ptr[ret].val.via.str.ptr, len); - tmp[len] = '\0'; - flb_plg_error(ctx->ins, "invalid parentSpanId: '%s'", tmp); - return -1; - } - - /* decode the hex string (8 bytes) */ - memset(tmp, '\0', sizeof(tmp)); - hex_to_id((char *) span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size, - (unsigned char *) tmp, 8); - ctr_span_set_parent_span_id(ctr_span, tmp, 8); - } - - /* flags */ - ret = find_map_entry_by_key(&span.via.map, "flags", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_span_set_flags(ctr_span, span.via.map.ptr[ret].val.via.u64); - } - - /* start_time_unix_nano */ - ret = find_map_entry_by_key(&span.via.map, "startTimeUnixNano", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - /* convert string number to integer */ - val = convert_string_number_to_u64((char *) span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size); - ctr_span_start_ts(ctr, ctr_span, val); - } - - /* end_time_unix_nano */ - ret = find_map_entry_by_key(&span.via.map, "endTimeUnixNano", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - /* convert string number to integer */ - val = convert_string_number_to_u64((char *) span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size); - ctr_span_end_ts(ctr, ctr_span, val); - } - - /* kind */ - ret = find_map_entry_by_key(&span.via.map, "kind", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_span_kind_set(ctr_span, span.via.map.ptr[ret].val.via.u64); - } - - /* attributes */ - ret = find_map_entry_by_key(&span.via.map, "attributes", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - attr = &span.via.map.ptr[ret].val; - ctr_attr = convert_attributes(ctx, attr, "span"); - if (ctr_attr) { - ctr_span_set_attributes(ctr_span, ctr_attr); - } - } - - /* dropped_attributes_count */ - ret = find_map_entry_by_key(&span.via.map, "droppedAttributesCount", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_span_set_dropped_attributes_count(ctr_span, span.via.map.ptr[ret].val.via.u64); - } - - /* events */ - ret = find_map_entry_by_key(&span.via.map, "events", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - ret = process_events(ctx, ctr, ctr_span, &span.via.map.ptr[ret].val); - } - - /* dropped_events_count */ - ret = find_map_entry_by_key(&span.via.map, "droppedEventsCount", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_span_set_dropped_events_count(ctr_span, span.via.map.ptr[ret].val.via.u64); - } - - /* dropped_links_count */ - ret = find_map_entry_by_key(&span.via.map, "droppedLinksCount", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_span_set_dropped_links_count(ctr_span, span.via.map.ptr[ret].val.via.u64); - } - - /* links */ - ret = find_map_entry_by_key(&span.via.map, "links", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - ret = process_links(ctx, ctr, ctr_span, &span.via.map.ptr[ret].val); - } - - /* schema_url */ - ret = find_map_entry_by_key(&span.via.map, "schemaUrl", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - val_str = cfl_sds_create_len(span.via.map.ptr[ret].val.via.str.ptr, - span.via.map.ptr[ret].val.via.str.size); - ctr_span_set_schema_url(ctr_span, val_str); - cfl_sds_destroy(val_str); - val_str = NULL; - } - - /* status */ - ret = find_map_entry_by_key(&span.via.map, "status", 0, FLB_TRUE); - if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_MAP) { - process_span_status(ctx, ctr, ctr_span, &span.via.map.ptr[ret].val); - } - } - - return 0; -} - - -static int process_scope_span(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - struct ctrace_resource_span *resource_span, - msgpack_object *scope_spans) -{ - int ret; - msgpack_object scope; - msgpack_object *name; - msgpack_object *attr; - msgpack_object *version; - msgpack_object *schema_url; - msgpack_object *dropped_attr; - msgpack_object *spans; - cfl_sds_t url = NULL; - struct ctrace_scope_span *scope_span; - - /* get 'scope' */ - ret = find_map_entry_by_key(&scope_spans->via.map, "scope", 0, FLB_TRUE); - if (ret >= 0) { - scope = scope_spans->via.map.ptr[ret].val; - if (scope.type != MSGPACK_OBJECT_MAP) { - flb_plg_error(ctx->ins, "unexpected scope type in scope span"); - return -1; - } - - /* create the scope_span */ - scope_span = ctr_scope_span_create(resource_span); - if (scope_span == NULL) { - return -1; - } - - /* instrumentation scope: name */ - name = NULL; - ret = find_map_entry_by_key(&scope.via.map, "name", 0, FLB_TRUE); - if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - name = &scope.via.map.ptr[ret].val; - } - - /* instrumentation scope: version */ - version = NULL; - ret = find_map_entry_by_key(&scope.via.map, "version", 0, FLB_TRUE); - if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { - version = &scope.via.map.ptr[ret].val; - } - - /* instrumentation scope: attributes */ - attr = NULL; - ret = find_map_entry_by_key(&scope.via.map, "attributes", 0, FLB_TRUE); - if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - attr = &scope.via.map.ptr[ret].val; - } - - /* instrumentation scope: dropped_attributes_count */ - dropped_attr = NULL; - ret = find_map_entry_by_key(&scope.via.map, "droppedAttributesCount", 0, FLB_TRUE); - if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - dropped_attr = &scope.via.map.ptr[ret].val; - } - - ret = process_scope_attributes(ctx, - ctr, - scope_span, - name, version, attr, dropped_attr); - if (ret == -1) { - flb_plg_warn(ctx->ins, "failed to process scope attributes"); - } - } - - /* schema_url */ - ret = find_map_entry_by_key(&scope_spans->via.map, "schemaUrl", 0, FLB_TRUE); - if (ret >= 0) { - schema_url = &scope_spans->via.map.ptr[ret].val; - if (schema_url->type == MSGPACK_OBJECT_STR) { - /* set schema url */ - url = cfl_sds_create_len(schema_url->via.str.ptr, schema_url->via.str.size); - if (url) { - ctr_scope_span_set_schema_url(scope_span, url); - cfl_sds_destroy(url); - } - } - } - - /* process the scope spans[] */ - ret = find_map_entry_by_key(&scope_spans->via.map, "spans", 0, FLB_TRUE); - if (ret >= 0 && scope_spans->via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { - spans = &scope_spans->via.map.ptr[ret].val; - ret = process_spans(ctx, ctr, scope_span, spans); - if (ret == -1) { - flb_plg_warn(ctx->ins, "failed to process spans"); - } - } - - return 0; -} - -static int process_resource_span(struct flb_opentelemetry *ctx, - struct ctrace *ctr, - msgpack_object *resource_spans) -{ - int i; - int ret; - cfl_sds_t url; - struct ctrace_resource *ctr_resource; - struct ctrace_resource_span *resource_span; - msgpack_object resource; - msgpack_object attr; - msgpack_object scope_spans; - msgpack_object schema_url; - - if (resource_spans->type != MSGPACK_OBJECT_MAP) { - return -1; - } - - /* get the resource */ - ret = find_map_entry_by_key(&resource_spans->via.map, "resource", 0, FLB_TRUE); - if (ret == -1) { - flb_plg_error(ctx->ins, "resource missing"); - return -1; - } - - resource = resource_spans->via.map.ptr[ret].val; - if (resource.type != MSGPACK_OBJECT_MAP) { - flb_plg_error(ctx->ins, "unexpected resource type in resource span"); - return -1; - } - - - resource_span = ctr_resource_span_create(ctr); - if (resource_span == NULL) { - return -1; - } - - ctr_resource = ctr_resource_span_get_resource(resource_span); - - /* droppedAttributesCount */ - ret = find_map_entry_by_key(&resource.via.map, "droppedAttributesCount", 0, FLB_FALSE); - if (ret >= 0 && resource.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - ctr_resource_set_dropped_attr_count(ctr_resource, resource.via.map.ptr[ret].val.via.u64); - } - - - /* Get resource attributes */ - ret = find_map_entry_by_key(&resource.via.map, "attributes", 0, FLB_TRUE); - if (ret >= 0) { - attr = resource.via.map.ptr[ret].val; - if (attr.type == MSGPACK_OBJECT_ARRAY) { - /* iterate and register attributes */ - ret = process_resource_attributes(ctx, - ctr, - resource_span, - &attr); - if (ret == -1) { - flb_plg_warn(ctx->ins, "failed to process resource attributes"); - } - } - } - - /* scopeSpans */ - ret = find_map_entry_by_key(&resource_spans->via.map, "scopeSpans", 0, FLB_TRUE); - if (ret == -1) { - flb_plg_error(ctx->ins, "scopeSpans missing"); - return -1; - } - scope_spans = resource_spans->via.map.ptr[ret].val; - - if (scope_spans.type != MSGPACK_OBJECT_ARRAY) { - flb_plg_error(ctx->ins, "unexpected scopeSpans type"); - ctr_destroy(ctr); - return -1; - } - - for (i = 0; i < scope_spans.via.array.size; i++) { - ret = process_scope_span(ctx, - ctr, - resource_span, - &scope_spans.via.array.ptr[i]); - if (ret == -1) { - flb_plg_warn(ctx->ins, "failed to process scope span"); - } - } - - /* schema_url */ - ret = find_map_entry_by_key(&resource.via.map, "schemaUrl", 0, FLB_TRUE); - if (ret >= 0) { - schema_url = resource.via.map.ptr[ret].val; - if (schema_url.type == MSGPACK_OBJECT_STR) { - url = cfl_sds_create_len(schema_url.via.str.ptr, schema_url.via.str.size); - if (url) { - ctr_resource_span_set_schema_url(resource_span, url); - cfl_sds_destroy(url); - } - } - } - - return 0; -} - -static struct ctrace *process_root_msgpack(struct flb_opentelemetry *ctx, msgpack_object *obj) -{ - int i; - int ret; - struct ctrace *ctr; - msgpack_object_array *resource_spans; - - if (obj->type != MSGPACK_OBJECT_MAP) { - return NULL; - } - - ret = find_map_entry_by_key(&obj->via.map, "resourceSpans", 0, FLB_TRUE); - if (ret == -1) { - ret = find_map_entry_by_key(&obj->via.map, "resource_spans", 0, FLB_TRUE); - if (ret == -1) { - flb_plg_error(ctx->ins, "resourceSpans missing"); - return NULL; - } - } - - if (obj->via.map.ptr[ret].val.type != MSGPACK_OBJECT_ARRAY) { - flb_plg_error(ctx->ins, "unexpected resourceSpans type"); - return NULL; - } - - resource_spans = &obj->via.map.ptr[ret].val.via.array; - ret = 0; - - ctr = ctr_create(NULL); - if (!ctr) { - return NULL; - } - - for (i = 0; i < resource_spans->size; i++) { - ret = process_resource_span(ctx, ctr, &resource_spans->ptr[i]); - if (ret == -1) { - flb_plg_warn(ctx->ins, "failed to process resource span"); - - ctr_destroy(ctr); - return NULL; - } - } - - return ctr; -} static int process_json(struct flb_opentelemetry *ctx, char *tag, size_t tag_len, const char *body, size_t len) { - int result = -1; - int root_type; - char *msgpack_body; - size_t msgpack_body_length; - size_t offset = 0; - msgpack_unpacked unpacked_root; - struct ctrace *ctr; - - result = flb_pack_json(body, len, &msgpack_body, &msgpack_body_length, - &root_type, NULL); - - if (result != 0) { - flb_plg_error(ctx->ins, "invalid JSON trace: msgpack conversion error"); - return -1; - } - - msgpack_unpacked_init(&unpacked_root); - - result = msgpack_unpack_next(&unpacked_root, - msgpack_body, - msgpack_body_length, - &offset); - - if (result != MSGPACK_UNPACK_SUCCESS) { - return -1; - } + int result = -1; + int error_status = 0; + struct ctrace *ctr; - /* iterate msgpack and comppose a CTraces context */ - ctr = process_root_msgpack(ctx, &unpacked_root.data); + /* Use the new centralized API for JSON to ctrace conversion */ + ctr = flb_opentelemetry_json_traces_to_ctrace(body, len, &error_status); if (ctr) { result = flb_input_trace_append(ctx->ins, tag, tag_len, ctr); if (result == -1) { ctr_destroy(ctr); } } - - msgpack_unpacked_destroy(&unpacked_root); - flb_free(msgpack_body); + else { + flb_plg_error(ctx->ins, "invalid JSON trace: conversion error (status: %d)", error_status); + } return result; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13bc3e4418f..707b18da454 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,6 +114,7 @@ endif() set(src ${src} opentelemetry/flb_opentelemetry_logs.c + opentelemetry/flb_opentelemetry_traces.c opentelemetry/flb_opentelemetry_utils.c ) diff --git a/src/opentelemetry/flb_opentelemetry_logs.c b/src/opentelemetry/flb_opentelemetry_logs.c index 85894225e3c..700f8c6e1ef 100644 --- a/src/opentelemetry/flb_opentelemetry_logs.c +++ b/src/opentelemetry/flb_opentelemetry_logs.c @@ -302,14 +302,24 @@ static int process_json_payload_log_records_entry( } if (trace_id != NULL && trace_id->type == MSGPACK_OBJECT_STR && trace_id->via.str.size == 32) { - flb_otel_utils_hex_to_id(trace_id->via.str.ptr, trace_id->via.str.size, tmp_id, 16); + if (flb_otel_utils_hex_to_id(trace_id->via.str.ptr, trace_id->via.str.size, tmp_id, 16) != 0) { + if (error_status) { + *error_status = FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID; + } + return -FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID; + } flb_log_event_encoder_append_metadata_values(encoder, FLB_LOG_EVENT_STRING_VALUE("trace_id", 8), FLB_LOG_EVENT_BINARY_VALUE(tmp_id, 16)); } if (span_id != NULL && span_id->type == MSGPACK_OBJECT_STR && span_id->via.str.size == 16) { - flb_otel_utils_hex_to_id(span_id->via.str.ptr, span_id->via.str.size, tmp_id, 8); + if (flb_otel_utils_hex_to_id(span_id->via.str.ptr, span_id->via.str.size, tmp_id, 8) != 0) { + if (error_status) { + *error_status = FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID; + } + return -FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID; + } flb_log_event_encoder_append_metadata_values(encoder, FLB_LOG_EVENT_STRING_VALUE("span_id", 7), FLB_LOG_EVENT_BINARY_VALUE(tmp_id, 8)); diff --git a/src/opentelemetry/flb_opentelemetry_traces.c b/src/opentelemetry/flb_opentelemetry_traces.c new file mode 100644 index 00000000000..4d61f323fb6 --- /dev/null +++ b/src/opentelemetry/flb_opentelemetry_traces.c @@ -0,0 +1,1263 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2025 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 + +static int process_attribute(struct ctrace_attributes *attr, + msgpack_object *key, msgpack_object *value, int type) +{ + int ret; + char *key_str; + char *value_str; + int64_t value_int; + double value_double; + int value_bool; + + if (key->type != MSGPACK_OBJECT_STR) { + return -1; + } + + /* temporary buffer for key since it needs to be NULL terminated */ + key_str = flb_sds_create_len(key->via.str.ptr, key->via.str.size); + if (key_str == NULL) { + return -1; + } + + /* + * the value of 'type' is set by the JSON wrapped value, we need to to convert it + * since msgpack_object *value is always a string + */ + switch (type) { + case MSGPACK_OBJECT_STR: + if (value->type != MSGPACK_OBJECT_STR) { + flb_sds_destroy(key_str); + return -1; + } + + value_str = flb_sds_create_len(value->via.str.ptr, value->via.str.size); + if (value_str == NULL) { + flb_sds_destroy(key_str); + return -1; + } + + ret = ctr_attributes_set_string(attr, key_str, value_str); + flb_sds_destroy(value_str); + break; + case MSGPACK_OBJECT_POSITIVE_INTEGER: + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + if (value->type == MSGPACK_OBJECT_STR) { + value_str = flb_sds_create_len(value->via.str.ptr, value->via.str.size); + if (value_str == NULL) { + flb_sds_destroy(key_str); + return -1; + } + value_int = strtoll(value_str, NULL, 10); + flb_sds_destroy(value_str); + } + else if (value->type == MSGPACK_OBJECT_POSITIVE_INTEGER || + value->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { + value_int = value->via.i64; + } + else { + flb_sds_destroy(key_str); + return -1; + } + + ret = ctr_attributes_set_int64(attr, key_str, value_int); + break; + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + if (value->type == MSGPACK_OBJECT_STR) { + value_str = flb_sds_create_len(value->via.str.ptr, value->via.str.size); + if (value_str == NULL) { + flb_sds_destroy(key_str); + return -1; + } + value_double = strtod(value_str, NULL); + flb_sds_destroy(value_str); + } + else if (value->type == MSGPACK_OBJECT_FLOAT32 || + value->type == MSGPACK_OBJECT_FLOAT64) { + value_double = value->via.f64; + } + else { + flb_sds_destroy(key_str); + return -1; + } + + ret = ctr_attributes_set_double(attr, key_str, value_double); + break; + case MSGPACK_OBJECT_BOOLEAN: + if (value->type != MSGPACK_OBJECT_BOOLEAN) { + flb_sds_destroy(key_str); + return -1; + } + + value_bool = value->via.boolean; + ret = ctr_attributes_set_bool(attr, key_str, value_bool); + break; + case MSGPACK_OBJECT_ARRAY: + /* + * The less fun part (OTel JSON encoding), the value can be an array and + * only allows values such as string, bool, int64, double. I am glad this + * don't support nested arrays or maps. + */ + ret = 0; + break; + default: + flb_sds_destroy(key_str); + return -1; + } + + flb_sds_destroy(key_str); + return ret; +} + +static int process_resource_unwrap_attribute(msgpack_object *attr, + msgpack_object *out_key, + msgpack_object *out_value, int *out_value_type) +{ + int ret; + int type; + msgpack_object key; + msgpack_object val; + msgpack_object *real_value; + + if (attr->type != MSGPACK_OBJECT_MAP) { + return -1; + } + + ret = flb_otel_utils_find_map_entry_by_key(&attr->via.map, "key", 0, FLB_TRUE); + if (ret == -1) { + return -1; + } + + key = attr->via.map.ptr[ret].val; + if (key.type != MSGPACK_OBJECT_STR) { + return -1; + } + + ret = flb_otel_utils_find_map_entry_by_key(&attr->via.map, "value", 0, FLB_TRUE); + if (ret == -1) { + return -1; + } + + val = attr->via.map.ptr[ret].val; + + ret = flb_otel_utils_json_payload_get_wrapped_value(&val, &real_value, &type); + if (ret != 0) { + return -1; + } + + *out_key = key; + *out_value = *real_value; + *out_value_type = type; + + return 0; +} + +/* + * Convert a list of attributes in msgpack format to a cfl attributes by + * unwrapping JSON encoded value types. + */ +static struct ctrace_attributes *convert_attributes(msgpack_object *attributes, + const char *log_context) +{ + int i; + int ret; + int value_type; + msgpack_object key; + msgpack_object value; + struct ctrace_attributes *attr; + + attr = ctr_attributes_create(); + if (attr == NULL) { + return NULL; + } + + for (i = 0; i < attributes->via.array.size; i++) { + ret = process_resource_unwrap_attribute(&attributes->via.array.ptr[i], + &key, &value, &value_type); + if (ret == -1) { + flb_warn("found invalid %s attribute, skipping", log_context); + continue; + } + + /* set attribute */ + ret = process_attribute(attr, &key, &value, value_type); + if (ret == -1) { + flb_warn("failed to set %s attribute, skipping", log_context); + continue; + } + } + + return attr; +} + +static int process_resource_attributes(struct ctrace *ctr, + struct ctrace_resource_span *resource_span, + msgpack_object *attributes, + int *error_status) +{ + struct ctrace_resource *resource; + struct ctrace_attributes *attr; + + attr = convert_attributes(attributes, "trace resource"); + if (!attr) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES; + } + return -1; + } + + resource = ctr_resource_span_get_resource(resource_span); + ctr_resource_set_attributes(resource, attr); + + return 0; +} + +static int process_scope_attributes(struct ctrace *ctr, + struct ctrace_scope_span *scope_span, + msgpack_object *name, + msgpack_object *version, + msgpack_object *attributes, + msgpack_object *dropped_attributes_count, + int *error_status) +{ + int dropped = 0; + cfl_sds_t name_str = NULL; + cfl_sds_t version_str = NULL; + struct ctrace_attributes *attr = NULL; + struct ctrace_instrumentation_scope *ins_scope; + + if (attributes) { + attr = convert_attributes(attributes, "trace scope"); + if (!attr) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES; + } + return -1; + } + } + + if (name) { + name_str = cfl_sds_create_len(name->via.str.ptr, name->via.str.size); + } + if (version) { + version_str = cfl_sds_create_len(version->via.str.ptr, version->via.str.size); + } + + if (dropped_attributes_count) { + dropped = dropped_attributes_count->via.u64; + } + + ins_scope = ctr_instrumentation_scope_create(name_str, version_str, dropped, attr); + if (!ins_scope) { + if (name_str) { + cfl_sds_destroy(name_str); + } + if (version_str) { + cfl_sds_destroy(version_str); + } + if (attr) { + ctr_attributes_destroy(attr); + } + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + return -1; + } + + if (name_str) { + cfl_sds_destroy(name_str); + } + if (version_str) { + cfl_sds_destroy(version_str); + } + + ctr_scope_span_set_instrumentation_scope(scope_span, ins_scope); + return 0; +} + +static int process_events(struct ctrace *ctr, + struct ctrace_span *span, + msgpack_object *events, + int *error_status) +{ + int i; + int ret; + int len; + uint64_t ts = 0; + char tmp[64]; + cfl_sds_t name_str = NULL; + msgpack_object event; + msgpack_object *name = NULL; + msgpack_object *attr = NULL; + struct ctrace_span_event *ctr_event = NULL; + struct ctrace_attributes *ctr_attr = NULL; + + for (i = 0; i < events->via.array.size; i++) { + event = events->via.array.ptr[i]; + if (event.type != MSGPACK_OBJECT_MAP) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY; + } + return -1; + } + + name_str = NULL; + ts = 0; + + /* name */ + ret = flb_otel_utils_find_map_entry_by_key(&event.via.map, "name", 0, FLB_TRUE); + if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + name = &event.via.map.ptr[ret].val; + name_str = cfl_sds_create_len(name->via.str.ptr, name->via.str.size); + if (name_str == NULL) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY; + } + return -1; + } + } + + if (!name_str) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY; + } + return -1; + } + + /* time_unix_nano */ + ret = flb_otel_utils_find_map_entry_by_key(&event.via.map, "timeUnixNano", 0, FLB_TRUE); + if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + /* convert to uint64_t */ + len = event.via.map.ptr[ret].val.via.str.size; + if (len > sizeof(tmp) - 1) { + len = sizeof(tmp) - 1; + memcpy(tmp, event.via.map.ptr[ret].val.via.str.ptr, len); + tmp[len] = '\0'; + + if (name_str) { + cfl_sds_destroy(name_str); + } + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_EVENT_TIMESTAMP; + } + return -1; + } + + memcpy(tmp, event.via.map.ptr[ret].val.via.str.ptr, len); + tmp[len] = '\0'; + + ts = strtoull(tmp, NULL, 10); + } + + ctr_event = ctr_span_event_add_ts(span, name_str, ts); + cfl_sds_destroy(name_str); + if (ctr_event == NULL) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY; + } + return -1; + } + + /* attributes */ + ret = flb_otel_utils_find_map_entry_by_key(&event.via.map, "attributes", 0, FLB_TRUE); + if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + attr = &event.via.map.ptr[ret].val; + ctr_attr = convert_attributes(attr, "span event"); + if (ctr_attr) { + ctr_span_event_set_attributes(ctr_event, ctr_attr); + } + } + + /* dropped_attributes_count */ + ret = flb_otel_utils_find_map_entry_by_key(&event.via.map, "droppedAttributesCount", 0, FLB_FALSE); + if (ret >= 0 && event.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_span_event_set_dropped_attributes_count(ctr_event, event.via.map.ptr[ret].val.via.u64); + } + } + + return 0; +} + +static int process_links(struct ctrace *ctr, + struct ctrace_span *span, + msgpack_object *links, + int *error_status) +{ + int i; + int ret; + int len; + char tmp[64]; + char trace_id_bin[16]; + char span_id_bin[8]; + cfl_sds_t buf; + msgpack_object link; + msgpack_object *trace_id = NULL; + msgpack_object *span_id = NULL; + msgpack_object *trace_state = NULL; + msgpack_object *dropped_attr_count = NULL; + msgpack_object *flags = NULL; + msgpack_object *attr = NULL; + + struct ctrace_link *ctr_link = NULL; + struct ctrace_attributes *ctr_attr = NULL; + + for (i = 0; i < links->via.array.size; i++) { + link = links->via.array.ptr[i]; + if (link.type != MSGPACK_OBJECT_MAP) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_ENTRY; + } + return -1; + } + + trace_id = NULL; + span_id = NULL; + trace_state = NULL; + dropped_attr_count = NULL; + flags = NULL; + ctr_attr = NULL; + + /* traceId */ + ret = flb_otel_utils_find_map_entry_by_key(&link.via.map, "traceId", 0, FLB_TRUE); + if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + trace_id = &link.via.map.ptr[ret].val; + + /* trace_id is an hex of 32 bytes */ + if (trace_id->via.str.size != 32) { + len = trace_id->via.str.size; + if (len > sizeof(tmp) - 1) { + len = sizeof(tmp) - 1; + } + memcpy(tmp, trace_id->via.str.ptr, len); + tmp[len] = '\0'; + + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID; + } + return -1; + } + + /* decode the hex string (16 bytes) */ + if (flb_otel_utils_hex_to_id((char *) trace_id->via.str.ptr, trace_id->via.str.size, + (unsigned char *) trace_id_bin, 16) != 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID; + } + return -1; + } + } + + if (!trace_id) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID; + } + return -1; + } + + /* spanId */ + ret = flb_otel_utils_find_map_entry_by_key(&link.via.map, "spanId", 0, FLB_TRUE); + if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + span_id = &link.via.map.ptr[ret].val; + + /* span_id is an hex of 16 bytes */ + if (span_id->via.str.size != 16) { + len = span_id->via.str.size; + if (len > sizeof(tmp) - 1) { + len = sizeof(tmp) - 1; + } + memcpy(tmp, span_id->via.str.ptr, len); + tmp[len] = '\0'; + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID; + } + return -1; + } + + /* decode the hex string (8 bytes) */ + memset(tmp, '\0', sizeof(tmp)); + if (flb_otel_utils_hex_to_id((char *) span_id->via.str.ptr, span_id->via.str.size, + (unsigned char *) span_id_bin, 8) != 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID; + } + return -1; + } + } + + if (!span_id) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID; + } + return -1; + } + + /* traceState */ + ret = flb_otel_utils_find_map_entry_by_key(&link.via.map, "traceState", 0, FLB_FALSE); + if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + trace_state = &link.via.map.ptr[ret].val; + } + + /* attributes */ + ret = flb_otel_utils_find_map_entry_by_key(&link.via.map, "attributes", 0, FLB_FALSE); + if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + attr = &link.via.map.ptr[ret].val; + ctr_attr = convert_attributes(attr, "event link"); + } + + /* droped_attributes_count */ + ret = flb_otel_utils_find_map_entry_by_key(&link.via.map, "droppedAttributesCount", 0, FLB_FALSE); + if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + dropped_attr_count = &link.via.map.ptr[ret].val; + } + + /* flags */ + ret = flb_otel_utils_find_map_entry_by_key(&link.via.map, "flags", 0, FLB_FALSE); + if (ret >= 0 && link.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + flags = &link.via.map.ptr[ret].val; + } + + ctr_link = ctr_link_create(span, + trace_id_bin, 16, + span_id_bin, 8); + + if (ctr_link == NULL) { + if (ctr_attr) { + ctr_attributes_destroy(ctr_attr); + } + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_LINK_ENTRY; + } + return -1; + } + + if (trace_state) { + buf = cfl_sds_create_len(trace_state->via.str.ptr, trace_state->via.str.size); + if (buf) { + ctr_link_set_trace_state(ctr_link, buf); + cfl_sds_destroy(buf); + } + } + + if (ctr_attr) { + ctr_link_set_attributes(ctr_link, ctr_attr); + } + + if (dropped_attr_count) { + ctr_link_set_dropped_attr_count(ctr_link, dropped_attr_count->via.u64); + } + + if (flags) { + ctr_link_set_flags(ctr_link, flags->via.u64); + } + } + + return 0; +} + +static int process_span_status(struct ctrace *ctr, + struct ctrace_span *span, + msgpack_object *status, + int *error_status) +{ + int ret; + int code = 0; + cfl_sds_t tmp = NULL; + char *message = NULL; + + if (status->type != MSGPACK_OBJECT_MAP) { + flb_error("unexpected type for status"); + return -1; + } + + /* code */ + ret = flb_otel_utils_find_map_entry_by_key(&status->via.map, "code", 0, FLB_TRUE); + if (ret >= 0 && status->via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + tmp = cfl_sds_create_len(status->via.map.ptr[ret].val.via.str.ptr, + status->via.map.ptr[ret].val.via.str.size); + if (!tmp) { + return -1; + } + + if (strcasecmp(tmp, "UNSET") == 0) { + code = CTRACE_SPAN_STATUS_CODE_UNSET; + } + else if (strcasecmp(tmp, "OK") == 0) { + code = CTRACE_SPAN_STATUS_CODE_OK; + } + else if (strcasecmp(tmp, "ERROR") == 0) { + code = CTRACE_SPAN_STATUS_CODE_ERROR; + } + else { + cfl_sds_destroy(tmp); + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_STATUS_FAILURE; + } + return -1; + } + cfl_sds_destroy(tmp); + } + else { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_STATUS_FAILURE; + } + return -1; + } + + /* message */ + ret = flb_otel_utils_find_map_entry_by_key(&status->via.map, "message", 0, FLB_FALSE); + if (ret >= 0 && status->via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + message = flb_sds_create_len(status->via.map.ptr[ret].val.via.str.ptr, + status->via.map.ptr[ret].val.via.str.size); + } + + ctr_span_set_status(span, code, message); + if (message) { + flb_sds_destroy(message); + } + + return 0; +} + +static int process_spans(struct ctrace *ctr, + struct ctrace_scope_span *scope_span, + msgpack_object *spans, + int *error_status) +{ + int i; + int ret; + int len; + uint64_t val; + char tmp[64]; + cfl_sds_t val_str = NULL; + msgpack_object span; + msgpack_object *name = NULL; + msgpack_object *attr = NULL; + struct ctrace_span *ctr_span = NULL; + struct ctrace_attributes *ctr_attr = NULL; + + for (i = 0; i < spans->via.array.size; i++) { + span = spans->via.array.ptr[i]; + if (span.type != MSGPACK_OBJECT_MAP) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_SPAN_ENTRY_TYPE; + } + return -1; + } + + val_str = NULL; + + /* name */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "name", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + name = &span.via.map.ptr[ret].val; + val_str = cfl_sds_create_len(name->via.str.ptr, name->via.str.size); + } + + if (!val_str) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_SPAN_NAME_MISSING; + } + return -1; + } + + /* create the span */ + ctr_span = ctr_span_create(ctr, scope_span, val_str, NULL); + cfl_sds_destroy(val_str); + + if (ctr_span == NULL) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + return -1; + } + + /* traceId */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "traceId", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + /* trace_id is an hex of 32 bytes */ + if (span.via.map.ptr[ret].val.via.str.size != 32) { + len = span.via.map.ptr[ret].val.via.str.size; + if (len > sizeof(tmp) - 1) { + len = sizeof(tmp) - 1; + } + memcpy(tmp, span.via.map.ptr[ret].val.via.str.ptr, len); + tmp[len] = '\0'; + + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID; + } + return -1; + } + + /* decode the hex string (16 bytes) */ + memset(tmp, '\0', sizeof(tmp)); + if (flb_otel_utils_hex_to_id((char *) span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size, + (unsigned char *) tmp, 16) != 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID; + } + return -1; + } + ctr_span_set_trace_id(ctr_span, tmp, 16); + } + + /* spanId */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "spanId", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + /* span_id is an hex of 16 bytes */ + if (span.via.map.ptr[ret].val.via.str.size != 16) { + len = span.via.map.ptr[ret].val.via.str.size; + if (len > sizeof(tmp) - 1) { + len = sizeof(tmp) - 1; + } + memcpy(tmp, span.via.map.ptr[ret].val.via.str.ptr, len); + tmp[len] = '\0'; + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID; + } + return -1; + } + + /* decode the hex string (8 bytes) */ + memset(tmp, '\0', sizeof(tmp)); + if (flb_otel_utils_hex_to_id((char *) span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size, + (unsigned char *) tmp, 8) != 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID; + } + return -1; + } + ctr_span_set_span_id(ctr_span, tmp, 8); + } + + /* traceState */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "traceState", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + val_str = cfl_sds_create_len(span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size); + ctr_span->trace_state = val_str; + val_str = NULL; + } + + /* flags */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "flags", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + /* unsupported in CTraces */ + } + + /* parentSpanId */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "parentSpanId", 0, FLB_TRUE); + if (ret >= 0 && + span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR && + span.via.map.ptr[ret].val.via.str.size > 0) { + + /* parent_span_id is an hex of 16 bytes */ + if (span.via.map.ptr[ret].val.via.str.size != 16) { + len = span.via.map.ptr[ret].val.via.str.size; + if (len > sizeof(tmp) - 1) { + len = sizeof(tmp) - 1; + } + memcpy(tmp, span.via.map.ptr[ret].val.via.str.ptr, len); + tmp[len] = '\0'; + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID; + } + return -1; + } + + /* decode the hex string (8 bytes) */ + memset(tmp, '\0', sizeof(tmp)); + if (flb_otel_utils_hex_to_id((char *) span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size, + (unsigned char *) tmp, 8) != 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID; + } + return -1; + } + ctr_span_set_parent_span_id(ctr_span, tmp, 8); + } + + /* flags */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "flags", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_span_set_flags(ctr_span, span.via.map.ptr[ret].val.via.u64); + } + + /* start_time_unix_nano */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "startTimeUnixNano", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + /* convert string number to integer */ + val = flb_otel_utils_convert_string_number_to_u64((char *) span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size); + ctr_span_start_ts(ctr, ctr_span, val); + } + + /* end_time_unix_nano */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "endTimeUnixNano", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + /* convert string number to integer */ + val = flb_otel_utils_convert_string_number_to_u64((char *) span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size); + ctr_span_end_ts(ctr, ctr_span, val); + } + + /* kind */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "kind", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_span_kind_set(ctr_span, span.via.map.ptr[ret].val.via.u64); + } + + /* attributes */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "attributes", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + attr = &span.via.map.ptr[ret].val; + ctr_attr = convert_attributes(attr, "span"); + if (ctr_attr) { + ctr_span_set_attributes(ctr_span, ctr_attr); + } + } + + /* dropped_attributes_count */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "droppedAttributesCount", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_span_set_dropped_attributes_count(ctr_span, span.via.map.ptr[ret].val.via.u64); + } + + /* events */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "events", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + ret = process_events(ctr, ctr_span, &span.via.map.ptr[ret].val, error_status); + if (ret == -1) { + return -1; + } + } + + /* dropped_events_count */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "droppedEventsCount", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_span_set_dropped_events_count(ctr_span, span.via.map.ptr[ret].val.via.u64); + } + + /* dropped_links_count */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "droppedLinksCount", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_span_set_dropped_links_count(ctr_span, span.via.map.ptr[ret].val.via.u64); + } + + /* links */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "links", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + ret = process_links(ctr, ctr_span, &span.via.map.ptr[ret].val, error_status); + if (ret == -1) { + return -1; + } + } + + /* schema_url */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "schemaUrl", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + val_str = cfl_sds_create_len(span.via.map.ptr[ret].val.via.str.ptr, + span.via.map.ptr[ret].val.via.str.size); + ctr_span_set_schema_url(ctr_span, val_str); + cfl_sds_destroy(val_str); + val_str = NULL; + } + + /* status */ + ret = flb_otel_utils_find_map_entry_by_key(&span.via.map, "status", 0, FLB_TRUE); + if (ret >= 0 && span.via.map.ptr[ret].val.type == MSGPACK_OBJECT_MAP) { + ret = process_span_status(ctr, ctr_span, &span.via.map.ptr[ret].val, error_status); + if (ret == -1) { + return -1; + } + } + } + + return 0; +} + +static int process_scope_span(struct ctrace *ctr, + struct ctrace_resource_span *resource_span, + msgpack_object *scope_spans, + int *error_status) +{ + int ret; + msgpack_object scope; + msgpack_object *name; + msgpack_object *attr; + msgpack_object *version; + msgpack_object *schema_url; + msgpack_object *dropped_attr; + msgpack_object *spans; + cfl_sds_t url = NULL; + struct ctrace_scope_span *scope_span; + + /* get 'scope' */ + ret = flb_otel_utils_find_map_entry_by_key(&scope_spans->via.map, "scope", 0, FLB_TRUE); + if (ret >= 0) { + scope = scope_spans->via.map.ptr[ret].val; + if (scope.type != MSGPACK_OBJECT_MAP) { + flb_error("unexpected scope type in scope span"); + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_ENTRY_TYPE; + } + return -1; + } + + /* create the scope_span */ + scope_span = ctr_scope_span_create(resource_span); + if (scope_span == NULL) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + return -1; + } + + /* instrumentation scope: name */ + name = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&scope.via.map, "name", 0, FLB_TRUE); + if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + name = &scope.via.map.ptr[ret].val; + } + + /* instrumentation scope: version */ + version = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&scope.via.map, "version", 0, FLB_TRUE); + if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + version = &scope.via.map.ptr[ret].val; + } + + /* instrumentation scope: attributes */ + attr = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&scope.via.map, "attributes", 0, FLB_TRUE); + if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + attr = &scope.via.map.ptr[ret].val; + } + + /* instrumentation scope: dropped_attributes_count */ + dropped_attr = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&scope.via.map, "droppedAttributesCount", 0, FLB_TRUE); + if (ret >= 0 && scope.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + dropped_attr = &scope.via.map.ptr[ret].val; + } + + ret = process_scope_attributes(ctr, + scope_span, + name, version, attr, dropped_attr, + error_status); + if (ret == -1) { + flb_warn("failed to process scope attributes"); + if (error_status && *error_status == 0) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES; + } + return -1; + } + } + else { + /* If scope is not defined we still need the scope span container */ + scope_span = ctr_scope_span_create(resource_span); + if (scope_span == NULL) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + return -1; + } + } + + /* schema_url */ + ret = flb_otel_utils_find_map_entry_by_key(&scope_spans->via.map, "schemaUrl", 0, FLB_TRUE); + if (ret >= 0) { + if (scope_spans->via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + schema_url = &scope_spans->via.map.ptr[ret].val; + url = cfl_sds_create_len(schema_url->via.str.ptr, schema_url->via.str.size); + if (url) { + ctr_scope_span_set_schema_url(scope_span, url); + cfl_sds_destroy(url); + url = NULL; + } + } + } + + /* spans */ + spans = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&scope_spans->via.map, "spans", 0, FLB_TRUE); + if (ret < 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_SPANS_MISSING; + } + return -1; + } + + if (scope_spans->via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + spans = &scope_spans->via.map.ptr[ret].val; + } + else { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_SPANS_TYPE; + } + return -1; + } + + ret = process_spans(ctr, scope_span, spans, error_status); + if (ret == -1) { + return -1; + } + + return 0; +} + +static int process_resource_span(struct ctrace *ctr, + msgpack_object *resource_span, + int *error_status) +{ + int ret; + msgpack_object resource; + msgpack_object *attr; + msgpack_object *schema_url; + msgpack_object *scope_spans; + msgpack_object_array *scope_spans_array; + cfl_sds_t url = NULL; + struct ctrace_resource_span *ctr_resource_span; + + if (resource_span->type != MSGPACK_OBJECT_MAP) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_ENTRY_TYPE; + } + return -1; + } + + /* create a resource span */ + ctr_resource_span = ctr_resource_span_create(ctr); + if (ctr_resource_span == NULL) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + return -1; + } + + /* process resource data */ + ret = flb_otel_utils_find_map_entry_by_key(&resource_span->via.map, "resource", 0, FLB_TRUE); + if (ret >= 0 && resource_span->via.map.ptr[ret].val.type == MSGPACK_OBJECT_MAP) { + resource = resource_span->via.map.ptr[ret].val; + + /* attributes */ + attr = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&resource.via.map, "attributes", 0, FLB_TRUE); + if (ret >= 0 && resource.via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + attr = &resource.via.map.ptr[ret].val; + ret = process_resource_attributes(ctr, ctr_resource_span, attr, error_status); + if (ret == -1) { + if (error_status && *error_status == 0) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_ATTRIBUTES; + } + return -1; + } + } + + /* dropped_attributes_count */ + ret = flb_otel_utils_find_map_entry_by_key(&resource.via.map, "droppedAttributesCount", 0, FLB_TRUE); + if (ret >= 0 && resource.via.map.ptr[ret].val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + ctr_resource_set_dropped_attr_count(ctr_resource_span->resource, + resource.via.map.ptr[ret].val.via.u64); + } + + /* schema_url */ + ret = flb_otel_utils_find_map_entry_by_key(&resource.via.map, "schemaUrl", 0, FLB_TRUE); + if (ret >= 0 && resource.via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + schema_url = &resource.via.map.ptr[ret].val; + url = cfl_sds_create_len(schema_url->via.str.ptr, schema_url->via.str.size); + if (url) { + ctr_resource_span_set_schema_url(ctr_resource_span, url); + cfl_sds_destroy(url); + url = NULL; + } + } + } + + /* schema_url */ + ret = flb_otel_utils_find_map_entry_by_key(&resource_span->via.map, "schemaUrl", 0, FLB_TRUE); + if (ret >= 0 && resource_span->via.map.ptr[ret].val.type == MSGPACK_OBJECT_STR) { + schema_url = &resource_span->via.map.ptr[ret].val; + url = cfl_sds_create_len(schema_url->via.str.ptr, schema_url->via.str.size); + if (url) { + ctr_resource_span_set_schema_url(ctr_resource_span, url); + cfl_sds_destroy(url); + url = NULL; + } + } + + /* scopeSpans */ + scope_spans = NULL; + ret = flb_otel_utils_find_map_entry_by_key(&resource_span->via.map, "scopeSpans", 0, FLB_TRUE); + if (ret < 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_SCOPE_SPANS_MISSING; + } + return -1; + } + + if (resource_span->via.map.ptr[ret].val.type == MSGPACK_OBJECT_ARRAY) { + scope_spans = &resource_span->via.map.ptr[ret].val; + } + else { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_TYPE; + } + return -1; + } + + scope_spans_array = &scope_spans->via.array; + for (ret = 0; ret < scope_spans_array->size; ret++) { + if (scope_spans_array->ptr[ret].type != MSGPACK_OBJECT_MAP) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_ENTRY_TYPE; + } + return -1; + } + + if (process_scope_span(ctr, + ctr_resource_span, + &scope_spans_array->ptr[ret], + error_status) == -1) { + return -1; + } + } + + return 0; +} + +static struct ctrace *process_root_msgpack(msgpack_object *obj, + int *error_status) +{ + int ret; + msgpack_object *resource_spans; + struct ctrace *ctr; + + if (obj->type != MSGPACK_OBJECT_MAP) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_ROOT_OBJECT_TYPE; + } + return NULL; + } + + ret = flb_otel_utils_find_map_entry_by_key(&obj->via.map, "resourceSpans", 0, FLB_TRUE); + if (ret < 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_RESOURCE_SPANS_MISSING; + } + return NULL; + } + + if (obj->via.map.ptr[ret].val.type != MSGPACK_OBJECT_ARRAY) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_TYPE; + } + return NULL; + } + + resource_spans = &obj->via.map.ptr[ret].val; + ret = 0; + + ctr = ctr_create(NULL); + if (!ctr) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + return NULL; + } + + for (ret = 0; ret < resource_spans->via.array.size; ret++) { + if (process_resource_span(ctr, + &resource_spans->via.array.ptr[ret], + error_status) == -1) { + ctr_destroy(ctr); + return NULL; + } + } + + return ctr; +} + +struct ctrace *flb_opentelemetry_json_traces_to_ctrace(const char *body, size_t len, int *error_status) + +{ + int result; + int root_type; + char *msgpack_body; + size_t msgpack_body_length; + size_t offset = 0; + msgpack_unpacked unpacked_root; + struct ctrace *ctr; + + if (error_status) { + *error_status = 0; + } + + result = flb_pack_json(body, len, &msgpack_body, &msgpack_body_length, + &root_type, NULL); + + if (result != 0) { + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_INVALID_JSON; + } + return NULL; + } + + msgpack_unpacked_init(&unpacked_root); + + result = msgpack_unpack_next(&unpacked_root, + msgpack_body, + msgpack_body_length, + &offset); + + if (result != MSGPACK_UNPACK_SUCCESS) { + msgpack_unpacked_destroy(&unpacked_root); + flb_free(msgpack_body); + if (error_status) { + *error_status = FLB_OTEL_TRACES_ERR_UNEXPECTED_ROOT_OBJECT_TYPE; + } + return NULL; + } + + /* iterate msgpack and compose a CTraces context */ + ctr = process_root_msgpack(&unpacked_root.data, error_status); + + msgpack_unpacked_destroy(&unpacked_root); + flb_free(msgpack_body); + + if (!ctr && error_status && *error_status == 0) { + *error_status = FLB_OTEL_TRACES_ERR_GENERIC_ERROR; + } + + return ctr; +} diff --git a/tests/internal/CMakeLists.txt b/tests/internal/CMakeLists.txt index a270f1dc3c8..fd2110bf017 100644 --- a/tests/internal/CMakeLists.txt +++ b/tests/internal/CMakeLists.txt @@ -171,7 +171,7 @@ set(UNIT_TESTS_DATA data/parser/regex.conf data/input_chunk/log/a_thousand_plus_one_bytes.log data/input_chunk/log/test_buffer_valid.log - data/opentelemetry/test_cases.json + data/opentelemetry/logs.json ) set(FLB_TESTS_DATA_PATH ${CMAKE_CURRENT_SOURCE_DIR}/) diff --git a/tests/internal/README.md b/tests/internal/README.md index 898f4825de4..271ba0f5bbe 100644 --- a/tests/internal/README.md +++ b/tests/internal/README.md @@ -4,9 +4,12 @@ The following directory contains unit tests to validate specific functions of Fl ## OpenTelemetry Test Cases -OpenTelemetry JSON tests are described in a single file located at -`data/opentelemetry/test_cases.json`. Each entry is keyed by the test name and -contains the following fields: +OpenTelemetry JSON tests are described in two separate files: + +### Logs Test Cases (`data/opentelemetry/logs.json`) + +Logs test cases validate the `flb_opentelemetry_logs_json_to_msgpack()` function. +Each entry is keyed by the test name and contains the following fields: ``` { @@ -28,6 +31,42 @@ contains the following fields: } ``` -When `expected_error` is present the unit test checks that -`flb_opentelemetry_logs_json_to_msgpack()` fails with the given error code and -message. +### Traces Test Cases (`data/opentelemetry/traces.json`) + +Traces test cases validate the `flb_opentelemetry_json_traces_to_ctrace()` function. +Each entry is keyed by the test name and contains the following fields: + +``` +{ + "test_name": { + "input": { ... }, # OTLP/JSON traces payload + "expected": { # successful result (optional) + # For successful cases, the test validates that a valid ctrace object is created + } + }, + "error_case": { + "input": { ... }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_*", + "message": "" + } + } +} +``` + +### Error Handling + +When `expected_error` is present, the unit tests check that the respective functions fail with the given error code and message: + +- **Logs**: `flb_opentelemetry_logs_json_to_msgpack()` should fail with `FLB_OTEL_LOGS_ERR_*` codes +- **Traces**: `flb_opentelemetry_json_traces_to_ctrace()` should fail with `FLB_OTEL_TRACES_ERR_*` codes + +### Test Coverage + +Both test files include comprehensive coverage for: + +- **Valid payloads**: Successful processing of well-formed OTLP JSON +- **Invalid structure**: Missing required fields, wrong data types +- **Hex decoding errors**: Invalid trace_id/span_id formats, zero-length strings, odd-length hex +- **Field validation**: Required vs optional fields according to OpenTelemetry specification +- **Edge cases**: Empty payloads, malformed JSON, boundary conditions diff --git a/tests/internal/data/opentelemetry/test_cases.json b/tests/internal/data/opentelemetry/logs.json similarity index 93% rename from tests/internal/data/opentelemetry/test_cases.json rename to tests/internal/data/opentelemetry/logs.json index 640a9694894..dbc9cd0c1dd 100644 --- a/tests/internal/data/opentelemetry/test_cases.json +++ b/tests/internal/data/opentelemetry/logs.json @@ -592,6 +592,108 @@ } }, + "trace_id_invalid_hex_character": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "traceId": "0000000000000000000000000000000G", + "body": {"stringValue": "test log with invalid hex character in trace_id"} + }] + }] + }] + }, + "expected_error": { + "code": "FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID" + } + }, + + "span_id_invalid_hex_character": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "spanId": "000000000000000G", + "body": {"stringValue": "test log with invalid hex character in span_id"} + }] + }] + }] + }, + "expected_error": { + "code": "FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID" + } + }, + + "trace_id_odd_length": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "traceId": "0000000000000000000000000000001", + "body": {"stringValue": "test log with odd length trace_id"} + }] + }] + }] + }, + "expected_error": { + "code": "FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID" + } + }, + + "span_id_odd_length": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "spanId": "000000000000001", + "body": {"stringValue": "test log with odd length span_id"} + }] + }] + }] + }, + "expected_error": { + "code": "FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID" + } + }, + + "trace_id_mixed_valid_invalid_hex": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "traceId": "0000000000000000000000000000000Z", + "body": {"stringValue": "test log with mixed valid/invalid hex in trace_id"} + }] + }] + }] + }, + "expected_error": { + "code": "FLB_OTEL_LOGS_ERR_INVALID_TRACE_ID" + } + }, + + "span_id_mixed_valid_invalid_hex": { + "input": { + "resourceLogs": [{ + "scopeLogs": [{ + "logRecords": [{ + "timeUnixNano": "1640995200000000000", + "spanId": "00000000000000Z", + "body": {"stringValue": "test log with mixed valid/invalid hex in span_id"} + }] + }] + }] + }, + "expected_error": { + "code": "FLB_OTEL_LOGS_ERR_INVALID_SPAN_ID" + } + }, + "base64_encoded_trace_span": { "input": { "resourceLogs": [{ diff --git a/tests/internal/data/opentelemetry/traces.json b/tests/internal/data/opentelemetry/traces.json new file mode 100644 index 00000000000..cf3af0ccd27 --- /dev/null +++ b/tests/internal/data/opentelemetry/traces.json @@ -0,0 +1,728 @@ +{ + "resourceSpans_missing": { + "input": {}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_RESOURCE_SPANS_MISSING" + } + }, + "resourceSpans_wrong_type": { + "input": {"resourceSpans": {}}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_TYPE" + } + }, + "resourceSpans_entry_wrong_type": { + "input": {"resourceSpans": [1]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_RESOURCE_SPANS_ENTRY_TYPE" + } + }, + "root_wrong_type": { + "input": [], + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_ROOT_OBJECT_TYPE" + } + }, + "scopeSpans_missing": { + "input": {"resourceSpans": [{}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_SCOPE_SPANS_MISSING" + } + }, + "scopeSpans_wrong_type": { + "input": {"resourceSpans": [{"scopeSpans": {}}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_TYPE" + } + }, + "scopeSpans_entry_wrong_type": { + "input": {"resourceSpans": [{"scopeSpans": [1]}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_SCOPE_SPANS_ENTRY_TYPE" + } + }, + "spans_missing": { + "input": {"resourceSpans": [{"scopeSpans": [{}]}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_SPANS_MISSING" + } + }, + "spans_wrong_type": { + "input": {"resourceSpans": [{"scopeSpans": [{"spans": {}}]}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_SPANS_TYPE" + } + }, + "span_entry_wrong_type": { + "input": {"resourceSpans": [{"scopeSpans": [{"spans": [1]}]}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_UNEXPECTED_SPAN_ENTRY_TYPE" + } + }, + "span_name_missing": { + "input": {"resourceSpans": [{"scopeSpans": [{"spans": [{}]}]}]}, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_SPAN_NAME_MISSING" + } + }, + "invalid_trace_id": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "1234", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID" + } + }, + "invalid_span_id": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "1234" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID" + } + }, + "invalid_parent_span_id": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "parentSpanId": "1234" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID" + } + }, + "event_missing_name": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "events": [ + {} + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_EVENT_ENTRY" + } + }, + "event_invalid_timestamp": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "events": [ + { + "name": "event", + "timeUnixNano": "1234567890123456789012345678901234567890123456789012345678901234567890" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_EVENT_TIMESTAMP" + } + }, + "link_entry_wrong_type": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [1] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_ENTRY" + } + }, + "link_missing_trace_id": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + {} + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID" + } + }, + "link_invalid_span_id": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "00000000000000000000000000000001", + "spanId": "1234" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID" + } + }, + "status_invalid_code": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "status": { + "code": "NOT_A_STATUS" + } + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_STATUS_FAILURE" + } + }, + "trace_id_zero_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID" + } + }, + "span_id_zero_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID" + } + }, + "parent_span_id_zero_length_valid": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "parentSpanId": "" + } + ] + } + ] + } + ] + } + }, + "trace_id_invalid_hex": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "0000000000000000000000000000000G", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID" + } + }, + "span_id_invalid_hex": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "000000000000000G" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID" + } + }, + "parent_span_id_invalid_hex": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "parentSpanId": "000000000000000G" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID" + } + }, + "trace_id_odd_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "0000000000000000000000000000001", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_TRACE_ID" + } + }, + "span_id_odd_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "000000000000001" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_SPAN_ID" + } + }, + "parent_span_id_odd_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "parentSpanId": "000000000000001" + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_PARENT_SPAN_ID" + } + }, + "link_trace_id_zero_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID" + } + }, + "link_span_id_zero_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "00000000000000000000000000000001", + "spanId": "" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID" + } + }, + "link_trace_id_invalid_hex": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "0000000000000000000000000000000G", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID" + } + }, + "link_span_id_invalid_hex": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "00000000000000000000000000000001", + "spanId": "000000000000000G" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID" + } + }, + "link_trace_id_odd_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "0000000000000000000000000000001", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_TRACE_ID" + } + }, + "link_span_id_odd_length": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "links": [ + { + "traceId": "00000000000000000000000000000001", + "spanId": "000000000000001" + } + ] + } + ] + } + ] + } + ] + }, + "expected_error": { + "code": "FLB_OTEL_TRACES_ERR_INVALID_LINK_SPAN_ID" + } + }, + "valid_with_parent_span_id": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "child_span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000002", + "parentSpanId": "0000000000000001" + } + ] + } + ] + } + ] + } + }, + "valid_minimal": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001" + } + ] + } + ] + } + ] + } + }, + "valid_with_event_and_link": { + "input": { + "resourceSpans": [ + { + "scopeSpans": [ + { + "spans": [ + { + "name": "span", + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001", + "events": [ + { + "name": "event", + "timeUnixNano": "1234" + } + ], + "links": [ + { + "traceId": "00000000000000000000000000000001", + "spanId": "0000000000000001" + } + ], + "status": { + "code": "OK" + } + } + ] + } + ] + } + ] + } + } +} diff --git a/tests/internal/opentelemetry.c b/tests/internal/opentelemetry.c index ea31931f2da..b9b5b9fdbe2 100644 --- a/tests/internal/opentelemetry.c +++ b/tests/internal/opentelemetry.c @@ -27,6 +27,7 @@ // #include "../../plugins/in_opentelemetry/opentelemetry.h" #include +#include #include #include @@ -407,6 +408,44 @@ void test_hex_to_id() TEST_CHECK(memcmp(out, expect, sizeof(expect)) == 0); } +void test_hex_to_id_error_cases() +{ + unsigned char out[16]; + int ret; + + /* Test zero length string */ + ret = flb_otel_utils_hex_to_id("", 0, out, 16); + TEST_CHECK(ret == 0); /* Zero length should succeed (empty output) */ + + /* Test odd length string */ + ret = flb_otel_utils_hex_to_id("123", 3, out, 16); + TEST_CHECK(ret == -1); /* Odd length should fail */ + + /* Test invalid hex character */ + ret = flb_otel_utils_hex_to_id("0000000000000000000000000000000G", 32, out, 16); + TEST_CHECK(ret == -1); /* Invalid hex character should fail */ + + /* Test mixed valid/invalid hex */ + ret = flb_otel_utils_hex_to_id("0000000000000000000000000000000Z", 32, out, 16); + TEST_CHECK(ret == -1); /* Invalid hex character should fail */ + + /* Test valid hex with wrong output size */ + ret = flb_otel_utils_hex_to_id("00000000000000000000000000000001", 32, out, 8); + TEST_CHECK(ret == 0); /* Should succeed even with larger output buffer */ + + /* Test valid hex with correct size */ + ret = flb_otel_utils_hex_to_id("0000000000000001", 16, out, 8); + TEST_CHECK(ret == 0); /* Should succeed */ + + /* Test valid hex with uppercase */ + ret = flb_otel_utils_hex_to_id("0000000000000000000000000000000A", 32, out, 16); + TEST_CHECK(ret == 0); /* Should succeed with uppercase hex */ + + /* Test valid hex with lowercase */ + ret = flb_otel_utils_hex_to_id("0000000000000000000000000000000a", 32, out, 16); + TEST_CHECK(ret == 0); /* Should succeed with lowercase hex */ +} + void test_convert_string_number_to_u64() { uint64_t val; @@ -498,7 +537,8 @@ void test_json_payload_get_wrapped_value() msgpack_unpacked_destroy(&up); } -#define OTEL_TEST_CASES_PATH FLB_TESTS_DATA_PATH "/data/opentelemetry/test_cases.json" +#define OTEL_TEST_CASES_PATH FLB_TESTS_DATA_PATH "/data/opentelemetry/logs.json" +#define OTEL_TRACES_TEST_CASES_PATH FLB_TESTS_DATA_PATH "/data/opentelemetry/traces.json" void test_opentelemetry_cases() { @@ -758,6 +798,133 @@ void test_opentelemetry_cases() flb_free(cases_json); } +void test_opentelemetry_traces_cases() +{ + int ret; + char *cases_json; + char *tmp_buf = NULL; + size_t tmp_size; + int type; + msgpack_unpacked result; + msgpack_object *root; + size_t i; + + cases_json = mk_file_to_buffer(OTEL_TRACES_TEST_CASES_PATH); + TEST_CHECK(cases_json != NULL); + if (cases_json == NULL) { + flb_error("could not read trace test cases from '%s'", OTEL_TRACES_TEST_CASES_PATH); + return; + } + + ret = flb_pack_json(cases_json, strlen(cases_json), &tmp_buf, &tmp_size, &type, NULL); + TEST_CHECK(ret == 0); + if (ret != 0) { + flb_error("could not convert trace test cases to msgpack from '%s'", OTEL_TRACES_TEST_CASES_PATH); + flb_free(cases_json); + return; + } + + msgpack_unpacked_init(&result); + ret = msgpack_unpack_next(&result, tmp_buf, tmp_size, NULL); + TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS); + if (ret != MSGPACK_UNPACK_SUCCESS) { + msgpack_unpacked_destroy(&result); + flb_free(tmp_buf); + flb_free(cases_json); + return; + } + + root = &result.data; + + for (i = 0; i < root->via.map.size; i++) { + msgpack_object *case_obj; + char *case_name; + char *input_json = NULL; + struct ctrace *ctr = NULL; + int error_status = 0; + int expect_error = FLB_FALSE; + int expected_code = 0; + + case_name = flb_malloc(root->via.map.ptr[i].key.via.str.size + 1); + if (!case_name) { + flb_errno(); + continue; + } + memcpy(case_name, + root->via.map.ptr[i].key.via.str.ptr, + root->via.map.ptr[i].key.via.str.size); + case_name[root->via.map.ptr[i].key.via.str.size] = '\0'; + printf(">> running trace test case '%s'\n", case_name); + + case_obj = &root->via.map.ptr[i].val; + + ret = flb_otel_utils_find_map_entry_by_key(&case_obj->via.map, "input", 0, FLB_TRUE); + TEST_CHECK(ret >= 0); + if (ret < 0) { + flb_free(case_name); + continue; + } + + input_json = flb_msgpack_to_json_str(512, &case_obj->via.map.ptr[ret].val, FLB_TRUE); + TEST_CHECK(input_json != NULL); + if (input_json == NULL) { + flb_free(case_name); + continue; + } + + ret = flb_otel_utils_find_map_entry_by_key(&case_obj->via.map, "expected_error", 0, FLB_TRUE); + if (ret >= 0) { + msgpack_object *exp_obj; + int code_idx; + + exp_obj = &case_obj->via.map.ptr[ret].val; + code_idx = flb_otel_utils_find_map_entry_by_key(&exp_obj->via.map, "code", 0, FLB_TRUE); + TEST_CHECK(code_idx >= 0); + if (code_idx >= 0 && exp_obj->via.map.ptr[code_idx].val.type == MSGPACK_OBJECT_STR) { + char *code_str; + + code_str = flb_malloc(exp_obj->via.map.ptr[code_idx].val.via.str.size + 1); + if (code_str) { + memcpy(code_str, + exp_obj->via.map.ptr[code_idx].val.via.str.ptr, + exp_obj->via.map.ptr[code_idx].val.via.str.size); + code_str[exp_obj->via.map.ptr[code_idx].val.via.str.size] = '\0'; + expected_code = flb_opentelemetry_error_code(code_str); + TEST_CHECK(expected_code != -1000); + flb_free(code_str); + expect_error = FLB_TRUE; + } + } + } + + ctr = flb_opentelemetry_json_traces_to_ctrace(input_json, strlen(input_json), &error_status); + + if (expect_error == FLB_TRUE) { + TEST_CHECK_(ctr == NULL, "trace case %s should fail", case_name); + TEST_CHECK_(error_status == expected_code, + "trace case %s expected status %d got %d", + case_name, expected_code, error_status); + } + else { + TEST_CHECK_(ctr != NULL, "trace case %s should succeed", case_name); + TEST_CHECK_(error_status == 0, + "trace case %s expected success status 0 got %d", + case_name, error_status); + } + + if (ctr) { + ctr_destroy(ctr); + } + + flb_free(input_json); + flb_free(case_name); + } + + msgpack_unpacked_destroy(&result); + flb_free(tmp_buf); + flb_free(cases_json); +} + void test_trace_span_binary_sizes() { int ret; @@ -775,7 +942,6 @@ void test_trace_span_binary_sizes() struct flb_record_accessor *ra_span_id; struct flb_ra_value *val_trace_id; struct flb_ra_value *val_span_id; - size_t len; /* Test input with trace_id and span_id */ input_json = "{\"resourceLogs\":[{\"scopeLogs\":[{\"logRecords\":[{\"timeUnixNano\":\"1640995200000000000\",\"traceId\":\"5B8EFFF798038103D269B633813FC60C\",\"spanId\":\"EEE19B7EC3C1B174\",\"body\":{\"stringValue\":\"test\"}}]}]}]}"; @@ -850,10 +1016,12 @@ void test_trace_span_binary_sizes() /* Test list */ TEST_LIST = { { "hex_to_id", test_hex_to_id }, + { "hex_to_id_error_cases", test_hex_to_id_error_cases }, { "convert_string_number_to_u64", test_convert_string_number_to_u64 }, { "find_map_entry_by_key", test_find_map_entry_by_key }, { "json_payload_get_wrapped_value", test_json_payload_get_wrapped_value }, { "opentelemetry_cases", test_opentelemetry_cases }, + { "opentelemetry_traces_cases", test_opentelemetry_traces_cases }, { "trace_span_binary_sizes", test_trace_span_binary_sizes }, { 0 } };