diff --git a/exporters/otlp/CMakeLists.txt b/exporters/otlp/CMakeLists.txt index 83b35f16a7..be17395190 100644 --- a/exporters/otlp/CMakeLists.txt +++ b/exporters/otlp/CMakeLists.txt @@ -332,6 +332,9 @@ if(BUILD_TESTING) else() find_library(GMOCK_LIB gmock PATH_SUFFIXES lib) endif() + if(NOT GMOCK_LIB) + unset(GMOCK_LIB CACHE) + endif() endif() if(WITH_OTLP_GRPC) diff --git a/exporters/zipkin/CMakeLists.txt b/exporters/zipkin/CMakeLists.txt index 60f8a80a2a..19019a0378 100644 --- a/exporters/zipkin/CMakeLists.txt +++ b/exporters/zipkin/CMakeLists.txt @@ -59,6 +59,9 @@ if(BUILD_TESTING) else() find_library(GMOCK_LIB gmock PATH_SUFFIXES lib) endif() + if(NOT GMOCK_LIB) + unset(GMOCK_LIB CACHE) + endif() add_executable(zipkin_exporter_test test/zipkin_exporter_test.cc) diff --git a/sdk/include/opentelemetry/sdk/common/attribute_utils.h b/sdk/include/opentelemetry/sdk/common/attribute_utils.h index 86b507a66b..4f5f82902c 100644 --- a/sdk/include/opentelemetry/sdk/common/attribute_utils.h +++ b/sdk/include/opentelemetry/sdk/common/attribute_utils.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,8 @@ using OwnedAttributeValue = nostd::variant, std::vector>; +using OwnedAttributeView = nostd::variant, std::unique_ptr>; + enum OwnedAttributeType { kTypeBool, @@ -215,6 +218,248 @@ class OrderedAttributeMap : public std::map AttributeConverter converter_; }; +/** + * Class for storing attributes. + */ +struct MixedAttributeMapStorage +{ + std::unordered_map attributes; + AttributeMap owned_attributes; + std::unordered_map owened_attributes_view; +}; + +/** + * Set an owned copy (OwnedAttributeValue) and attribute view of a non-owning AttributeValue. + */ +class MixedAttributeViewSetter +{ +public: + inline MixedAttributeViewSetter(const nostd::string_view &key, + MixedAttributeMapStorage &storage, + AttributeConverter &converter) noexcept + : key_(&key), storage_(&storage), converter_(&converter) + {} + + void operator()(bool v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + storage_->attributes[std::string(*key_)] = v; + } + + void operator()(int32_t v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + storage_->attributes[std::string(*key_)] = v; + } + + void operator()(uint32_t v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + storage_->attributes[std::string(*key_)] = v; + } + + void operator()(int64_t v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + storage_->attributes[std::string(*key_)] = v; + } + + void operator()(uint64_t v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + storage_->attributes[std::string(*key_)] = v; + } + + void operator()(double v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + storage_->attributes[std::string(*key_)] = v; + } + + void operator()(nostd::string_view v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get(owned_value); + } + + void operator()(const char *v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get(owned_value).c_str(); + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get>(owned_value); + } + + void operator()(nostd::span v) + { + storage_->owned_attributes[std::string(*key_)] = (*converter_)(v); + if (v.empty()) + { + storage_->attributes[std::string(*key_)] = nostd::span{}; + } + else + { + std::unique_ptr owned_view{new bool[v.size()]}; + for (size_t i = 0; i < v.size(); i++) + { + owned_view[i] = v[i]; + } + + storage_->attributes[std::string(*key_)] = + nostd::span{owned_view.get(), v.size()}; + storage_->owened_attributes_view[std::string(*key_)] = std::move(owned_view); + } + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get>(owned_value); + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get>(owned_value); + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get>(owned_value); + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get>(owned_value); + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + storage_->attributes[std::string(*key_)] = nostd::get>(owned_value); + } + + void operator()(nostd::span v) + { + auto &owned_value = storage_->owned_attributes[std::string(*key_)]; + owned_value = (*converter_)(v); + + if (v.empty()) + { + storage_->attributes[std::string(*key_)] = nostd::span{}; + } + else + { + auto &owned_view = storage_->owened_attributes_view[std::string(*key_)]; + owned_view = std::vector{}; + auto &owned_vector = nostd::get>(owned_view); + + owned_vector.reserve(v.size()); + for (auto &data : nostd::get>(owned_value)) + { + owned_vector.push_back(data); + } + + storage_->attributes[std::string(*key_)] = + nostd::span{owned_vector.data(), owned_vector.size()}; + } + } + +private: + const nostd::string_view *key_; + MixedAttributeMapStorage *storage_; + AttributeConverter *converter_; +}; + +/** + * Class for storing attributes and attribute view. + */ +class MixedAttributeMap +{ +public: + // Construct empty attribute map + MixedAttributeMap() {} + + // Construct attribute map and populate with attributes + MixedAttributeMap(const opentelemetry::common::KeyValueIterable &attributes) : MixedAttributeMap() + { + attributes.ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + nostd::visit(MixedAttributeViewSetter(key, storage_, converter_), value); + return true; + }); + } + + // Construct attribute map and populate with optional attributes + MixedAttributeMap(const opentelemetry::common::KeyValueIterable *attributes) : MixedAttributeMap() + { + if (attributes != nullptr) + { + attributes->ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + nostd::visit(MixedAttributeViewSetter(key, storage_, converter_), value); + return true; + }); + } + } + + // Construct map from initializer list by applying `SetAttribute` transform for every attribute + MixedAttributeMap( + std::initializer_list> + attributes) + : MixedAttributeMap() + { + for (auto &kv : attributes) + { + nostd::visit(MixedAttributeViewSetter(kv.first, storage_, converter_), kv.second); + } + } + + // Returns a reference to this map + const std::unordered_map &GetAttributes() + const noexcept + { + return storage_.attributes; + } + + const AttributeMap &GetOwnedAttributes() const noexcept { return storage_.owned_attributes; } + + // Convert non-owning key-value to owning std::string(key) and OwnedAttributeValue(value) + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept + { + nostd::visit(MixedAttributeViewSetter(key, storage_, converter_), value); + } + + void Reserve(AttributeMap::size_type size) + { + storage_.attributes.reserve(size); + storage_.owned_attributes.reserve(size); + } + + AttributeMap::size_type Size() const noexcept { return storage_.attributes.size(); } + + bool Empty() const noexcept { return storage_.attributes.empty(); } + +private: + MixedAttributeMapStorage storage_; + AttributeConverter converter_; +}; + } // namespace common } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h b/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h index 9831eaa1c9..443aede3d4 100644 --- a/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h +++ b/sdk/include/opentelemetry/sdk/logs/read_write_log_record.h @@ -187,8 +187,9 @@ class ReadWriteLogRecord final : public ReadableLogRecord const opentelemetry::sdk::resource::Resource *resource_; const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_; - std::unordered_map attributes_map_; - opentelemetry::common::AttributeValue body_; + common::MixedAttributeMap attributes_map_; + // We resue the same utility functions of MixedAttributeMap with key="" for the body field + common::MixedAttributeMap body_; opentelemetry::common::SystemTimestamp timestamp_; opentelemetry::common::SystemTimestamp observed_timestamp_; diff --git a/sdk/src/logs/read_write_log_record.cc b/sdk/src/logs/read_write_log_record.cc index 48c9a2699d..98c5239161 100644 --- a/sdk/src/logs/read_write_log_record.cc +++ b/sdk/src/logs/read_write_log_record.cc @@ -29,11 +29,12 @@ ReadWriteLogRecord::ReadWriteLogRecord() : severity_(opentelemetry::logs::Severity::kInvalid), resource_(nullptr), instrumentation_scope_(nullptr), - body_(nostd::string_view()), observed_timestamp_(std::chrono::system_clock::now()), event_id_(0), event_name_("") -{} +{ + body_.SetAttribute("", nostd::string_view()); +} ReadWriteLogRecord::~ReadWriteLogRecord() {} @@ -70,12 +71,12 @@ opentelemetry::logs::Severity ReadWriteLogRecord::GetSeverity() const noexcept void ReadWriteLogRecord::SetBody(const opentelemetry::common::AttributeValue &message) noexcept { - body_ = message; + body_.SetAttribute("", message); } const opentelemetry::common::AttributeValue &ReadWriteLogRecord::GetBody() const noexcept { - return body_; + return body_.GetAttributes().begin()->second; } void ReadWriteLogRecord::SetEventId(int64_t id, nostd::string_view name) noexcept @@ -160,13 +161,13 @@ const opentelemetry::trace::TraceFlags &ReadWriteLogRecord::GetTraceFlags() cons void ReadWriteLogRecord::SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept { - attributes_map_[static_cast(key)] = value; + attributes_map_.SetAttribute(key, value); } const std::unordered_map & ReadWriteLogRecord::GetAttributes() const noexcept { - return attributes_map_; + return attributes_map_.GetAttributes(); } const opentelemetry::sdk::resource::Resource &ReadWriteLogRecord::GetResource() const noexcept diff --git a/sdk/test/common/attribute_utils_test.cc b/sdk/test/common/attribute_utils_test.cc index 9b1f6bd56e..11e6795cd3 100644 --- a/sdk/test/common/attribute_utils_test.cc +++ b/sdk/test/common/attribute_utils_test.cc @@ -7,9 +7,87 @@ #include #include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/common/attribute_utils.h" +namespace +{ + +template +static void CheckExceptOneValue(const opentelemetry::sdk::common::MixedAttributeMap &data, + opentelemetry::nostd::string_view key, + DataType value) +{ + auto iter1 = data.GetAttributes().find(std::string(key)); + EXPECT_TRUE(iter1 != data.GetAttributes().end()); + + auto iter2 = data.GetOwnedAttributes().find(std::string(key)); + EXPECT_TRUE(iter2 != data.GetOwnedAttributes().end()); + + EXPECT_EQ(opentelemetry::nostd::get(iter1->second), value); + EXPECT_EQ(opentelemetry::nostd::get(iter2->second), value); +} + +template +static void CheckExceptOneStringView(const opentelemetry::sdk::common::MixedAttributeMap &data, + opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view value) +{ + auto iter1 = data.GetAttributes().find(std::string(key)); + EXPECT_TRUE(iter1 != data.GetAttributes().end()); + + auto iter2 = data.GetOwnedAttributes().find(std::string(key)); + EXPECT_TRUE(iter2 != data.GetOwnedAttributes().end()); + + EXPECT_EQ(opentelemetry::nostd::get(iter1->second), value); + EXPECT_EQ(opentelemetry::nostd::get(iter2->second), value); +} + +template +static void CheckExceptArrayValue(const opentelemetry::sdk::common::MixedAttributeMap &data, + opentelemetry::nostd::string_view key, + DataType (&value)[DataSize]) +{ + auto iter1 = data.GetAttributes().find(std::string(key)); + EXPECT_TRUE(iter1 != data.GetAttributes().end()); + + auto iter2 = data.GetOwnedAttributes().find(std::string(key)); + EXPECT_TRUE(iter2 != data.GetOwnedAttributes().end()); + + for (size_t i = 0; i < DataSize; ++i) + { + EXPECT_EQ( + opentelemetry::nostd::get>(iter1->second)[i], + value[i]); + EXPECT_EQ(opentelemetry::nostd::get>(iter2->second)[i], value[i]); + } +} + +template +static void CheckExceptArrayString(const opentelemetry::sdk::common::MixedAttributeMap &data, + opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view (&value)[DataSize]) +{ + auto iter1 = data.GetAttributes().find(std::string(key)); + EXPECT_TRUE(iter1 != data.GetAttributes().end()); + + auto iter2 = data.GetOwnedAttributes().find(std::string(key)); + EXPECT_TRUE(iter2 != data.GetOwnedAttributes().end()); + + for (size_t i = 0; i < DataSize; ++i) + { + EXPECT_EQ( + opentelemetry::nostd::get< + opentelemetry::nostd::span>(iter1->second)[i], + value[i]); + EXPECT_EQ(opentelemetry::nostd::get>(iter2->second)[i], value[i]); + } +} + +} // namespace + TEST(AttributeMapTest, DefaultConstruction) { opentelemetry::sdk::common::AttributeMap attribute_map; @@ -55,3 +133,63 @@ TEST(OrderedAttributeMapTest, AttributesConstruction) EXPECT_EQ(opentelemetry::nostd::get(attribute_map.GetAttributes().at(keys[i])), values[i]); } } + +TEST(MixedAttributeMapTest, SetterAndGetter) +{ + opentelemetry::sdk::common::MixedAttributeMap attribute_map; + attribute_map.Reserve(16); + EXPECT_TRUE(attribute_map.Empty()); + + attribute_map.SetAttribute("bool", true); + attribute_map.SetAttribute("int32_t", static_cast(42)); + attribute_map.SetAttribute("int64_t", static_cast(43)); + attribute_map.SetAttribute("uint32_t", static_cast(44)); + attribute_map.SetAttribute("double", static_cast(45.0)); + attribute_map.SetAttribute("uint64_t", static_cast(46)); + attribute_map.SetAttribute("const char *", "const char *"); + attribute_map.SetAttribute("string_view", opentelemetry::nostd::string_view("string_view")); + + bool array_bool[] = {true, false, true}; + uint8_t array_uint8[] = {47, 48}; + int32_t array_int32[] = {48, 49}; + int64_t array_int64[] = {49, 50}; + uint32_t array_uint32[] = {50, 51}; + double array_double[] = {51.0, 52.0}; + uint64_t array_uint64[] = {52, 53}; + + attribute_map.SetAttribute("sbool", opentelemetry::nostd::span(array_bool)); + attribute_map.SetAttribute("suint8_t", opentelemetry::nostd::span(array_uint8)); + attribute_map.SetAttribute("sint32_t", opentelemetry::nostd::span(array_int32)); + attribute_map.SetAttribute("sint64_t", opentelemetry::nostd::span(array_int64)); + attribute_map.SetAttribute("suint32_t", opentelemetry::nostd::span(array_uint32)); + attribute_map.SetAttribute("sdouble", opentelemetry::nostd::span(array_double)); + attribute_map.SetAttribute("suint64_t", opentelemetry::nostd::span(array_uint64)); + + opentelemetry::nostd::string_view array_string_view[] = {"string_view1", "string_view2"}; + attribute_map.SetAttribute( + "sstring_view", + opentelemetry::nostd::span(array_string_view)); + + EXPECT_FALSE(attribute_map.Empty()); + EXPECT_EQ(attribute_map.Size(), 16); + + CheckExceptOneValue(attribute_map, "bool", true); + CheckExceptOneValue(attribute_map, "int32_t", static_cast(42)); + CheckExceptOneValue(attribute_map, "int64_t", static_cast(43)); + CheckExceptOneValue(attribute_map, "uint32_t", static_cast(44)); + CheckExceptOneValue(attribute_map, "double", static_cast(45.0)); + CheckExceptOneValue(attribute_map, "uint64_t", static_cast(46)); + + CheckExceptOneStringView(attribute_map, "const char *", "const char *"); + CheckExceptOneStringView(attribute_map, "string_view", + "string_view"); + + CheckExceptArrayValue(attribute_map, "sbool", array_bool); + CheckExceptArrayValue(attribute_map, "suint8_t", array_uint8); + CheckExceptArrayValue(attribute_map, "sint32_t", array_int32); + CheckExceptArrayValue(attribute_map, "sint64_t", array_int64); + CheckExceptArrayValue(attribute_map, "suint32_t", array_uint32); + CheckExceptArrayValue(attribute_map, "sdouble", array_double); + CheckExceptArrayValue(attribute_map, "suint64_t", array_uint64); + CheckExceptArrayString(attribute_map, "sstring_view", array_string_view); +} diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc index 99ba9f296d..a27afad6e8 100644 --- a/sdk/test/logs/log_record_test.cc +++ b/sdk/test/logs/log_record_test.cc @@ -105,17 +105,23 @@ class TestBodyLogger : public opentelemetry::logs::Logger { if (record) { - last_body_ = static_cast(record.get())->GetBody(); + last_body_.reset(static_cast(record.release())); } } const opentelemetry::common::AttributeValue &GetLastLogRecord() const noexcept { - return last_body_; + if (last_body_) + { + return last_body_->GetBody(); + } + + return empty_; } private: - opentelemetry::common::AttributeValue last_body_; + opentelemetry::common::AttributeValue empty_ = nostd::string_view(); + nostd::unique_ptr last_body_; }; // Define a basic LoggerProvider class that returns an instance of the logger class defined above @@ -177,8 +183,8 @@ TEST(LogBody, BodyConversation) opentelemetry::nostd::holds_alternative(real_logger->GetLastLogRecord())); if (opentelemetry::nostd::holds_alternative(real_logger->GetLastLogRecord())) { - ASSERT_EQ(nostd::string_view{"128"}, - opentelemetry::nostd::get(real_logger->GetLastLogRecord())); + ASSERT_EQ(nostd::string_view{"128"}, nostd::string_view{opentelemetry::nostd::get( + real_logger->GetLastLogRecord())}); } else { @@ -200,7 +206,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + ASSERT_TRUE(data_span[i] == output[i]); } } @@ -218,7 +224,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + EXPECT_EQ(data_span[i], output[i]); } } @@ -236,7 +242,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + EXPECT_EQ(data_span[i], output[i]); } } @@ -254,7 +260,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + EXPECT_EQ(data_span[i], output[i]); } } @@ -272,7 +278,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + EXPECT_EQ(data_span[i], output[i]); } } @@ -290,7 +296,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + EXPECT_EQ(data_span[i], output[i]); } } @@ -328,7 +334,7 @@ TEST(LogBody, BodyConversation) for (size_t i = 0; i < data_span.size(); ++i) { - ASSERT_EQ(data_span[i], output[i]); + EXPECT_EQ(data_span[i], output[i]); } } } diff --git a/test_common/src/http/client/nosend/CMakeLists.txt b/test_common/src/http/client/nosend/CMakeLists.txt index 7394053a22..0c3b317f99 100644 --- a/test_common/src/http/client/nosend/CMakeLists.txt +++ b/test_common/src/http/client/nosend/CMakeLists.txt @@ -26,6 +26,9 @@ if(${BUILD_TESTING}) else() find_library(GMOCK_LIB gmock PATH_SUFFIXES lib) endif() + if(NOT GMOCK_LIB) + unset(GMOCK_LIB CACHE) + endif() target_link_libraries( opentelemetry_http_client_nosend opentelemetry_ext diff --git a/third_party/prometheus-cpp b/third_party/prometheus-cpp index e5fada4313..ad99e21f47 160000 --- a/third_party/prometheus-cpp +++ b/third_party/prometheus-cpp @@ -1 +1 @@ -Subproject commit e5fada43131d251e9c4786b04263ce98b6767ba5 +Subproject commit ad99e21f4706193670c42b36c9824dc997f4c475