diff --git a/lib/filterx/object-dict-interface.c b/lib/filterx/object-dict-interface.c index b6650b0189..4fff5614e3 100644 --- a/lib/filterx/object-dict-interface.c +++ b/lib/filterx/object-dict-interface.c @@ -34,6 +34,27 @@ filterx_dict_iter(FilterXObject *s, FilterXDictIterFunc func, gpointer user_data return self->iter(self, func, user_data); } +static gboolean +_add_elem_to_dict(FilterXObject *key_obj, FilterXObject *value_obj, gpointer user_data) +{ + FilterXObject *dict = (FilterXObject *) user_data; + + FilterXObject *new_value = filterx_object_ref(value_obj); + gboolean success = filterx_object_set_subscript(dict, key_obj, &new_value); + filterx_object_unref(new_value); + + return success; +} + +gboolean +filterx_dict_merge(FilterXObject *s, FilterXObject *other) +{ + FilterXDict *self = (FilterXDict *) s; + + g_assert(filterx_object_is_type(other, &FILTERX_TYPE_NAME(dict))); + return filterx_dict_iter(other, _add_elem_to_dict, self); +} + static gboolean _len(FilterXObject *s, guint64 *len) { diff --git a/lib/filterx/object-dict-interface.h b/lib/filterx/object-dict-interface.h index 023703b5b2..87dd74d0a7 100644 --- a/lib/filterx/object-dict-interface.h +++ b/lib/filterx/object-dict-interface.h @@ -44,6 +44,7 @@ struct FilterXDict_ }; gboolean filterx_dict_iter(FilterXObject *s, FilterXDictIterFunc func, gpointer user_data); +gboolean filterx_dict_merge(FilterXObject *s, FilterXObject *other); void filterx_dict_init_instance(FilterXDict *self, FilterXType *type); diff --git a/lib/filterx/object-json.c b/lib/filterx/object-json.c index 3b47bfad53..137bca01ec 100644 --- a/lib/filterx/object-json.c +++ b/lib/filterx/object-json.c @@ -24,6 +24,8 @@ #include "filterx/object-null.h" #include "filterx/object-primitive.h" #include "filterx/object-string.h" +#include "filterx/object-dict-interface.h" +#include "filterx/object-list-interface.h" #include "filterx/object-message-value.h" #include "filterx/filterx-weakrefs.h" #include "filterx/filterx-eval.h" @@ -167,6 +169,28 @@ filterx_json_new_from_args(GPtrArray *args) filterx_object_is_type(arg, &FILTERX_TYPE_NAME(json_object))) return filterx_object_ref(arg); + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(dict))) + { + FilterXObject *self = filterx_json_object_new_empty(); + if (!filterx_dict_merge(self, arg)) + { + filterx_object_unref(self); + return NULL; + } + return self; + } + + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(list))) + { + FilterXObject *self = filterx_json_array_new_empty(); + if (!filterx_list_merge(self, arg)) + { + filterx_object_unref(self); + return NULL; + } + return self; + } + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(message_value))) { FilterXObject *unmarshalled = filterx_object_unmarshal(arg); diff --git a/lib/filterx/object-list-interface.c b/lib/filterx/object-list-interface.c index 7ba00b4e3b..bd759a915c 100644 --- a/lib/filterx/object-list-interface.c +++ b/lib/filterx/object-list-interface.c @@ -61,6 +61,28 @@ filterx_list_unset_index(FilterXObject *s, gint64 index) return result; } +gboolean +filterx_list_merge(FilterXObject *s, FilterXObject *other) +{ + g_assert(filterx_object_is_type(other, &FILTERX_TYPE_NAME(list))); + + guint64 len; + g_assert(filterx_object_len(other, &len)); + + for (guint64 i = 0; i < len; i++) + { + FilterXObject *value_obj = filterx_list_get_subscript(other, (gint64) MIN(i, G_MAXINT64)); + gboolean success = filterx_list_append(s, &value_obj); + + filterx_object_unref(value_obj); + + if (!success) + return FALSE; + } + + return TRUE; +} + static gboolean _len(FilterXObject *s, guint64 *len) { diff --git a/lib/filterx/object-list-interface.h b/lib/filterx/object-list-interface.h index f5c5ea74cf..d4a2725ba5 100644 --- a/lib/filterx/object-list-interface.h +++ b/lib/filterx/object-list-interface.h @@ -43,6 +43,7 @@ FilterXObject *filterx_list_get_subscript(FilterXObject *s, gint64 index); gboolean filterx_list_set_subscript(FilterXObject *s, gint64 index, FilterXObject **new_value); gboolean filterx_list_append(FilterXObject *s, FilterXObject **new_value); gboolean filterx_list_unset_index(FilterXObject *s, gint64 index); +gboolean filterx_list_merge(FilterXObject *s, FilterXObject *other); void filterx_list_init_instance(FilterXList *self, FilterXType *type); diff --git a/modules/grpc/otel/filterx/object-otel-array.cpp b/modules/grpc/otel/filterx/object-otel-array.cpp index fd87aba5d5..7dbe683352 100644 --- a/modules/grpc/otel/filterx/object-otel-array.cpp +++ b/modules/grpc/otel/filterx/object-otel-array.cpp @@ -252,11 +252,27 @@ filterx_otel_array_new_from_args(GPtrArray *args) try { if (!args || args->len == 0) - self->cpp = new Array(self); + { + self->cpp = new Array(self); + } else if (args->len == 1) - self->cpp = new Array(self, (FilterXObject *) g_ptr_array_index(args, 0)); + { + FilterXObject *arg = (FilterXObject *) g_ptr_array_index(args, 0); + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(list))) + { + self->cpp = new Array(self); + if (!filterx_list_merge(&self->super.super, arg)) + throw std::runtime_error("Failed to merge list"); + } + else + { + self->cpp = new Array(self, arg); + } + } else - throw std::runtime_error("Invalid number of arguments"); + { + throw std::runtime_error("Invalid number of arguments"); + } } catch (const std::runtime_error &e) { diff --git a/modules/grpc/otel/filterx/object-otel-kvlist.cpp b/modules/grpc/otel/filterx/object-otel-kvlist.cpp index 8b7edf2fb5..33b9c80171 100644 --- a/modules/grpc/otel/filterx/object-otel-kvlist.cpp +++ b/modules/grpc/otel/filterx/object-otel-kvlist.cpp @@ -354,11 +354,27 @@ filterx_otel_kvlist_new_from_args(GPtrArray *args) try { if (!args || args->len == 0) - self->cpp = new KVList(self); + { + self->cpp = new KVList(self); + } else if (args->len == 1) - self->cpp = new KVList(self, (FilterXObject *) g_ptr_array_index(args, 0)); + { + FilterXObject *arg = (FilterXObject *) g_ptr_array_index(args, 0); + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(dict))) + { + self->cpp = new KVList(self); + if (!filterx_dict_merge(&self->super.super, arg)) + throw std::runtime_error("Failed to merge dict"); + } + else + { + self->cpp = new KVList(self, arg); + } + } else - throw std::runtime_error("Invalid number of arguments"); + { + throw std::runtime_error("Invalid number of arguments"); + } } catch (const std::runtime_error &e) { diff --git a/modules/grpc/otel/filterx/object-otel-logrecord.cpp b/modules/grpc/otel/filterx/object-otel-logrecord.cpp index c189007529..e2b961b4ab 100644 --- a/modules/grpc/otel/filterx/object-otel-logrecord.cpp +++ b/modules/grpc/otel/filterx/object-otel-logrecord.cpp @@ -38,6 +38,7 @@ #include #include #include +#include using namespace syslogng::grpc::otel::filterx; @@ -65,21 +66,23 @@ LogRecord::LogRecord(const LogRecord &o, FilterXOtelLogRecord *super_) : super(s } std::string -LogRecord::Marshal(void) +LogRecord::marshal(void) { std::string serializedString = this->logRecord.SerializePartialAsString(); return serializedString; } bool -LogRecord::SetField(const gchar *attribute, FilterXObject **value) +LogRecord::set_subscript(FilterXObject *key, FilterXObject **value) { try { - ProtoReflectors reflectors(this->logRecord, attribute); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(this->logRecord, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + FilterXObject *assoc_object = NULL; - if (!otel_converter_by_field_descriptor(reflectors.fieldDescriptor)->Set(&this->logRecord, attribute, *value, - &assoc_object)) + if (!converter->Set(&this->logRecord, key_str, *value, &assoc_object)) return false; filterx_object_unref(*value); @@ -88,32 +91,75 @@ LogRecord::SetField(const gchar *attribute, FilterXObject **value) } catch(const std::exception &ex) { - msg_error("protobuf-field: Failed to set field:", - evt_tag_str("message", ex.what()), - evt_tag_str("field_name", attribute)); return false; } } FilterXObject * -LogRecord::GetField(const gchar *attribute) +LogRecord::get_subscript(FilterXObject *key) { try { - ProtoReflectors reflectors(this->logRecord, attribute); - return otel_converter_by_field_descriptor(reflectors.fieldDescriptor)->Get(&this->logRecord, attribute); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(this->logRecord, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->Get(&this->logRecord, key_str); } catch(const std::exception &ex) { - msg_error("protobuf-field: Failed to get field:", - evt_tag_str("message", ex.what()), - evt_tag_str("field_name", attribute)); return nullptr; } } +bool +LogRecord::unset_key(FilterXObject *key) +{ + try + { + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(this->logRecord, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->Unset(&this->logRecord, key_str); + } + catch(const std::exception &ex) + { + return false; + } +} + +bool +LogRecord::is_key_set(FilterXObject *key) +{ + try + { + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(this->logRecord, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->IsSet(&this->logRecord, key_str); + } + catch(const std::exception &ex) + { + return false; + } +} + +uint64_t +LogRecord::len() const +{ + return get_protobuf_message_set_field_count(logRecord); +} + +bool +LogRecord::iter(FilterXDictIterFunc func, void *user_data) +{ + return iter_on_otel_protobuf_message_fields(logRecord, func, user_data); +} + const opentelemetry::proto::logs::v1::LogRecord & -LogRecord::GetValue() const +LogRecord::get_value() const { return this->logRecord; } @@ -126,49 +172,61 @@ grpc_otel_filterx_logrecord_contruct_new(Plugin *self) return (gpointer) &filterx_otel_logrecord_new_from_args; } -FilterXObject * -_filterx_otel_logrecord_clone(FilterXObject *s) +static void +_free(FilterXObject *s) { FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; - FilterXOtelLogRecord *clone = g_new0(FilterXOtelLogRecord, 1); - filterx_object_init_instance((FilterXObject *) clone, &FILTERX_TYPE_NAME(otel_logrecord)); + delete self->cpp; + self->cpp = NULL; +} - try - { - clone->cpp = new LogRecord(*self->cpp, clone); - } - catch (const std::runtime_error &) - { - g_assert_not_reached(); - } +static gboolean +_set_subscript(FilterXDict *s, FilterXObject *key, FilterXObject **new_value) +{ + FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; - return &clone->super; + return self->cpp->set_subscript(key, new_value); } -static void -_free(FilterXObject *s) +static FilterXObject * +_get_subscript(FilterXDict *s, FilterXObject *key) { FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; - delete self->cpp; - self->cpp = NULL; + return self->cpp->get_subscript(key); } static gboolean -_setattr(FilterXObject *s, FilterXObject *attr, FilterXObject **new_value) +_unset_key(FilterXDict *s, FilterXObject *key) { FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; - return self->cpp->SetField(filterx_string_get_value(attr, NULL), new_value); + return self->cpp->unset_key(key); } -static FilterXObject * -_getattr(FilterXObject *s, FilterXObject *attr) +static gboolean +_is_key_set(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; + + return self->cpp->is_key_set(key); +} + +static guint64 +_len(FilterXDict *s) +{ + FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; + + return self->cpp->len(); +} + +static gboolean +_iter(FilterXDict *s, FilterXDictIterFunc func, gpointer user_data) { FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; - return self->cpp->GetField(filterx_string_get_value(attr, NULL)); + return self->cpp->iter(func, user_data); } static gboolean @@ -182,7 +240,7 @@ _marshal(FilterXObject *s, GString *repr, LogMessageValueType *t) { FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; - std::string serialized = self->cpp->Marshal(); + std::string serialized = self->cpp->marshal(); g_string_truncate(repr, 0); g_string_append_len(repr, serialized.c_str(), serialized.length()); @@ -190,37 +248,86 @@ _marshal(FilterXObject *s, GString *repr, LogMessageValueType *t) return TRUE; } +static void +_init_instance(FilterXOtelLogRecord *self) +{ + filterx_dict_init_instance(&self->super, &FILTERX_TYPE_NAME(otel_logrecord)); + + self->super.get_subscript = _get_subscript; + self->super.set_subscript = _set_subscript; + self->super.unset_key = _unset_key; + self->super.is_key_set = _is_key_set; + self->super.len = _len; + self->super.iter = _iter; +} + +FilterXObject * +_filterx_otel_logrecord_clone(FilterXObject *s) +{ + FilterXOtelLogRecord *self = (FilterXOtelLogRecord *) s; + + FilterXOtelLogRecord *clone = g_new0(FilterXOtelLogRecord, 1); + _init_instance(clone); + + try + { + clone->cpp = new LogRecord(*self->cpp, clone); + } + catch (const std::runtime_error &) + { + g_assert_not_reached(); + } + + return &clone->super.super; +} + FilterXObject * filterx_otel_logrecord_new_from_args(GPtrArray *args) { FilterXOtelLogRecord *self = g_new0(FilterXOtelLogRecord, 1); - filterx_object_init_instance((FilterXObject *) self, &FILTERX_TYPE_NAME(otel_logrecord)); + _init_instance(self); try { if (!args || args->len == 0) - self->cpp = new LogRecord(self); + { + self->cpp = new LogRecord(self); + } else if (args->len == 1) - self->cpp = new LogRecord(self, (FilterXObject *) g_ptr_array_index(args, 0)); + { + FilterXObject *arg = (FilterXObject *) g_ptr_array_index(args, 0); + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(dict))) + { + self->cpp = new LogRecord(self); + if (!filterx_dict_merge(&self->super.super, arg)) + throw std::runtime_error("Failed to merge dict"); + } + else + { + self->cpp = new LogRecord(self, arg); + } + } else - throw std::runtime_error("Invalid number of arguments"); + { + throw std::runtime_error("Invalid number of arguments"); + } } catch (const std::runtime_error &e) { msg_error("FilterX: Failed to create OTel LogRecord object", evt_tag_str("error", e.what())); - filterx_object_unref(&self->super); + filterx_object_unref(&self->super.super); return NULL; } - return &self->super; + return &self->super.super; } -FILTERX_DEFINE_TYPE(otel_logrecord, FILTERX_TYPE_NAME(object), +FILTERX_DEFINE_TYPE(otel_logrecord, FILTERX_TYPE_NAME(dict), .is_mutable = TRUE, .marshal = _marshal, .clone = _filterx_otel_logrecord_clone, .truthy = _truthy, - .getattr = _getattr, - .setattr = _setattr, + .list_factory = filterx_otel_array_new, + .dict_factory = filterx_otel_kvlist_new, .free_fn = _free, ); diff --git a/modules/grpc/otel/filterx/object-otel-logrecord.hpp b/modules/grpc/otel/filterx/object-otel-logrecord.hpp index 0fb5bd54ea..e337440731 100644 --- a/modules/grpc/otel/filterx/object-otel-logrecord.hpp +++ b/modules/grpc/otel/filterx/object-otel-logrecord.hpp @@ -26,7 +26,7 @@ #include "syslog-ng.h" #include "compat/cpp-start.h" -#include "filterx/filterx-object.h" +#include "filterx/object-dict-interface.h" #include "object-otel.h" #include "compat/cpp-end.h" @@ -48,10 +48,14 @@ class LogRecord LogRecord(FilterXOtelLogRecord *super, FilterXObject *protobuf_object); LogRecord(LogRecord &o) = delete; LogRecord(LogRecord &&o) = delete; - bool SetField(const gchar *attribute, FilterXObject **value); - std::string Marshal(void); - FilterXObject *GetField(const gchar *attribute); - const opentelemetry::proto::logs::v1::LogRecord &GetValue() const; + std::string marshal(void); + FilterXObject *get_subscript(FilterXObject *key); + bool set_subscript(FilterXObject *key, FilterXObject **value); + bool unset_key(FilterXObject *key); + bool is_key_set(FilterXObject *key); + uint64_t len() const; + bool iter(FilterXDictIterFunc func, void *user_data); + const opentelemetry::proto::logs::v1::LogRecord &get_value() const; private: FilterXOtelLogRecord *super; opentelemetry::proto::logs::v1::LogRecord logRecord; @@ -66,7 +70,7 @@ class LogRecord struct FilterXOtelLogRecord_ { - FilterXObject super; + FilterXDict super; syslogng::grpc::otel::filterx::LogRecord *cpp; }; diff --git a/modules/grpc/otel/filterx/object-otel-resource.cpp b/modules/grpc/otel/filterx/object-otel-resource.cpp index 6945cad12a..a7847a7a32 100644 --- a/modules/grpc/otel/filterx/object-otel-resource.cpp +++ b/modules/grpc/otel/filterx/object-otel-resource.cpp @@ -60,73 +60,99 @@ Resource::marshal(void) } bool -Resource::set_field(const gchar *attribute, FilterXObject **value) +Resource::set_subscript(FilterXObject *key, FilterXObject **value) { try { - ProtoReflectors reflectors(resource, attribute); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(resource, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + FilterXObject *assoc_object = NULL; - if (!otel_converter_by_field_descriptor(reflectors.fieldDescriptor)->Set(&resource, attribute, *value, &assoc_object)) + if (!converter->Set(&resource, key_str, *value, &assoc_object)) return false; filterx_object_unref(*value); *value = assoc_object; return true; } - catch (const std::invalid_argument &e) + catch(const std::exception &ex) { - msg_error("FilterX: Failed to set OTel Resource field", - evt_tag_str("field_name", attribute), - evt_tag_str("error", e.what())); return false; } } FilterXObject * -Resource::get_field(const gchar *attribute) +Resource::get_subscript(FilterXObject *key) { try { - ProtoReflectors reflectors(resource, attribute); - return otel_converter_by_field_descriptor(reflectors.fieldDescriptor)->Get(&resource, attribute); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(resource, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->Get(&resource, key_str); } - catch (const std::invalid_argument &e) + catch(const std::exception &ex) { - msg_error("FilterX: Failed to get OTel Resource field", - evt_tag_str("field_name", attribute), - evt_tag_str("error", e.what())); return nullptr; } } -const opentelemetry::proto::resource::v1::Resource & -Resource::get_value() const +bool +Resource::unset_key(FilterXObject *key) { - return resource; -} + try + { + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(resource, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); -/* C Wrappers */ + return converter->Unset(&resource, key_str); + } + catch(const std::exception &ex) + { + return false; + } +} -FilterXObject * -_filterx_otel_resource_clone(FilterXObject *s) +bool +Resource::is_key_set(FilterXObject *key) { - FilterXOtelResource *self = (FilterXOtelResource *) s; - - FilterXOtelResource *clone = g_new0(FilterXOtelResource, 1); - filterx_object_init_instance(&clone->super, &FILTERX_TYPE_NAME(otel_resource)); - try { - clone->cpp = new Resource(*self->cpp, self); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(resource, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->IsSet(&resource, key_str); } - catch (const std::runtime_error &) + catch(const std::exception &ex) { - g_assert_not_reached(); + return false; } +} - return &clone->super; +uint64_t +Resource::len() const +{ + return get_protobuf_message_set_field_count(resource); +} + +bool +Resource::iter(FilterXDictIterFunc func, void *user_data) +{ + return iter_on_otel_protobuf_message_fields(resource, func, user_data); } +const opentelemetry::proto::resource::v1::Resource & +Resource::get_value() const +{ + return resource; +} + +/* C Wrappers */ + static void _free(FilterXObject *s) { @@ -137,19 +163,51 @@ _free(FilterXObject *s) } static gboolean -_setattr(FilterXObject *s, FilterXObject *attr, FilterXObject **new_value) +_set_subscript(FilterXDict *s, FilterXObject *key, FilterXObject **new_value) { FilterXOtelResource *self = (FilterXOtelResource *) s; - return self->cpp->set_field(filterx_string_get_value(attr, NULL), new_value); + return self->cpp->set_subscript(key, new_value); } static FilterXObject * -_getattr(FilterXObject *s, FilterXObject *attr) +_get_subscript(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelResource *self = (FilterXOtelResource *) s; + + return self->cpp->get_subscript(key); +} + +static gboolean +_unset_key(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelResource *self = (FilterXOtelResource *) s; + + return self->cpp->unset_key(key); +} + +static gboolean +_is_key_set(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelResource *self = (FilterXOtelResource *) s; + + return self->cpp->is_key_set(key); +} + +static guint64 +_len(FilterXDict *s) { FilterXOtelResource *self = (FilterXOtelResource *) s; - return self->cpp->get_field(filterx_string_get_value(attr, NULL)); + return self->cpp->len(); +} + +static gboolean +_iter(FilterXDict *s, FilterXDictIterFunc func, gpointer user_data) +{ + FilterXOtelResource *self = (FilterXOtelResource *) s; + + return self->cpp->iter(func, user_data); } static gboolean @@ -171,29 +229,78 @@ _marshal(FilterXObject *s, GString *repr, LogMessageValueType *t) return TRUE; } +static void +_init_instance(FilterXOtelResource *self) +{ + filterx_dict_init_instance(&self->super, &FILTERX_TYPE_NAME(otel_resource)); + + self->super.get_subscript = _get_subscript; + self->super.set_subscript = _set_subscript; + self->super.unset_key = _unset_key; + self->super.is_key_set = _is_key_set; + self->super.len = _len; + self->super.iter = _iter; +} + +FilterXObject * +_filterx_otel_resource_clone(FilterXObject *s) +{ + FilterXOtelResource *self = (FilterXOtelResource *) s; + + FilterXOtelResource *clone = g_new0(FilterXOtelResource, 1); + _init_instance(clone); + + try + { + clone->cpp = new Resource(*self->cpp, self); + } + catch (const std::runtime_error &) + { + g_assert_not_reached(); + } + + return &clone->super.super; +} + FilterXObject * filterx_otel_resource_new_from_args(GPtrArray *args) { FilterXOtelResource *s = g_new0(FilterXOtelResource, 1); - filterx_object_init_instance((FilterXObject *)s, &FILTERX_TYPE_NAME(otel_resource)); + _init_instance(s); try { if (!args || args->len == 0) - s->cpp = new Resource(s); + { + s->cpp = new Resource(s); + } else if (args->len == 1) - s->cpp = new Resource(s, (FilterXObject *) g_ptr_array_index(args, 0)); + { + FilterXObject *arg = (FilterXObject *) g_ptr_array_index(args, 0); + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(dict))) + { + s->cpp = new Resource(s); + if (!filterx_dict_merge(&s->super.super, arg)) + throw std::runtime_error("Failed to merge dict"); + } + else + { + s->cpp = new Resource(s, arg); + } + } else - throw std::runtime_error("Invalid number of arguments"); + { + throw std::runtime_error("Invalid number of arguments"); + } } catch (const std::runtime_error &e) { msg_error("FilterX: Failed to create OTel Resource object", evt_tag_str("error", e.what())); - filterx_object_unref(&s->super); + filterx_object_unref(&s->super.super); return NULL; } - return &s->super; + return &s->super.super; } gpointer @@ -202,12 +309,12 @@ grpc_otel_filterx_resource_construct_new(Plugin *self) return (gpointer) &filterx_otel_resource_new_from_args; } -FILTERX_DEFINE_TYPE(otel_resource, FILTERX_TYPE_NAME(object), +FILTERX_DEFINE_TYPE(otel_resource, FILTERX_TYPE_NAME(dict), .is_mutable = TRUE, .marshal = _marshal, .clone = _filterx_otel_resource_clone, .truthy = _truthy, - .getattr = _getattr, - .setattr = _setattr, + .list_factory = filterx_otel_array_new, + .dict_factory = filterx_otel_kvlist_new, .free_fn = _free, ); diff --git a/modules/grpc/otel/filterx/object-otel-resource.hpp b/modules/grpc/otel/filterx/object-otel-resource.hpp index cabf66b405..e424e94fde 100644 --- a/modules/grpc/otel/filterx/object-otel-resource.hpp +++ b/modules/grpc/otel/filterx/object-otel-resource.hpp @@ -26,7 +26,7 @@ #include "syslog-ng.h" #include "compat/cpp-start.h" -#include "filterx/filterx-object.h" +#include "filterx/object-dict-interface.h" #include "object-otel.h" #include "compat/cpp-end.h" @@ -50,8 +50,12 @@ class Resource Resource(Resource &&o) = delete; std::string marshal(); - bool set_field(const gchar *attribute, FilterXObject **value); - FilterXObject *get_field(const gchar *attribute); + bool set_subscript(FilterXObject *key, FilterXObject **value); + FilterXObject *get_subscript(FilterXObject *key); + bool unset_key(FilterXObject *key); + bool is_key_set(FilterXObject *key); + uint64_t len() const; + bool iter(FilterXDictIterFunc func, void *user_data); const opentelemetry::proto::resource::v1::Resource &get_value() const; private: @@ -70,7 +74,7 @@ class Resource struct FilterXOtelResource_ { - FilterXObject super; + FilterXDict super; syslogng::grpc::otel::filterx::Resource *cpp; }; diff --git a/modules/grpc/otel/filterx/object-otel-scope.cpp b/modules/grpc/otel/filterx/object-otel-scope.cpp index 6313dce9fd..6904117c69 100644 --- a/modules/grpc/otel/filterx/object-otel-scope.cpp +++ b/modules/grpc/otel/filterx/object-otel-scope.cpp @@ -60,73 +60,99 @@ Scope::marshal(void) } bool -Scope::set_field(const gchar *attribute, FilterXObject **value) +Scope::set_subscript(FilterXObject *key, FilterXObject **value) { try { - ProtoReflectors reflectors(scope, attribute); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(scope, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + FilterXObject *assoc_object = NULL; - if (!otel_converter_by_field_descriptor(reflectors.fieldDescriptor)->Set(&scope, attribute, *value, &assoc_object)) + if (!converter->Set(&scope, key_str, *value, &assoc_object)) return false; filterx_object_unref(*value); *value = assoc_object; return true; } - catch (const std::invalid_argument &e) + catch(const std::exception &ex) { - msg_error("FilterX: Failed to set OTel Scope field", - evt_tag_str("field_name", attribute), - evt_tag_str("error", e.what())); return false; } } FilterXObject * -Scope::get_field(const gchar *attribute) +Scope::get_subscript(FilterXObject *key) { try { - ProtoReflectors reflectors(scope, attribute); - return otel_converter_by_field_descriptor(reflectors.fieldDescriptor)->Get(&scope, attribute); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(scope, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->Get(&scope, key_str); } - catch (const std::invalid_argument &e) + catch(const std::exception &ex) { - msg_error("FilterX: Failed to get OTel Scope field", - evt_tag_str("field_name", attribute), - evt_tag_str("error", e.what())); return nullptr; } } -const opentelemetry::proto::common::v1::InstrumentationScope & -Scope::get_value() const +bool +Scope::unset_key(FilterXObject *key) { - return scope; -} + try + { + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(scope, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); -/* C Wrappers */ + return converter->Unset(&scope, key_str); + } + catch(const std::exception &ex) + { + return false; + } +} -FilterXObject * -_filterx_otel_scope_clone(FilterXObject *s) +bool +Scope::is_key_set(FilterXObject *key) { - FilterXOtelScope *self = (FilterXOtelScope *) s; - - FilterXOtelScope *clone = g_new0(FilterXOtelScope, 1); - filterx_object_init_instance(&clone->super, &FILTERX_TYPE_NAME(otel_scope)); - try { - clone->cpp = new Scope(*self->cpp, self); + std::string key_str = extract_string_from_object(key); + ProtoReflectors reflectors(scope, key_str); + ProtobufField *converter = otel_converter_by_field_descriptor(reflectors.fieldDescriptor); + + return converter->IsSet(&scope, key_str); } - catch (const std::runtime_error &) + catch(const std::exception &ex) { - g_assert_not_reached(); + return false; } +} - return &clone->super; +uint64_t +Scope::len() const +{ + return get_protobuf_message_set_field_count(scope); +} + +bool +Scope::iter(FilterXDictIterFunc func, void *user_data) +{ + return iter_on_otel_protobuf_message_fields(scope, func, user_data); } +const opentelemetry::proto::common::v1::InstrumentationScope & +Scope::get_value() const +{ + return scope; +} + +/* C Wrappers */ + static void _free(FilterXObject *s) { @@ -137,19 +163,51 @@ _free(FilterXObject *s) } static gboolean -_setattr(FilterXObject *s, FilterXObject *attr, FilterXObject **new_value) +_set_subscript(FilterXDict *s, FilterXObject *key, FilterXObject **new_value) { FilterXOtelScope *self = (FilterXOtelScope *) s; - return self->cpp->set_field(filterx_string_get_value(attr, NULL), new_value); + return self->cpp->set_subscript(key, new_value); } static FilterXObject * -_getattr(FilterXObject *s, FilterXObject *attr) +_get_subscript(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelScope *self = (FilterXOtelScope *) s; + + return self->cpp->get_subscript(key); +} + +static gboolean +_unset_key(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelScope *self = (FilterXOtelScope *) s; + + return self->cpp->unset_key(key); +} + +static gboolean +_is_key_set(FilterXDict *s, FilterXObject *key) +{ + FilterXOtelScope *self = (FilterXOtelScope *) s; + + return self->cpp->is_key_set(key); +} + +static guint64 +_len(FilterXDict *s) { FilterXOtelScope *self = (FilterXOtelScope *) s; - return self->cpp->get_field(filterx_string_get_value(attr, NULL)); + return self->cpp->len(); +} + +static gboolean +_iter(FilterXDict *s, FilterXDictIterFunc func, gpointer user_data) +{ + FilterXOtelScope *self = (FilterXOtelScope *) s; + + return self->cpp->iter(func, user_data); } static gboolean @@ -171,29 +229,78 @@ _marshal(FilterXObject *s, GString *repr, LogMessageValueType *t) return TRUE; } +static void +_init_instance(FilterXOtelScope *self) +{ + filterx_dict_init_instance(&self->super, &FILTERX_TYPE_NAME(otel_scope)); + + self->super.set_subscript = _set_subscript; + self->super.get_subscript = _get_subscript; + self->super.unset_key = _unset_key; + self->super.is_key_set = _is_key_set; + self->super.len = _len; + self->super.iter = _iter; +} + +FilterXObject * +_filterx_otel_scope_clone(FilterXObject *s) +{ + FilterXOtelScope *self = (FilterXOtelScope *) s; + + FilterXOtelScope *clone = g_new0(FilterXOtelScope, 1); + _init_instance(clone); + + try + { + clone->cpp = new Scope(*self->cpp, self); + } + catch (const std::runtime_error &) + { + g_assert_not_reached(); + } + + return &clone->super.super; +} + FilterXObject * filterx_otel_scope_new_from_args(GPtrArray *args) { FilterXOtelScope *s = g_new0(FilterXOtelScope, 1); - filterx_object_init_instance((FilterXObject *)s, &FILTERX_TYPE_NAME(otel_scope)); + _init_instance(s); try { if (!args || args->len == 0) - s->cpp = new Scope(s); + { + s->cpp = new Scope(s); + } else if (args->len == 1) - s->cpp = new Scope(s, (FilterXObject *) g_ptr_array_index(args, 0)); + { + FilterXObject *arg = (FilterXObject *) g_ptr_array_index(args, 0); + if (filterx_object_is_type(arg, &FILTERX_TYPE_NAME(dict))) + { + s->cpp = new Scope(s); + if (!filterx_dict_merge(&s->super.super, arg)) + throw std::runtime_error("Failed to merge dict"); + } + else + { + s->cpp = new Scope(s, arg); + } + } else - throw std::runtime_error("Invalid number of arguments"); + { + throw std::runtime_error("Invalid number of arguments"); + } } catch (const std::runtime_error &e) { msg_error("FilterX: Failed to create OTel Scope object", evt_tag_str("error", e.what())); - filterx_object_unref(&s->super); + filterx_object_unref(&s->super.super); return NULL; } - return &s->super; + return &s->super.super; } gpointer @@ -202,12 +309,12 @@ grpc_otel_filterx_scope_construct_new(Plugin *self) return (gpointer) &filterx_otel_scope_new_from_args; } -FILTERX_DEFINE_TYPE(otel_scope, FILTERX_TYPE_NAME(object), +FILTERX_DEFINE_TYPE(otel_scope, FILTERX_TYPE_NAME(dict), .is_mutable = TRUE, .marshal = _marshal, .clone = _filterx_otel_scope_clone, .truthy = _truthy, - .getattr = _getattr, - .setattr = _setattr, + .list_factory = filterx_otel_array_new, + .dict_factory = filterx_otel_kvlist_new, .free_fn = _free, ); diff --git a/modules/grpc/otel/filterx/object-otel-scope.hpp b/modules/grpc/otel/filterx/object-otel-scope.hpp index 62dc3a9876..306c3ecc47 100644 --- a/modules/grpc/otel/filterx/object-otel-scope.hpp +++ b/modules/grpc/otel/filterx/object-otel-scope.hpp @@ -26,7 +26,7 @@ #include "syslog-ng.h" #include "compat/cpp-start.h" -#include "filterx/filterx-object.h" +#include "filterx/object-dict-interface.h" #include "object-otel.h" #include "compat/cpp-end.h" @@ -50,8 +50,12 @@ class Scope Scope(Scope &&o) = delete; std::string marshal(); - bool set_field(const gchar *attribute, FilterXObject **value); - FilterXObject *get_field(const gchar *attribute); + bool set_subscript(FilterXObject *key, FilterXObject **value); + FilterXObject *get_subscript(FilterXObject *key); + bool unset_key(FilterXObject *key); + bool is_key_set(FilterXObject *key); + uint64_t len() const; + bool iter(FilterXDictIterFunc func, void *user_data); const opentelemetry::proto::common::v1::InstrumentationScope &get_value() const; private: @@ -70,7 +74,7 @@ class Scope struct FilterXOtelScope_ { - FilterXObject super; + FilterXDict super; syslogng::grpc::otel::filterx::Scope *cpp; }; diff --git a/modules/grpc/otel/filterx/otel-field.cpp b/modules/grpc/otel/filterx/otel-field.cpp index 7130375a7c..6d09b2c498 100644 --- a/modules/grpc/otel/filterx/otel-field.cpp +++ b/modules/grpc/otel/filterx/otel-field.cpp @@ -333,3 +333,44 @@ ProtobufField *syslogng::grpc::otel::otel_converter_by_field_descriptor(const Fi const FieldDescriptor::Type fieldType = fd->type(); return otel_converter_by_type(fieldType); } + +bool +syslogng::grpc::otel::iter_on_otel_protobuf_message_fields(google::protobuf::Message &message, FilterXDictIterFunc func, + void *user_data) +{ + const google::protobuf::Reflection *reflection = message.GetReflection(); + std::vector fields; + reflection->ListFields(message, &fields); + + try + { + for (const google::protobuf::FieldDescriptor *field : fields) + { + const std::string &name = field->name(); + ProtoReflectors field_reflectors(message, name); + ProtobufField *converter = syslogng::grpc::otel::otel_converter_by_field_descriptor(field_reflectors.fieldDescriptor); + + FilterXObject *key = filterx_string_new(name.c_str(), name.size()); + FilterXObject *value = converter->Get(&message, name); + if (!value) + { + filterx_object_unref(key); + return false; + } + + bool success = func(key, value, user_data); + + filterx_object_unref(key); + filterx_object_unref(value); + + if (!success) + return false; + } + } + catch (const std::exception &) + { + return false; + } + + return true; +} diff --git a/modules/grpc/otel/filterx/otel-field.hpp b/modules/grpc/otel/filterx/otel-field.hpp index 8231b8758b..66da5114ad 100644 --- a/modules/grpc/otel/filterx/otel-field.hpp +++ b/modules/grpc/otel/filterx/otel-field.hpp @@ -26,6 +26,10 @@ #include "syslog-ng.h" #include "protobuf-field.hpp" +#include "compat/cpp-start.h" +#include "filterx/object-dict-interface.h" +#include "compat/cpp-end.h" + namespace syslogng { namespace grpc { namespace otel { @@ -50,6 +54,9 @@ extern AnyField any_field_converter; ProtobufField *otel_converter_by_type(FieldDescriptor::Type fieldType); ProtobufField *otel_converter_by_field_descriptor(const FieldDescriptor *fd); +bool iter_on_otel_protobuf_message_fields(google::protobuf::Message &message, FilterXDictIterFunc func, + void *user_data); + } } } diff --git a/modules/grpc/otel/filterx/protobuf-field.cpp b/modules/grpc/otel/filterx/protobuf-field.cpp index e6bbcd54d9..69d52af910 100644 --- a/modules/grpc/otel/filterx/protobuf-field.cpp +++ b/modules/grpc/otel/filterx/protobuf-field.cpp @@ -25,6 +25,7 @@ #include "compat/cpp-start.h" #include "filterx/object-string.h" +#include "filterx/object-message-value.h" #include "filterx/object-datetime.h" #include "filterx/object-primitive.h" #include "scratch-buffers.h" @@ -365,3 +366,27 @@ ProtobufField *syslogng::grpc::otel::protobuf_converter_by_type(google::protobuf g_assert(fieldType <= google::protobuf::FieldDescriptor::MAX_TYPE && fieldType > 0); return all_protobuf_converters()[fieldType - 1].get(); } + +std::string +syslogng::grpc::otel::extract_string_from_object(FilterXObject *object) +{ + gsize len; + const gchar *key_c_str = filterx_string_get_value(object, &len); + + if (!key_c_str) + key_c_str = filterx_message_value_get_value(object, &len); + + if (!key_c_str) + throw std::runtime_error("not a string instance"); + + return std::string{key_c_str, len}; +} + +uint64_t +syslogng::grpc::otel::get_protobuf_message_set_field_count(const google::protobuf::Message &message) +{ + const google::protobuf::Reflection *reflection = message.GetReflection(); + std::vector fields; + reflection->ListFields(message, &fields); + return fields.size(); +} diff --git a/modules/grpc/otel/filterx/protobuf-field.hpp b/modules/grpc/otel/filterx/protobuf-field.hpp index 7bc8c5716e..ba181b9bba 100644 --- a/modules/grpc/otel/filterx/protobuf-field.hpp +++ b/modules/grpc/otel/filterx/protobuf-field.hpp @@ -43,7 +43,7 @@ struct ProtoReflectors const google::protobuf::Descriptor *descriptor; const google::protobuf::FieldDescriptor *fieldDescriptor; google::protobuf::FieldDescriptor::Type fieldType; - ProtoReflectors(const google::protobuf::Message &message, std::string fieldName) + ProtoReflectors(const google::protobuf::Message &message, const std::string &fieldName) { this->reflection = message.GetReflection(); this->descriptor = message.GetDescriptor(); @@ -71,7 +71,7 @@ struct ProtoReflectors class ProtobufField { public: - FilterXObject *Get(google::protobuf::Message *message, std::string fieldName) + FilterXObject *Get(google::protobuf::Message *message, const std::string &fieldName) { try { @@ -84,7 +84,8 @@ class ProtobufField return nullptr; } }; - bool Set(google::protobuf::Message *message, std::string fieldName, FilterXObject *object, FilterXObject **assoc_object) + bool Set(google::protobuf::Message *message, const std::string &fieldName, FilterXObject *object, + FilterXObject **assoc_object) { try { @@ -103,6 +104,33 @@ class ProtobufField return false; } } + bool Unset(google::protobuf::Message *message, const std::string &fieldName) + { + try + { + ProtoReflectors reflectors(*message, fieldName); + reflectors.reflection->ClearField(message, reflectors.fieldDescriptor); + return true; + } + catch(const std::exception &ex) + { + msg_error("protobuf-field: Failed to unset field:", evt_tag_str("message", ex.what())); + return false; + } + } + bool IsSet(google::protobuf::Message *message, const std::string &fieldName) + { + try + { + ProtoReflectors reflectors(*message, fieldName); + return reflectors.reflection->HasField(*message, reflectors.fieldDescriptor); + } + catch(const std::exception &ex) + { + msg_error("protobuf-field: Failed to check field:", evt_tag_str("message", ex.what())); + return false; + } + } virtual ~ProtobufField() {} protected: @@ -114,6 +142,10 @@ class ProtobufField std::unique_ptr *all_protobuf_converters(); ProtobufField *protobuf_converter_by_type(google::protobuf::FieldDescriptor::Type fieldType); +std::string extract_string_from_object(FilterXObject *object); + +uint64_t get_protobuf_message_set_field_count(const google::protobuf::Message &message); + } } } diff --git a/modules/grpc/otel/tests/test-otel-filterx.cpp b/modules/grpc/otel/tests/test-otel-filterx.cpp index 23a94937b5..7d17542181 100644 --- a/modules/grpc/otel/tests/test-otel-filterx.cpp +++ b/modules/grpc/otel/tests/test-otel-filterx.cpp @@ -155,9 +155,9 @@ Test(otel_filterx, logrecord_empty) FilterXOtelLogRecord *filterx_otel_logrecord = (FilterXOtelLogRecord *) filterx_otel_logrecord_new_from_args(NULL); cr_assert(filterx_otel_logrecord); - cr_assert(MessageDifferencer::Equals(LogRecord(), filterx_otel_logrecord->cpp->GetValue())); + cr_assert(MessageDifferencer::Equals(LogRecord(), filterx_otel_logrecord->cpp->get_value())); - filterx_object_unref(&filterx_otel_logrecord->super); + filterx_object_unref(&filterx_otel_logrecord->super.super); } Test(otel_filterx, logrecord_from_protobuf) @@ -176,10 +176,10 @@ Test(otel_filterx, logrecord_from_protobuf) FilterXOtelLogRecord *filterx_otel_logrecord = (FilterXOtelLogRecord *) filterx_otel_logrecord_new_from_args(args); cr_assert(filterx_otel_logrecord); - const LogRecord &log_record_from_filterx = filterx_otel_logrecord->cpp->GetValue(); + const LogRecord &log_record_from_filterx = filterx_otel_logrecord->cpp->get_value(); cr_assert(MessageDifferencer::Equals(log_record, log_record_from_filterx)); - filterx_object_unref(&filterx_otel_logrecord->super); + filterx_object_unref(&filterx_otel_logrecord->super.super); g_ptr_array_free(args, TRUE); } @@ -217,6 +217,85 @@ Test(otel_filterx, logrecord_too_many_args) g_ptr_array_free(args, TRUE); } +Test(otel_filterx, logrecord_len_and_unset_and_is_key_set) +{ + FilterXObject *logrecord = filterx_otel_logrecord_new_from_args(NULL); + FilterXObject *body = filterx_string_new("body", -1); + FilterXObject *body_val = filterx_string_new("body_val", -1); + FilterXObject *time_unix_nano = filterx_string_new("time_unix_nano", -1); + FilterXObject *time_unix_nano_val = filterx_integer_new(123); + + guint64 len; + cr_assert(filterx_object_len(logrecord, &len)); + cr_assert_eq(len, 0); + + cr_assert_not(filterx_object_is_key_set(logrecord, body)); + cr_assert(filterx_object_set_subscript(logrecord, body, &body_val)); + cr_assert(filterx_object_len(logrecord, &len)); + cr_assert_eq(len, 1); + cr_assert(filterx_object_is_key_set(logrecord, body)); + + cr_assert_not(filterx_object_is_key_set(logrecord, time_unix_nano)); + cr_assert(filterx_object_set_subscript(logrecord, time_unix_nano, &time_unix_nano_val)); + cr_assert(filterx_object_len(logrecord, &len)); + cr_assert_eq(len, 2); + cr_assert(filterx_object_is_key_set(logrecord, time_unix_nano)); + + cr_assert(filterx_object_unset_key(logrecord, body)); + cr_assert(filterx_object_len(logrecord, &len)); + cr_assert_eq(len, 1); + cr_assert_not(filterx_object_is_key_set(logrecord, body)); + + cr_assert(filterx_object_unset_key(logrecord, time_unix_nano)); + cr_assert(filterx_object_len(logrecord, &len)); + cr_assert_eq(len, 0); + cr_assert_not(filterx_object_is_key_set(logrecord, time_unix_nano)); + + filterx_object_unref(time_unix_nano); + filterx_object_unref(time_unix_nano_val); + filterx_object_unref(body); + filterx_object_unref(body_val); + filterx_object_unref(logrecord); +} + +static gboolean +_append_to_str(FilterXObject *key, FilterXObject *value, gpointer user_data) +{ + GString *output = (GString *) user_data; + + LogMessageValueType type; + filterx_object_marshal_append(key, output, &type); + g_string_append_c(output, '\n'); + filterx_object_marshal_append(value, output, &type); + g_string_append_c(output, '\n'); + + return TRUE; +} + +Test(otel_filterx, logrecord_iter) +{ + FilterXObject *logrecord = filterx_otel_logrecord_new_from_args(NULL); + FilterXObject *body = filterx_string_new("body", -1); + FilterXObject *body_val = filterx_string_new("body_val", -1); + FilterXObject *time_unix_nano = filterx_string_new("time_unix_nano", -1); + FilterXObject *time_unix_nano_val = filterx_integer_new(123); + + cr_assert(filterx_object_set_subscript(logrecord, body, &body_val)); + cr_assert(filterx_object_set_subscript(logrecord, time_unix_nano, &time_unix_nano_val)); + + GString *output = g_string_new(NULL); + cr_assert(filterx_dict_iter(logrecord, _append_to_str, output)); + + cr_assert_str_eq(output->str, "time_unix_nano\n0.000123+00:00\nbody\nbody_val\n"); + + g_string_free(output, TRUE); + filterx_object_unref(time_unix_nano); + filterx_object_unref(time_unix_nano_val); + filterx_object_unref(body); + filterx_object_unref(body_val); + filterx_object_unref(logrecord); +} + /* Resource */ @@ -228,7 +307,7 @@ Test(otel_filterx, resource_empty) cr_assert(MessageDifferencer::Equals(opentelemetry::proto::resource::v1::Resource(), filterx_otel_resource->cpp->get_value())); - filterx_object_unref(&filterx_otel_resource->super); + filterx_object_unref(&filterx_otel_resource->super.super); } Test(otel_filterx, resource_from_protobuf) @@ -249,7 +328,7 @@ Test(otel_filterx, resource_from_protobuf) const opentelemetry::proto::resource::v1::Resource &resource_from_filterx = filterx_otel_resource->cpp->get_value(); cr_assert(MessageDifferencer::Equals(resource, resource_from_filterx)); - filterx_object_unref(&filterx_otel_resource->super); + filterx_object_unref(&filterx_otel_resource->super.super); g_ptr_array_free(args, TRUE); } @@ -350,6 +429,51 @@ Test(otel_filterx, resource_set_field) filterx_object_unref(filterx_otel_resource); } +Test(otel_filterx, resource_len_and_unset_and_is_key_set) +{ + FilterXObject *resource = filterx_otel_resource_new_from_args(NULL); + FilterXObject *dropped_attributes_count = filterx_string_new("dropped_attributes_count", -1); + FilterXObject *dropped_attributes_count_val = filterx_integer_new(42); + + guint64 len; + cr_assert(filterx_object_len(resource, &len)); + cr_assert_eq(len, 0); + + cr_assert_not(filterx_object_is_key_set(resource, dropped_attributes_count)); + cr_assert(filterx_object_set_subscript(resource, dropped_attributes_count, &dropped_attributes_count_val)); + cr_assert(filterx_object_len(resource, &len)); + cr_assert_eq(len, 1); + cr_assert(filterx_object_is_key_set(resource, dropped_attributes_count)); + + cr_assert(filterx_object_unset_key(resource, dropped_attributes_count)); + cr_assert(filterx_object_len(resource, &len)); + cr_assert_eq(len, 0); + cr_assert_not(filterx_object_is_key_set(resource, dropped_attributes_count)); + + filterx_object_unref(dropped_attributes_count); + filterx_object_unref(dropped_attributes_count_val); + filterx_object_unref(resource); +} + +Test(otel_filterx, resource_iter) +{ + FilterXObject *resource = filterx_otel_resource_new_from_args(NULL); + FilterXObject *dropped_attributes_count = filterx_string_new("dropped_attributes_count", -1); + FilterXObject *dropped_attributes_count_val = filterx_integer_new(42); + + cr_assert(filterx_object_set_subscript(resource, dropped_attributes_count, &dropped_attributes_count_val)); + + GString *output = g_string_new(NULL); + cr_assert(filterx_dict_iter(resource, _append_to_str, output)); + + cr_assert_str_eq(output->str, "dropped_attributes_count\n42\n"); + + g_string_free(output, TRUE); + filterx_object_unref(dropped_attributes_count); + filterx_object_unref(dropped_attributes_count_val); + filterx_object_unref(resource); +} + /* Scope */ @@ -361,7 +485,7 @@ Test(otel_filterx, scope_empty) cr_assert(MessageDifferencer::Equals(opentelemetry::proto::common::v1::InstrumentationScope(), filterx_otel_scope->cpp->get_value())); - filterx_object_unref(&filterx_otel_scope->super); + filterx_object_unref(&filterx_otel_scope->super.super); } Test(otel_filterx, scope_from_protobuf) @@ -384,7 +508,7 @@ Test(otel_filterx, scope_from_protobuf) const opentelemetry::proto::common::v1::InstrumentationScope &scope_from_filterx = filterx_otel_scope->cpp->get_value(); cr_assert(MessageDifferencer::Equals(scope, scope_from_filterx)); - filterx_object_unref(&filterx_otel_scope->super); + filterx_object_unref(&filterx_otel_scope->super.super); g_ptr_array_free(args, TRUE); } @@ -726,7 +850,7 @@ Test(otel_filterx, kvlist_through_logrecord) KeyValue *expected_logrecord_inner_attr_2 = expected_logrecord_inner_kvlist->add_values(); expected_logrecord_inner_attr_2->set_key("key_2"); expected_logrecord_inner_attr_2->mutable_value()->set_string_value("baz"); - cr_assert(MessageDifferencer::Equals(expected_logrecord, ((FilterXOtelLogRecord *) fx_logrecord)->cpp->GetValue())); + cr_assert(MessageDifferencer::Equals(expected_logrecord, ((FilterXOtelLogRecord *) fx_logrecord)->cpp->get_value())); KeyValueList expected_kvlist; KeyValue *expected_kvlist_value_0 = expected_kvlist.add_values(); @@ -757,6 +881,72 @@ Test(otel_filterx, kvlist_through_logrecord) filterx_object_unref(fx_logrecord); } +Test(otel_filterx, scope_len_and_unset_and_is_key_set) +{ + FilterXObject *scope = filterx_otel_scope_new_from_args(NULL); + FilterXObject *name = filterx_string_new("name", -1); + FilterXObject *name_val = filterx_string_new("name_val", -1); + FilterXObject *version = filterx_string_new("version", -1); + FilterXObject *version_val = filterx_string_new("version_val", -1); + + guint64 len; + cr_assert(filterx_object_len(scope, &len)); + cr_assert_eq(len, 0); + + cr_assert_not(filterx_object_is_key_set(scope, name)); + cr_assert(filterx_object_set_subscript(scope, name, &name_val)); + cr_assert(filterx_object_len(scope, &len)); + cr_assert_eq(len, 1); + cr_assert(filterx_object_is_key_set(scope, name)); + + cr_assert_not(filterx_object_is_key_set(scope, version)); + cr_assert(filterx_object_set_subscript(scope, version, &version_val)); + cr_assert(filterx_object_len(scope, &len)); + cr_assert_eq(len, 2); + cr_assert(filterx_object_is_key_set(scope, version)); + + cr_assert(filterx_object_unset_key(scope, name)); + cr_assert(filterx_object_len(scope, &len)); + cr_assert_eq(len, 1); + cr_assert_not(filterx_object_is_key_set(scope, name)); + + cr_assert(filterx_object_unset_key(scope, version)); + cr_assert(filterx_object_len(scope, &len)); + cr_assert_eq(len, 0); + cr_assert_not(filterx_object_is_key_set(scope, version)); + + filterx_object_unref(version); + filterx_object_unref(version_val); + filterx_object_unref(name); + filterx_object_unref(name_val); + filterx_object_unref(scope); +} + +Test(otel_filterx, scope_iter) +{ + FilterXObject *scope = filterx_otel_scope_new_from_args(NULL); + FilterXObject *name = filterx_string_new("name", -1); + FilterXObject *name_val = filterx_string_new("name_val", -1); + FilterXObject *version = filterx_string_new("version", -1); + FilterXObject *version_val = filterx_string_new("version_val", -1); + + cr_assert(filterx_object_set_subscript(scope, name, &name_val)); + cr_assert(filterx_object_set_subscript(scope, version, &version_val)); + + GString *output = g_string_new(NULL); + cr_assert(filterx_dict_iter(scope, _append_to_str, output)); + + cr_assert_str_eq(output->str, "name\nname_val\nversion\nversion_val\n"); + + g_string_free(output, TRUE); + filterx_object_unref(version); + filterx_object_unref(version_val); + filterx_object_unref(name); + filterx_object_unref(name_val); + filterx_object_unref(scope); +} + + /* Array */ Test(otel_filterx, array_empty) @@ -973,7 +1163,7 @@ Test(otel_filterx, array_through_logrecord) expected_logrecord_array->add_values()->set_string_value("bar"); ArrayValue *inner_array = expected_logrecord_array->add_values()->mutable_array_value(); inner_array->add_values()->set_string_value("baz"); - cr_assert(MessageDifferencer::Equals(expected_logrecord, ((FilterXOtelLogRecord *) fx_logrecord)->cpp->GetValue())); + cr_assert(MessageDifferencer::Equals(expected_logrecord, ((FilterXOtelLogRecord *) fx_logrecord)->cpp->get_value())); ArrayValue expected_array; expected_array.add_values()->set_string_value("foo"); diff --git a/tests/light/functional_tests/filterx/test_filterx.py b/tests/light/functional_tests/filterx/test_filterx.py index 0479bd0b59..4d88316f0a 100644 --- a/tests/light/functional_tests/filterx/test_filterx.py +++ b/tests/light/functional_tests/filterx/test_filterx.py @@ -330,6 +330,153 @@ def test_otel_to_json(config, syslog_ng): assert file_true.read_log() == '{"otel_kvl":{"foo":42,"bar":1337},"otel_arr":[1,2,3,4]}\n' +def test_otel_resource_scope_log_to_json(config, syslog_ng): + (file_true, file_false) = create_config( + config, r""" + log = otel_logrecord(); + log += { + "body": "fit", + "time_unix_nano": 123456789, + "attributes": { + "answer": 42, + "cool": true + } + }; + + resource = otel_resource(); + resource.attributes["host.name"] = "szami.to"; + + scope = otel_scope({ + "name": "oszcillo", + "version": "ceta" + }); + + resource_js = json(resource); + scope_js = json(scope); + log_js = json(log); + + js = json({ + "in_dict_generator": { + "resource": resource, + "scope": scope, + "log": log, + }, + "from_json_cast": { + "resource": resource_js, + "scope": scope_js, + "log": log_js, + } + }); + + $MSG = js; + """, + ) + syslog_ng.start(config) + + assert file_true.get_stats()["processed"] == 1 + assert "processed" not in file_false.get_stats() + assert json.loads(file_true.read_log()) == { + "in_dict_generator": { + "resource": { + "attributes": { + "host.name": "szami.to", + }, + }, + "scope": { + "name": "oszcillo", + "version": "ceta", + }, + "log": { + "time_unix_nano": "123.456789+00:00", + "body": "fit", + "attributes": { + "answer": 42, + "cool": True, + }, + }, + }, + "from_json_cast": { + "resource": { + "attributes": { + "host.name": "szami.to", + }, + }, + "scope": { + "name": "oszcillo", + "version": "ceta", + }, + "log": { + "time_unix_nano": "123.456789+00:00", + "body": "fit", + "attributes": { + "answer": 42, + "cool": True, + }, + }, + }, + } + + +def test_json_to_otel_resource_scope_log(config, syslog_ng): + (file_true, file_false) = create_config( + config, r""" + log_js = json(); + log_js += { + "body": "fit", + "time_unix_nano": 123456789, + "attributes": { + "answer": 42, + "cool": true + } + }; + + resource_js = json(); + resource_js.attributes = {}; + resource_js.attributes["host.name"] = "szami.to"; + + scope_js = json({ + "name": "oszcillo", + "version": "ceta" + }); + + resource = otel_resource(resource_js); + scope = otel_scope(scope_js); + log = otel_logrecord(log_js); + + js = json({ + "resource": resource, + "scope": scope, + "log": log, + }); + + $MSG = js; + """, + ) + syslog_ng.start(config) + + assert file_true.get_stats()["processed"] == 1 + assert "processed" not in file_false.get_stats() + assert json.loads(file_true.read_log()) == { + "resource": { + "attributes": { + "host.name": "szami.to", + }, + }, + "scope": { + "name": "oszcillo", + "version": "ceta", + }, + "log": { + "time_unix_nano": "123.456789+00:00", + "body": "fit", + "attributes": { + "answer": 42, + "cool": True, + }, + }, + } + + def test_simple_true_condition(config, syslog_ng): (file_true, file_false) = create_config(config, """ true; """) syslog_ng.start(config)