diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 625b44e0f..b490dd7bf 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -17,6 +17,7 @@ ".gitignore", ".vscode/cspell.json", "vcpkg-custom-ports", + "**/assets.json", "ci.yml", "squid.conf*", "eng/common/**/*", diff --git a/doc/DistributedTracing.md b/doc/DistributedTracing.md index 2c373d31a..9bec54b8f 100644 --- a/doc/DistributedTracing.md +++ b/doc/DistributedTracing.md @@ -210,6 +210,8 @@ also propagate the W3C distributed tracing headers from the span into the HTTP r The Azure standards for distributed tracing are define in [Azure Distributed Tracing Conventions](https://github.com/Azure/azure-sdk/blob/main/docs/tracing/distributed-tracing-conventions.md). The actual tracing elements generated by Azure services are defined in [Azure Tracing Conventions YAML](https://github.com/Azure/azure-sdk/blob/main/docs/tracing/distributed-tracing-conventions.yml). +The service names specified for the `az.namespace` attribute can be found [here](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers) + In summary, these are the traces and attributes which should be generated for azure services: diff --git a/sdk/attestation/assets.json b/sdk/attestation/assets.json index e4fac3aad..fc2dbbc44 100644 --- a/sdk/attestation/assets.json +++ b/sdk/attestation/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "cpp", "TagPrefix": "cpp/attestation", - "Tag": "cpp/attestation_b384d96f95" + "Tag": "cpp/attestation_10abfdc3e0" } diff --git a/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp b/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp index a9a47c737..30fb8dbf9 100644 --- a/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp +++ b/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp @@ -42,9 +42,13 @@ AttestationAdministrationClient::AttestationAdministrationClient( std::string const& endpoint, std::shared_ptr credential, AttestationAdministrationClientOptions const& options) - : m_endpoint(endpoint), m_apiVersion(options.ApiVersion), - m_tokenValidationOptions(options.TokenValidationOptions), - m_tracingFactory(options, "security.attestation", PackageVersion::ToString()) + : m_endpoint{endpoint}, m_apiVersion{options.ApiVersion}, + m_tokenValidationOptions{options.TokenValidationOptions}, + m_tracingFactory{ + options, + "Microsoft.Attestation", + "azure-security-attestation-cpp", + PackageVersion::ToString()} { std::vector> perRetrypolicies; if (credential) @@ -58,7 +62,11 @@ AttestationAdministrationClient::AttestationAdministrationClient( std::vector> perCallpolicies; m_pipeline = std::make_shared( - options, std::move(perRetrypolicies), std::move(perCallpolicies)); + options, + "security.attestation", + PackageVersion::ToString(), + std::move(perRetrypolicies), + std::move(perCallpolicies)); } AttestationAdministrationClient AttestationAdministrationClient::Create( diff --git a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp index 72458a1d3..667f4bb91 100644 --- a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp +++ b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp @@ -31,9 +31,13 @@ AttestationClient::AttestationClient( std::string const& endpoint, std::shared_ptr credential, AttestationClientOptions options) - : m_endpoint(endpoint), m_apiVersion(options.ApiVersion), - m_tokenValidationOptions(options.TokenValidationOptions), - m_tracingFactory(options, "security.attestation", PackageVersion::ToString()) + : m_endpoint{endpoint}, m_apiVersion{options.ApiVersion}, + m_tokenValidationOptions{options.TokenValidationOptions}, + m_tracingFactory{ + options, + "Microsoft.Attestation", + "azure-security-attestation-cpp", + PackageVersion::ToString()} { std::vector> perRetrypolicies; if (credential) @@ -47,7 +51,11 @@ AttestationClient::AttestationClient( std::vector> perCallpolicies; m_pipeline = std::make_shared( - options, std::move(perRetrypolicies), std::move(perCallpolicies)); + options, + "security.attestation", + PackageVersion::ToString(), + std::move(perRetrypolicies), + std::move(perCallpolicies)); } Azure::Response AttestationClient::GetOpenIdMetadata( diff --git a/sdk/core/azure-core-tracing-opentelemetry/inc/azure/core/tracing/opentelemetry/opentelemetry.hpp b/sdk/core/azure-core-tracing-opentelemetry/inc/azure/core/tracing/opentelemetry/opentelemetry.hpp index ce8142835..d6f215402 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/inc/azure/core/tracing/opentelemetry/opentelemetry.hpp +++ b/sdk/core/azure-core-tracing-opentelemetry/inc/azure/core/tracing/opentelemetry/opentelemetry.hpp @@ -45,6 +45,10 @@ namespace Azure { namespace Core { namespace Tracing { namespace OpenTelemetry { opentelemetry::nostd::shared_ptr tracerProvider = opentelemetry::trace::Provider::GetTracerProvider()); + // Schema URL for OpenTelemetry. Azure SDKs currently support version 1.17.0 only. + const char* OpenTelemetrySchemaUrl117 = "https://opentelemetry.io/schemas/1.17.0"; + const char* OpenTelemetrySchemaUrlCurrent = OpenTelemetrySchemaUrl117; + public: /** * @brief Create a new instance of an OpenTelemetryProvider. diff --git a/sdk/core/azure-core-tracing-opentelemetry/src/opentelemetry.cpp b/sdk/core/azure-core-tracing-opentelemetry/src/opentelemetry.cpp index 79f5e2242..692b3472f 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/src/opentelemetry.cpp +++ b/sdk/core/azure-core-tracing-opentelemetry/src/opentelemetry.cpp @@ -42,7 +42,7 @@ namespace Azure { namespace Core { namespace Tracing { namespace OpenTelemetry { std::string const& version) const { opentelemetry::nostd::shared_ptr returnTracer( - m_tracerProvider->GetTracer(name, version)); + m_tracerProvider->GetTracer(name, version, OpenTelemetrySchemaUrlCurrent)); return std::make_shared( returnTracer); } diff --git a/sdk/core/azure-core-tracing-opentelemetry/test/ut/service_support_test.cpp b/sdk/core/azure-core-tracing-opentelemetry/test/ut/service_support_test.cpp index c28ecafa4..466231db0 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/test/ut/service_support_test.cpp +++ b/sdk/core/azure-core-tracing-opentelemetry/test/ut/service_support_test.cpp @@ -180,22 +180,34 @@ protected: EXPECT_EQ(expectedAttributes.size(), attributes.size()); + // Make sure that every expected attribute is somewhere in the actual attributes. + for (auto const& expectedAttribute : expectedAttributes.items()) + { + EXPECT_TRUE( + std::find_if( + attributes.begin(), + attributes.end(), + [&](std::pair& item) { + return item.first == expectedAttribute.key(); + }) + != attributes.end()); + } + for (auto const& foundAttribute : attributes) { EXPECT_TRUE(expectedAttributes.contains(foundAttribute.first)); switch (foundAttribute.second.index()) { - case opentelemetry::common::kTypeBool: { + case RecordedSpan::Attribute::AttributeType::Bool: { EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_boolean()); - auto actualVal = opentelemetry::nostd::get(foundAttribute.second); + auto actualVal = foundAttribute.second.BoolValue; EXPECT_EQ(expectedAttributes[foundAttribute.first].get(), actualVal); break; } - case opentelemetry::common::kTypeCString: - case opentelemetry::common::kTypeString: { + case RecordedSpan::Attribute::AttributeType::CString: { EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_string()); - const auto& actualVal = opentelemetry::nostd::get(foundAttribute.second); + const auto& actualVal = foundAttribute.second.CStringValue; std::string expectedVal(expectedAttributes[foundAttribute.first].get()); std::regex expectedRegex(expectedVal); GTEST_LOG_(INFO) << "expected Regex: " << expectedVal << std::endl; @@ -203,32 +215,43 @@ protected: EXPECT_TRUE(std::regex_match(std::string(actualVal), expectedRegex)); break; } - case opentelemetry::common::kTypeDouble: { + + case RecordedSpan::Attribute::AttributeType::String: { + EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_string()); + const auto& actualVal = foundAttribute.second.StringValue; + std::string expectedVal(expectedAttributes[foundAttribute.first].get()); + std::regex expectedRegex(expectedVal); + GTEST_LOG_(INFO) << "expected Regex: " << expectedVal << std::endl; + GTEST_LOG_(INFO) << "actual val: " << actualVal << std::endl; + EXPECT_TRUE(std::regex_match(std::string(actualVal), expectedRegex)); + break; + } + case RecordedSpan::Attribute::AttributeType::Double: { EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_number()); - auto actualVal = opentelemetry::nostd::get(foundAttribute.second); + auto actualVal = foundAttribute.second.DoubleValue; EXPECT_EQ(expectedAttributes[foundAttribute.first].get(), actualVal); break; } - case opentelemetry::common::kTypeInt: - case opentelemetry::common::kTypeInt64: + case RecordedSpan::Attribute::AttributeType::Int32: + case RecordedSpan::Attribute::AttributeType::Int64: EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_number_integer()); break; - case opentelemetry::common::kTypeSpanBool: - case opentelemetry::common::kTypeSpanByte: - case opentelemetry::common::kTypeSpanDouble: - case opentelemetry::common::kTypeSpanInt: - case opentelemetry::common::kTypeSpanInt64: - case opentelemetry::common::kTypeSpanString: - case opentelemetry::common::kTypeSpanUInt: - case opentelemetry::common::kTypeSpanUInt64: + case RecordedSpan::Attribute::AttributeType::BoolArray: + case RecordedSpan::Attribute::AttributeType::ByteArray: + case RecordedSpan::Attribute::AttributeType::DoubleArray: + case RecordedSpan::Attribute::AttributeType::Int32Array: + case RecordedSpan::Attribute::AttributeType::Int64Array: + case RecordedSpan::Attribute::AttributeType::StringArray: + case RecordedSpan::Attribute::AttributeType::UInt32Array: + case RecordedSpan::Attribute::AttributeType::UInt64Array: EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_array()); throw std::runtime_error("Unsupported attribute kind"); break; - case opentelemetry::common::kTypeUInt: - case opentelemetry::common::kTypeUInt64: + case RecordedSpan::Attribute::AttributeType::UInt32: + case RecordedSpan::Attribute::AttributeType::UInt64: EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_number_unsigned()); break; default: @@ -247,6 +270,9 @@ protected: EXPECT_EQ( expectedSpanContents["library"]["version"].get(), span->GetInstrumentationScope().GetVersion()); + EXPECT_EQ( + expectedSpanContents["library"]["schema"].get(), + span->GetInstrumentationScope().GetSchemaURL()); } return true; } @@ -260,13 +286,13 @@ TEST_F(OpenTelemetryServiceTests, SimplestTest) { Azure::Core::_internal::ClientOptions clientOptions; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); } { Azure::Core::_internal::ClientOptions clientOptions; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); EXPECT_FALSE(contextAndSpan.Context.IsCancelled()); @@ -285,8 +311,11 @@ TEST_F(OpenTelemetryServiceTests, CreateWithExplicitProvider) Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider::Create(tracerProvider)); Azure::Core::Context rootContext; - rootContext.SetTracerProvider(provider); - EXPECT_EQ(provider, rootContext.GetTracerProvider()); + Azure::Core::Context::Key providerKey; + auto newContext = rootContext.WithValue(providerKey, provider); + decltype(provider) savedProvider; + EXPECT_TRUE(newContext.TryGetValue(providerKey, savedProvider)); + EXPECT_EQ(provider, savedProvider); } { @@ -301,7 +330,7 @@ TEST_F(OpenTelemetryServiceTests, CreateWithExplicitProvider) clientOptions.Telemetry.ApplicationId = "MyApplication"; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service", "1.0beta-2"); + clientOptions, "My.Service", "my-service", "MyServiceVersion1.0"); Azure::Core::Context clientContext; auto contextAndSpan = serviceTrace.CreateTracingContext("My API", clientContext); @@ -316,11 +345,12 @@ TEST_F(OpenTelemetryServiceTests, CreateWithExplicitProvider) "name": "My API", "kind": "internal", "attributes": { - "az.namespace": "my-service" + "az.namespace": "My.Service" }, "library": { "name": "my-service", - "version": "1.0beta-2" + "version": "MyServiceVersion1.0", + "schema": "https://opentelemetry.io/schemas/1.17.0" } })"); } @@ -333,17 +363,15 @@ TEST_F(OpenTelemetryServiceTests, CreateWithImplicitProvider) auto provider( Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider::Create(tracerProvider)); - Azure::Core::Context::ApplicationContext.SetTracerProvider(provider); - { Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.ApplicationId = "MyApplication"; + clientOptions.Telemetry.TracingProvider = provider; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service", "1.0beta-2"); + clientOptions, "My.Service", "my-service", "1.0.beta-2"); - Azure::Core::Context clientContext; - auto contextAndSpan = serviceTrace.CreateTracingContext("My API", clientContext); + auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); EXPECT_FALSE(contextAndSpan.Context.IsCancelled()); } @@ -356,17 +384,15 @@ TEST_F(OpenTelemetryServiceTests, CreateWithImplicitProvider) "name": "My API", "kind": "internal", "attributes": { - "az.namespace": "my-service" + "az.namespace": "My.Service" }, "library": { "name": "my-service", - "version": "1.0beta-2" + "version": "1.0.beta-2", + "schema": "https://opentelemetry.io/schemas/1.17.0" } })"); } - - // Clear the global tracer provider set earlier in the test. - Azure::Core::Context::ApplicationContext.SetTracerProvider(nullptr); } TEST_F(OpenTelemetryServiceTests, CreateSpanWithOptions) @@ -376,22 +402,19 @@ TEST_F(OpenTelemetryServiceTests, CreateSpanWithOptions) auto provider( Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider::Create(tracerProvider)); - Azure::Core::Context::ApplicationContext.SetTracerProvider(provider); - { Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.ApplicationId = "MyApplication"; + clientOptions.Telemetry.TracingProvider = provider; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service", "1.0beta-2"); + clientOptions, "My.Service", "my-service", "1.0.beta-2"); - Azure::Core::Context clientContext; Azure::Core::Tracing::_internal::CreateSpanOptions createOptions; createOptions.Kind = Azure::Core::Tracing::_internal::SpanKind::Internal; createOptions.Attributes = serviceTrace.CreateAttributeSet(); createOptions.Attributes->AddAttribute("TestAttribute", 3); - auto contextAndSpan - = serviceTrace.CreateTracingContext("My API", createOptions, clientContext); + auto contextAndSpan = serviceTrace.CreateTracingContext("My API", createOptions, {}); EXPECT_FALSE(contextAndSpan.Context.IsCancelled()); } @@ -404,18 +427,16 @@ TEST_F(OpenTelemetryServiceTests, CreateSpanWithOptions) "name": "My API", "kind": "internal", "attributes": { - "az.namespace": "my-service", + "az.namespace": "My.Service", "TestAttribute": 3 }, "library": { "name": "my-service", - "version": "1.0beta-2" + "version": "1.0.beta-2", + "schema": "https://opentelemetry.io/schemas/1.17.0" } })"); } - - // Clear the global tracer provider set earlier in the test. - Azure::Core::Context::ApplicationContext.SetTracerProvider(nullptr); } TEST_F(OpenTelemetryServiceTests, NestSpans) @@ -425,8 +446,6 @@ TEST_F(OpenTelemetryServiceTests, NestSpans) auto provider( Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider::Create(tracerProvider)); - Azure::Core::Context::ApplicationContext.SetTracerProvider(provider); - Azure::Core::Http::Request outerRequest( HttpMethod::Post, Azure::Core::Url("https://www.microsoft.com")); Azure::Core::Http::Request innerRequest( @@ -434,24 +453,23 @@ TEST_F(OpenTelemetryServiceTests, NestSpans) { Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.ApplicationId = "MyApplication"; + clientOptions.Telemetry.TracingProvider = provider; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service", "1.0beta-2"); + clientOptions, "My.Service", "my.service", "1.0beta-2"); - Azure::Core::Context parentContext; Azure::Core::Tracing::_internal::CreateSpanOptions createOptions; createOptions.Kind = Azure::Core::Tracing::_internal::SpanKind::Client; - auto contextAndSpan - = serviceTrace.CreateTracingContext("My API", createOptions, parentContext); + auto contextAndSpan = serviceTrace.CreateTracingContext("My API", createOptions, {}); EXPECT_FALSE(contextAndSpan.Context.IsCancelled()); - parentContext = contextAndSpan.Context; + auto outerContext{contextAndSpan.Context}; contextAndSpan.Span.PropagateToHttpHeaders(outerRequest); { Azure::Core::Tracing::_internal::CreateSpanOptions innerOptions; innerOptions.Kind = Azure::Core::Tracing::_internal::SpanKind::Server; auto innerContextAndSpan - = serviceTrace.CreateTracingContext("Nested API", innerOptions, parentContext); + = serviceTrace.CreateTracingContext("Nested API", innerOptions, outerContext); EXPECT_FALSE(innerContextAndSpan.Context.IsCancelled()); innerContextAndSpan.Span.PropagateToHttpHeaders(innerRequest); } @@ -462,31 +480,32 @@ TEST_F(OpenTelemetryServiceTests, NestSpans) // Because Nested API goes out of scope before My API, it will be logged first in the // tracing spans. - { - EXPECT_EQ("Nested API", spans[0]->GetName()); - EXPECT_TRUE(spans[0]->GetParentSpanId().IsValid()); - // The nested span should have the outer span as a parent. - EXPECT_EQ(spans[1]->GetSpanId(), spans[0]->GetParentSpanId()); - - const auto& attributes = spans[0]->GetAttributes(); - EXPECT_EQ(1ul, attributes.size()); - EXPECT_EQ( - "my-service", opentelemetry::nostd::get(attributes.at("az.namespace"))); - } - { - EXPECT_EQ("My API", spans[1]->GetName()); - EXPECT_FALSE(spans[1]->GetParentSpanId().IsValid()); - - const auto& attributes = spans[1]->GetAttributes(); - EXPECT_EQ(1ul, attributes.size()); - EXPECT_EQ( - "my-service", opentelemetry::nostd::get(attributes.at("az.namespace"))); - } - - EXPECT_EQ("my-service", spans[0]->GetInstrumentationScope().GetName()); - EXPECT_EQ("my-service", spans[1]->GetInstrumentationScope().GetName()); - EXPECT_EQ("1.0beta-2", spans[0]->GetInstrumentationScope().GetVersion()); - EXPECT_EQ("1.0beta-2", spans[1]->GetInstrumentationScope().GetVersion()); + VerifySpan(spans[0], R"( +{ + "name": "Nested API", + "kind": "server", + "attributes": { + "az.namespace": "My.Service" + }, + "library": { + "name": "my.service", + "version": "1.0beta-2", + "schema": "https://opentelemetry.io/schemas/1.17.0" + } +})"); + VerifySpan(spans[1], R"( +{ + "name": "My API", + "kind": "client", + "attributes": { + "az.namespace": "My.Service" + }, + "library": { + "name": "my.service", + "version": "1.0beta-2", + "schema": "https://opentelemetry.io/schemas/1.17.0" + } +})"); // The trace ID for the inner and outer requests must be the same, the parent-id/span-id must be // different. @@ -568,10 +587,16 @@ private: public: explicit ServiceClient(ServiceClientOptions const& clientOptions = ServiceClientOptions{}) - : m_tracingFactory(clientOptions, "Azure.Core.OpenTelemetry.Test.Service", "1.0.0.beta-2") + : m_tracingFactory( + clientOptions, + "Azure.Core.OpenTelemetry.Test.Service", + "azure-core-opentelemetry-test-service-cpp", + "1.0.0.beta-2") { std::vector> policies; policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique( + "core-opentelemetry-test-service-cpp", "1.0.0.beta-2", clientOptions.Telemetry)); policies.emplace_back(std::make_unique(RetryOptions{})); // Add the request ID policy - this adds the x-ms-request-id attribute to the pipeline. @@ -585,7 +610,11 @@ public: { throw Azure::Core::RequestFailedException("it all goes wrong here."); } - return std::make_unique(1, 1, HttpStatusCode::Ok, "Something"); + auto response = std::make_unique(1, 1, HttpStatusCode::Ok, "Something"); + + response->SetHeader("x-ms-request-id", "12345"); + + return response; })); m_pipeline = std::make_unique(policies); @@ -667,15 +696,19 @@ TEST_F(OpenTelemetryServiceTests, ServiceApiImplementation) "statusCode": "unset", "attributes": { "az.namespace": "Azure.Core.OpenTelemetry.Test.Service", + "az.client_request_id": ".*", + "az.service_request_id": "12345", + "net.peer.name": "https://www.microsoft.com", + "net.peer.port": 443, "http.method": "GET", "http.url": "https://www.microsoft.com", - "requestId": ".*", - "http.user_agent": "MyApplication azsdk-cpp-Azure.Core.OpenTelemetry.Test.Service/1.0.0.beta-2.*", + "http.user_agent": "MyApplication azsdk-cpp-core-opentelemetry-test-service-cpp/1.0.0.beta-2.*", "http.status_code": "200" }, "library": { - "name": "Azure.Core.OpenTelemetry.Test.Service", - "version": "1.0.0.beta-2" + "name": "azure-core-opentelemetry-test-service-cpp", + "version": "1.0.0.beta-2", + "schema": "https://opentelemetry.io/schemas/1.17.0" } })"); @@ -688,8 +721,9 @@ TEST_F(OpenTelemetryServiceTests, ServiceApiImplementation) "az.namespace": "Azure.Core.OpenTelemetry.Test.Service" }, "library": { - "name": "Azure.Core.OpenTelemetry.Test.Service", - "version": "1.0.0.beta-2" + "name": "azure-core-opentelemetry-test-service-cpp", + "version": "1.0.0.beta-2", + "schema": "https://opentelemetry.io/schemas/1.17.0" } })"); } diff --git a/sdk/core/azure-core-tracing-opentelemetry/test/ut/test_exporter.hpp b/sdk/core/azure-core-tracing-opentelemetry/test/ut/test_exporter.hpp index b3f19084a..4339fe2c7 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/test/ut/test_exporter.hpp +++ b/sdk/core/azure-core-tracing-opentelemetry/test/ut/test_exporter.hpp @@ -7,15 +7,211 @@ #include class RecordedSpan : public opentelemetry::sdk::trace::Recordable { +public: + struct Attribute + { + enum class AttributeType + { + Bool, + Int32, + Int64, + UInt32, + UInt64, + Double, + CString, + String, + BoolArray, + Int32Array, + UInt32Array, + Int64Array, + UInt64Array, + DoubleArray, + StringArray, + ByteArray + }; + AttributeType Type; + bool BoolValue{}; + int32_t Int32Value{}; + int64_t Int64Value{}; + uint32_t UInt32Value{}; + uint64_t UInt64Value{}; + double DoubleValue{}; + const char* CStringValue{}; + std::string StringValue; + std::vector BoolArrayValue; + std::vector Int32ArrayValue; + std::vector Int64ArrayValue; + std::vector UInt32ArrayValue; + std::vector UInt64ArrayValue; + std::vector DoubleArrayValue; + std::vector StringArrayValue; + std::vector ByteArrayValue; + Attribute(bool val) : Type{AttributeType::Bool}, BoolValue{val} {} + Attribute(const char* val) : Type{AttributeType::CString}, CStringValue{val} {} + Attribute(uint32_t val) : Type{AttributeType::UInt32}, UInt32Value{val} {} + Attribute(int32_t val) : Type{AttributeType::Int32}, Int32Value{val} {} + Attribute(int64_t val) : Type{AttributeType::Int64}, Int64Value{val} {} + Attribute(uint64_t val) : Type{AttributeType::UInt64}, UInt64Value{val} {} + Attribute(double val) : Type{AttributeType::Double}, DoubleValue{val} {} + Attribute(opentelemetry::nostd::string_view const val) + : Type{AttributeType::String}, StringValue{val} + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::BoolArray}, BoolArrayValue(val.size(), val.data()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::Int32Array}, Int32ArrayValue(val.begin(), val.end()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::UInt32Array}, UInt32ArrayValue(val.begin(), val.end()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::Int64Array}, Int64ArrayValue(val.begin(), val.end()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::UInt64Array}, UInt64ArrayValue(val.begin(), val.end()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::DoubleArray}, DoubleArrayValue(val.begin(), val.end()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::StringArray}, StringArrayValue(val.begin(), val.end()) + { + } + Attribute(opentelemetry::nostd::span val) + : Type{AttributeType::ByteArray}, ByteArrayValue(val.begin(), val.end()) + { + } + AttributeType index() const { return Type; } + + operator bool() const + { + assert(Type == AttributeType::Bool); + return BoolValue; + } + }; + class AttributeMap { + std::map m_attributes; + + public: + void SetAttribute(std::string const& key, const opentelemetry::common::AttributeValue& value) + { + switch (value.index()) + { + case opentelemetry::common::AttributeType::kTypeBool: { + ; + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeCString: { + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeInt: { + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeInt64: { + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeUInt: { + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeDouble: { + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeString: { + m_attributes.emplace( + key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanBool: { + m_attributes.emplace( + key, + Attribute{opentelemetry::nostd::get>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanInt: { + m_attributes.emplace( + key, + Attribute{ + opentelemetry::nostd::get>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanInt64: { + m_attributes.emplace( + key, + Attribute{ + opentelemetry::nostd::get>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanUInt: { + m_attributes.emplace( + key, + Attribute{ + opentelemetry::nostd::get>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanDouble: { + m_attributes.emplace( + key, + Attribute{ + opentelemetry::nostd::get>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanString: { + m_attributes.emplace( + key, + Attribute{opentelemetry::nostd::get< + opentelemetry::nostd::span>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeUInt64: { + m_attributes.emplace(key, Attribute{opentelemetry::nostd::get(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanUInt64: { + m_attributes.emplace( + key, + Attribute{ + opentelemetry::nostd::get>(value)}); + break; + } + case opentelemetry::common::AttributeType::kTypeSpanByte: { + m_attributes.emplace( + key, + Attribute{ + opentelemetry::nostd::get>(value)}); + break; + } + + break; + } + } + size_t size() const { return m_attributes.size(); } + decltype(m_attributes)::iterator begin() { return m_attributes.begin(); } + decltype(m_attributes)::iterator end() { return m_attributes.end(); } + Attribute const& at(std::string const& key) const { return m_attributes.at(key); } + }; struct Event { std::string Name; std::chrono::system_clock::time_point Timestamp; - opentelemetry::sdk::common::AttributeMap Attributes; + AttributeMap Attributes; }; opentelemetry::trace::SpanId m_parentSpan; opentelemetry::trace::SpanId m_spanId; - opentelemetry::sdk::common::AttributeMap m_attributes; + AttributeMap m_attributes; std::vector m_events; opentelemetry::trace::StatusCode m_statusCode{}; std::string m_statusDescription; @@ -51,7 +247,7 @@ public: opentelemetry::nostd::string_view key, const opentelemetry::common::AttributeValue& value) noexcept override { - m_attributes.SetAttribute(key, value); + m_attributes.SetAttribute(std::string{key}, value); }; /** @@ -72,7 +268,7 @@ public: attributes.ForEachKeyValue( [&event]( opentelemetry::nostd::string_view name, opentelemetry::common::AttributeValue value) { - event.Attributes.SetAttribute(name, value); + event.Attributes.SetAttribute(std::string{name}, value); return true; }); m_events.push_back(event); @@ -159,7 +355,7 @@ public: opentelemetry::trace::SpanId GetParentSpanId() { return m_parentSpan; } opentelemetry::trace::SpanKind GetSpanKind() { return m_spanKind; } opentelemetry::trace::SpanId GetSpanId() { return m_spanId; } - opentelemetry::sdk::common::AttributeMap const& GetAttributes() { return m_attributes; } + AttributeMap const& GetAttributes() { return m_attributes; } opentelemetry::sdk::instrumentationscope::InstrumentationScope& GetInstrumentationScope() { return *m_scope; diff --git a/sdk/core/azure-core/CHANGELOG.md b/sdk/core/azure-core/CHANGELOG.md index 1550dfc64..3a8ff9ce1 100644 --- a/sdk/core/azure-core/CHANGELOG.md +++ b/sdk/core/azure-core/CHANGELOG.md @@ -8,6 +8,8 @@ ### Bugs Fixed +- Fixed bug in WinHTTP client which caused the `IgnoreUnknownCertificateAuthority` and `EnableCertificateRevocationListCheck` fields to be +ignored if they were passed in from `TransportOptions`. - [[#4206]](https://github.com/Azure/azure-sdk-for-cpp/issues/4206) Fixed connectivity issues which can occur if a TCP connection is dropped prematurely. (A community contribution, courtesy of _[ahojnnes](https://github.com/ahojnnes)_) ### Acknowledgments diff --git a/sdk/core/azure-core/inc/azure/core/context.hpp b/sdk/core/azure-core/inc/azure/core/context.hpp index b46b01e63..0e19c9bf1 100644 --- a/sdk/core/azure-core/inc/azure/core/context.hpp +++ b/sdk/core/azure-core/inc/azure/core/context.hpp @@ -244,26 +244,10 @@ namespace Azure { namespace Core { } } - /** - * @brief Returns the tracer provider for the current context. - */ - std::shared_ptr GetTracerProvider() - { - return m_contextSharedState->TraceProvider; - } - - /** - * @brief Sets the tracer provider for the current context. - */ - void SetTracerProvider(std::shared_ptr tracerProvider) - { - m_contextSharedState->TraceProvider = tracerProvider; - } - /** * @brief The application context (root). * */ - static AZ_CORE_DLLEXPORT Context ApplicationContext; + static const AZ_CORE_DLLEXPORT Context ApplicationContext; }; }} // namespace Azure::Core diff --git a/sdk/core/azure-core/inc/azure/core/http/policies/policy.hpp b/sdk/core/azure-core/inc/azure/core/http/policies/policy.hpp index eb47716bb..b52b807fd 100644 --- a/sdk/core/azure-core/inc/azure/core/http/policies/policy.hpp +++ b/sdk/core/azure-core/inc/azure/core/http/policies/policy.hpp @@ -67,8 +67,7 @@ namespace Azure { namespace Core { namespace Http { namespace Policies { * @brief Specifies the default distributed tracing provider to use for this client. By default, * this will be the tracing provider specified in the application context. */ - std::shared_ptr TracingProvider{ - Context::ApplicationContext.GetTracerProvider()}; + std::shared_ptr TracingProvider; }; /** @@ -498,17 +497,17 @@ namespace Azure { namespace Core { namespace Http { namespace Policies { /** * @brief Construct HTTP telemetry policy. * - * @param componentName Azure SDK component name (e.g. "storage.blobs"). - * @param componentVersion Azure SDK component version (e.g. "11.0.0"). + * @param packageName Azure SDK component name (e.g. "storage.blobs"). + * @param packageVersion Azure SDK component version (e.g. "11.0.0"). * @param options The optional parameters for the policy (e.g. "AzCopy") */ explicit TelemetryPolicy( - std::string const& componentName, - std::string const& componentVersion, + std::string const& packageName, + std::string const& packageVersion, TelemetryOptions options = TelemetryOptions()) : m_telemetryId(Azure::Core::Http::_detail::UserAgentGenerator::GenerateUserAgent( - componentName, - componentVersion, + packageName, + packageVersion, options.ApplicationId)) { } diff --git a/sdk/core/azure-core/inc/azure/core/internal/http/pipeline.hpp b/sdk/core/azure-core/inc/azure/core/internal/http/pipeline.hpp index a44d24cbf..abdadc6bf 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/http/pipeline.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/http/pipeline.hpp @@ -31,7 +31,33 @@ namespace Azure { namespace Core { namespace Http { namespace _internal { * @remark See #policy.hpp */ class HttpPipeline final { - private: + protected: + std::vector> m_policies; + + public: + /** + * @brief Construct HTTP pipeline with the sequence of HTTP policies provided. + * + * @param policies A sequence of #Azure::Core::Http::Policies::HttpPolicy + * representing a stack, first element corresponding to the top of the stack. + * + * @throw `std::invalid_argument` when policies is empty. + */ + explicit HttpPipeline( + const std::vector>& policies) + { + if (policies.size() == 0) + { + throw std::invalid_argument("policies cannot be empty"); + } + + m_policies.reserve(policies.size()); + for (auto& policy : policies) + { + m_policies.emplace_back(policy->Clone()); + } + } + /** * @brief Construct a new HTTP Pipeline object from clientOptions. * @@ -39,18 +65,22 @@ namespace Azure { namespace Core { namespace Http { namespace _internal { * service-specific per retry policies. * * @param clientOptions The SDK client options. - * @param telemetryServiceName The name of the service for sending telemetry. - * @param telemetryServiceVersion The version of the service for sending telemetry. + * @param telemetryPackageName The name of the service for sending telemetry. + * @param telemetryPackageVersion The version of the service for sending telemetry. * @param perRetryPolicies The service-specific per retry policies. * @param perCallPolicies The service-specific per call policies. + * + * @remark The telemetryPackageName and telemetryPackageVersion SHOULD represent the name and + * version of the package as is found in the vcpkg package manager. Note that changing the + * telemetryPackageName parameter is considered a breaking change (changing + * telemetryPackageVersion is not). */ explicit HttpPipeline( Azure::Core::_internal::ClientOptions const& clientOptions, + std::string const& telemetryPackageName, + std::string const& telemetryPackageVersion, std::vector>&& perRetryPolicies, - std::vector>&& perCallPolicies, - bool includeTelemetryPolicy, - std::string const& telemetryServiceName = {}, - std::string const& telemetryServiceVersion = {}) + std::vector>&& perCallPolicies) { Azure::Core::Http::_internal::HttpSanitizer httpSanitizer( clientOptions.Log.AllowedHttpQueryParameters, clientOptions.Log.AllowedHttpHeaders); @@ -65,7 +95,7 @@ namespace Azure { namespace Core { namespace Http { namespace _internal { // - RequestActivityPolicy // - TransportPolicy auto pipelineSize = perCallClientPolicies.size() + perRetryClientPolicies.size() - + perRetryPolicies.size() + perCallPolicies.size() + 5 + (includeTelemetryPolicy ? 1 : 0); + + perRetryPolicies.size() + perCallPolicies.size() + 6; m_policies.reserve(pipelineSize); @@ -79,13 +109,10 @@ namespace Azure { namespace Core { namespace Http { namespace _internal { m_policies.emplace_back( std::make_unique()); - // Telemetry - if (includeTelemetryPolicy) - { - m_policies.emplace_back( - std::make_unique( - telemetryServiceName, telemetryServiceVersion, clientOptions.Telemetry)); - } + // Telemetry (user-agent header) + m_policies.emplace_back( + std::make_unique( + telemetryPackageName, telemetryPackageVersion, clientOptions.Telemetry)); // client-options per call policies. for (auto& policy : perCallClientPolicies) @@ -123,88 +150,6 @@ namespace Azure { namespace Core { namespace Http { namespace _internal { clientOptions.Transport)); } - protected: - std::vector> m_policies; - - public: - /** - * @brief Construct HTTP pipeline with the sequence of HTTP policies provided. - * - * @param policies A sequence of #Azure::Core::Http::Policies::HttpPolicy - * representing a stack, first element corresponding to the top of the stack. - * - * @throw `std::invalid_argument` when policies is empty. - */ - explicit HttpPipeline( - const std::vector>& policies) - { - if (policies.size() == 0) - { - throw std::invalid_argument("policies cannot be empty"); - } - - m_policies.reserve(policies.size()); - for (auto& policy : policies) - { - m_policies.emplace_back(policy->Clone()); - } - } - - /** - * @brief Construct a new HTTP Pipeline object from clientOptions. - * - * @remark The client options includes per retry and per call policies which are merged with the - * service-specific per retry policies. - * - * @param clientOptions The SDK client options. - * @param telemetryServiceName The name of the service for sending telemetry. - * @param telemetryServiceVersion The version of the service for sending telemetry. - * @param perRetryPolicies The service-specific per retry policies. - * @param perCallPolicies The service-specific per call policies. - */ - explicit HttpPipeline( - Azure::Core::_internal::ClientOptions const& clientOptions, - std::string const& telemetryServiceName, - std::string const& telemetryServiceVersion, - std::vector>&& perRetryPolicies, - std::vector>&& perCallPolicies) - : HttpPipeline( - clientOptions, - std::move(perRetryPolicies), - std::move(perCallPolicies), - true, - telemetryServiceName, - telemetryServiceVersion) - { - } - - /** - * @brief Construct a new HTTP Pipeline object from clientOptions. - * - * @remark The client options includes per retry and per call policies which are merged with the - * service-specific per retry policies. - * - * @remark This specialization of the HttpPipeline constructor constructs an HTTP pipeline - * *without* a telemetry policy. It is intended for use by service clients which have converted - * to use distributed tracing - the distributed tracing policy adds the User-Agent header to the - * request. - * - * @param clientOptions The SDK client options. - * @param perRetryPolicies The service-specific per retry policies. - * @param perCallPolicies The service-specific per call policies. - */ - explicit HttpPipeline( - Azure::Core::_internal::ClientOptions const& clientOptions, - std::vector>&& perRetryPolicies, - std::vector>&& perCallPolicies) - : HttpPipeline( - clientOptions, - std::move(perRetryPolicies), - std::move(perCallPolicies), - false) - { - } - /** * @brief Construct HTTP pipeline with the sequence of HTTP policies provided. * diff --git a/sdk/core/azure-core/inc/azure/core/internal/tracing/service_tracing.hpp b/sdk/core/azure-core/inc/azure/core/internal/tracing/service_tracing.hpp index 2bec23e92..038fb8e86 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/tracing/service_tracing.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/tracing/service_tracing.hpp @@ -172,8 +172,8 @@ namespace Azure { namespace Core { namespace Tracing { namespace _internal { class TracingContextFactory final { private: std::string m_serviceName; - std::string m_serviceVersion; - std::string m_userAgent; + std::string m_packageName; + std::string m_packageVersion; std::shared_ptr m_serviceTracer; /** @brief The key used to retrieve the span and tracer associated with a context object. @@ -188,22 +188,32 @@ namespace Azure { namespace Core { namespace Tracing { namespace _internal { static Azure::Core::Context::Key TracingFactoryContextKey; public: + /** + * @brief Construct a new Tracing Context Factory object + * + * @param options Client Options for tracing. + * @param serviceName Name of the resource provider for the service [See + * also](https://docs.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers). + * @param packageName Name of the package containing this service client. + * @param packageVersion Optional package version number for the package containing this + * service. (https://opentelemetry.io/docs/reference/specification/trace/api/#get-a-tracer). + */ TracingContextFactory( Azure::Core::_internal::ClientOptions const& options, - std::string serviceName, - std::string serviceVersion) - : m_serviceName(serviceName), m_serviceVersion(serviceVersion), - m_userAgent(Azure::Core::Http::_detail::UserAgentGenerator::GenerateUserAgent( - serviceName, - serviceVersion, - options.Telemetry.ApplicationId)), - m_serviceTracer( - options.Telemetry.TracingProvider - ? Azure::Core::Tracing::_internal::TracerProviderImplGetter::TracerImplFromTracer( - options.Telemetry.TracingProvider) - ->CreateTracer(serviceName, serviceVersion) - : nullptr) + std::string const& serviceName, + std::string const& packageName, + std::string packageVersion) + : m_serviceName{serviceName}, m_packageName{packageName}, m_packageVersion{packageVersion} { + // If the caller has configured a tracing provider, use it. Otherwise, use the default + // provider. + if (options.Telemetry.TracingProvider) + { + m_serviceTracer + = Azure::Core::Tracing::_internal::TracerProviderImplGetter::TracerImplFromTracer( + options.Telemetry.TracingProvider) + ->CreateTracer(packageName, packageVersion); + } } TracingContextFactory() = default; @@ -258,10 +268,6 @@ namespace Azure { namespace Core { namespace Tracing { namespace _internal { std::unique_ptr CreateAttributeSet() const; - /** @brief Retrieves the User-Agent header value for this tracing context factory. - */ - std::string const& GetUserAgent() const { return m_userAgent; } - /** @brief Returns true if this TracingContextFactory is connected to a service tracer. */ bool HasTracer() const { return static_cast(m_serviceTracer); } @@ -287,9 +293,25 @@ namespace Azure { namespace Core { namespace Tracing { namespace _internal { * [Namespace](https://docs.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers) * of Azure service request is made against. * + * @remarks Azure Specific attribute. + * */ AZ_CORE_DLLEXPORT const static TracingAttributes AzNamespace; + /** @brief Value of the[x - ms - client - request - id] header(or other request - id header, + * depending on the service) sent by the client. + * + * @remarks Azure Specific attribute. + */ + AZ_CORE_DLLEXPORT const static TracingAttributes RequestId; + + /** @brief Value of the [x-ms-request-id] header (or other request-id header, depending on the + * service) sent by the server in response. + * + * @remarks Azure Specific attribute. + */ + AZ_CORE_DLLEXPORT const static TracingAttributes ServiceRequestId; + /** * @brief HTTP request method. * @@ -315,15 +337,17 @@ namespace Azure { namespace Core { namespace Tracing { namespace _internal { */ AZ_CORE_DLLEXPORT const static TracingAttributes HttpUserAgent; - /** @brief Value of the[x - ms - client - request - id] header(or other request - id header, - * depending on the service) sent by the client. + /** @brief Fully qualified Azure service endpoint(host name component) + * + * For example: 'http://my-account.servicebus.windows.net/' */ - AZ_CORE_DLLEXPORT const static TracingAttributes RequestId; + AZ_CORE_DLLEXPORT const static TracingAttributes NetPeerName; - /** @brief Value of the [x-ms-request-id] header (or other request-id header, depending on the - * service) sent by the server in response. + /** @brief Port of the Azure Service Endpoint + * + * For example: 'http://my-account.servicebus.windows.net/' */ - AZ_CORE_DLLEXPORT const static TracingAttributes ServiceRequestId; + AZ_CORE_DLLEXPORT const static TracingAttributes NetPeerPort; }; }}}} // namespace Azure::Core::Tracing::_internal diff --git a/sdk/core/azure-core/inc/azure/core/operation.hpp b/sdk/core/azure-core/inc/azure/core/operation.hpp index 039ab3080..74cd439ec 100644 --- a/sdk/core/azure-core/inc/azure/core/operation.hpp +++ b/sdk/core/azure-core/inc/azure/core/operation.hpp @@ -199,7 +199,8 @@ namespace Azure { namespace Core { { // In the cases where the customer doesn't want to use a context we new one up and pass it // through - return PollUntilDone(period, Context::ApplicationContext); + Context context; + return PollUntilDone(period, context); } /** diff --git a/sdk/core/azure-core/inc/azure/core/tracing/tracing.hpp b/sdk/core/azure-core/inc/azure/core/tracing/tracing.hpp index 604a1e787..470f10e48 100644 --- a/sdk/core/azure-core/inc/azure/core/tracing/tracing.hpp +++ b/sdk/core/azure-core/inc/azure/core/tracing/tracing.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -25,12 +26,12 @@ namespace Azure { namespace Core { namespace Tracing { * * @param name Name of the tracer object, typically the name of the Service client * (Azure.Storage.Blobs, for example) - * @param version Version of the service client. + * @param version Optional version of the service client. * @return std::shared_ptr */ virtual std::shared_ptr CreateTracer( std::string const& name, - std::string const& version) const = 0; + std::string const& version = {}) const = 0; virtual ~TracerProviderImpl() = default; }; diff --git a/sdk/core/azure-core/src/context.cpp b/sdk/core/azure-core/src/context.cpp index f65aa13b3..370a7ec45 100644 --- a/sdk/core/azure-core/src/context.cpp +++ b/sdk/core/azure-core/src/context.cpp @@ -5,7 +5,7 @@ using namespace Azure::Core; -Context Context::ApplicationContext; +const Context Context::ApplicationContext; Azure::DateTime Azure::Core::Context::GetDeadline() const { diff --git a/sdk/core/azure-core/src/http/request_activity_policy.cpp b/sdk/core/azure-core/src/http/request_activity_policy.cpp index d2c97bab7..ddb2d186e 100644 --- a/sdk/core/azure-core/src/http/request_activity_policy.cpp +++ b/sdk/core/azure-core/src/http/request_activity_policy.cpp @@ -23,28 +23,13 @@ std::unique_ptr RequestActivityPolicy::Send( NextHttpPolicy nextPolicy, Context const& context) const { - Azure::Nullable userAgent; // Find a tracing factory from our context. Note that the factory value is owned by the // context chain so we can manage a raw pointer to the factory. auto tracingFactory = TracingContextFactory::CreateFromContext(context); - if (tracingFactory) - { - // Determine the value of the "User-Agent" header. - // - // If nobody has previously set a user agent header, then set the user agent header - // based on the value calculated by the tracing factory. - userAgent = request.GetHeader("User-Agent"); - if (!userAgent.HasValue()) - { - userAgent = tracingFactory->GetUserAgent(); - request.SetHeader("User-Agent", userAgent.Value()); - } - } // If our tracing factory has a tracer attached to it, register the request with the tracer. if (tracingFactory && tracingFactory->HasTracer()) { - // Create a tracing span over the HTTP request. std::string spanName("HTTP "); spanName.append(request.GetMethod().ToString()); @@ -63,7 +48,13 @@ std::unique_ptr RequestActivityPolicy::Send( TracingAttributes::HttpMethod.ToString(), request.GetMethod().ToString()); const std::string sanitizedUrl = m_httpSanitizer.SanitizeUrl(request.GetUrl()).GetAbsoluteUrl(); - createOptions.Attributes->AddAttribute("http.url", sanitizedUrl); + createOptions.Attributes->AddAttribute(TracingAttributes::HttpUrl.ToString(), sanitizedUrl); + + createOptions.Attributes->AddAttribute( + TracingAttributes::NetPeerPort.ToString(), request.GetUrl().GetPort()); + const std::string host = request.GetUrl().GetScheme() + "://" + request.GetUrl().GetHost(); + createOptions.Attributes->AddAttribute(TracingAttributes::NetPeerName.ToString(), host); + const Azure::Nullable requestId = request.GetHeader("x-ms-client-request-id"); if (requestId.HasValue()) { @@ -71,9 +62,12 @@ std::unique_ptr RequestActivityPolicy::Send( TracingAttributes::RequestId.ToString(), requestId.Value()); } - // We retrieved the value of the user-agent header above. - createOptions.Attributes->AddAttribute( - TracingAttributes::HttpUserAgent.ToString(), userAgent.Value()); + auto userAgent{request.GetHeader("User-Agent")}; + if (userAgent.HasValue()) + { + createOptions.Attributes->AddAttribute( + TracingAttributes::HttpUserAgent.ToString(), userAgent.Value()); + } auto contextAndSpan = tracingFactory->CreateTracingContext(spanName, createOptions, context); auto scope = std::move(contextAndSpan.Span); diff --git a/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp b/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp index a48149071..236b92147 100644 --- a/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp +++ b/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp @@ -756,13 +756,13 @@ WinHttpTransportOptions WinHttpTransportOptionsFromTransportOptions( } if (transportOptions.EnableCertificateRevocationListCheck) { - httpOptions.EnableCertificateRevocationListCheck; + httpOptions.EnableCertificateRevocationListCheck = true; } // If you specify an expected TLS root certificate, you also need to enable ignoring unknown // CAs. if (!transportOptions.ExpectedTlsRootCertificate.empty()) { - httpOptions.IgnoreUnknownCertificateAuthority; + httpOptions.IgnoreUnknownCertificateAuthority = true; } return httpOptions; diff --git a/sdk/core/azure-core/src/tracing/tracing.cpp b/sdk/core/azure-core/src/tracing/tracing.cpp index 4a4012ea2..f3453f78b 100644 --- a/sdk/core/azure-core/src/tracing/tracing.cpp +++ b/sdk/core/azure-core/src/tracing/tracing.cpp @@ -9,14 +9,25 @@ namespace Azure { namespace Core { namespace Tracing { namespace _internal { - const TracingAttributes TracingAttributes::AzNamespace("az.namespace"); - const TracingAttributes TracingAttributes::ServiceRequestId("serviceRequestId"); + // OTel specific Network attributes: + + // Fully qualified Azure service endpoint (host name component). + // For example: 'http://my-account.servicebus.windows.net/' + const TracingAttributes TracingAttributes::NetPeerName("net.peer.name"); + // Port of the Azure Service Endpoint + const TracingAttributes TracingAttributes::NetPeerPort("net.peer.port"); + + // OTel specific HTTP attributes: const TracingAttributes TracingAttributes::HttpUserAgent("http.user_agent"); const TracingAttributes TracingAttributes::HttpMethod("http.method"); const TracingAttributes TracingAttributes::HttpUrl("http.url"); - const TracingAttributes TracingAttributes::RequestId("requestId"); const TracingAttributes TracingAttributes::HttpStatusCode("http.status_code"); + // AZ specific attributes: + const TracingAttributes TracingAttributes::AzNamespace("az.namespace"); + const TracingAttributes TracingAttributes::RequestId("az.client_request_id"); + const TracingAttributes TracingAttributes::ServiceRequestId("az.service_request_id"); + using Azure::Core::Context; std::shared_ptr TracerProviderImplGetter::TracerImplFromTracer( diff --git a/sdk/core/azure-core/test/ut/context_test.cpp b/sdk/core/azure-core/test/ut/context_test.cpp index fc99cca6a..0c9144948 100644 --- a/sdk/core/azure-core/test/ut/context_test.cpp +++ b/sdk/core/azure-core/test/ut/context_test.cpp @@ -514,26 +514,3 @@ TEST(Context, KeyTypePairPrecondition) EXPECT_TRUE(c3.TryGetValue(key, strValue)); EXPECT_TRUE(strValue == s); } - -TEST(Context, SetTracingProvider) -{ - class TestTracingProvider final : public Azure::Core::Tracing::TracerProvider { - public: - TestTracingProvider() : TracerProvider() {} - ~TestTracingProvider() {} - std::shared_ptr CreateTracer( - std::string const&, - std::string const&) const override - { - throw std::runtime_error("Not implemented"); - }; - }; - - Context context; - context.SetTracerProvider(nullptr); - - // Verify we can round trip a tracing provider through the context. - auto testProvider = std::make_shared(); - context.SetTracerProvider(testProvider); - EXPECT_EQ(testProvider, context.GetTracerProvider()); -} diff --git a/sdk/core/azure-core/test/ut/request_activity_policy_test.cpp b/sdk/core/azure-core/test/ut/request_activity_policy_test.cpp index 45b53d379..2af4ddb13 100644 --- a/sdk/core/azure-core/test/ut/request_activity_policy_test.cpp +++ b/sdk/core/azure-core/test/ut/request_activity_policy_test.cpp @@ -161,7 +161,7 @@ TEST(RequestActivityPolicy, Basic) Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.TracingProvider = testTracer; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); Azure::Core::Context callContext = std::move(contextAndSpan.Context); @@ -194,7 +194,7 @@ TEST(RequestActivityPolicy, Basic) Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.TracingProvider = testTracer; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0.0.beta-2"); + clientOptions, "Azure.Service", "service", "1.0.0.beta-2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); Azure::Core::Context callContext = std::move(contextAndSpan.Context); Request request(HttpMethod::Get, Url("https://www.microsoft.com")); @@ -204,6 +204,7 @@ TEST(RequestActivityPolicy, Basic) std::vector> policies; // Add the request ID policy - this adds the x-ms-request-id attribute to the pipeline. policies.emplace_back(std::make_unique()); + policies.emplace_back(std::make_unique("my-service", "1.0.0.beta-2")); policies.emplace_back(std::make_unique(RetryOptions{})); policies.emplace_back( std::make_unique(Azure::Core::Http::_internal::HttpSanitizer{})); @@ -226,8 +227,8 @@ TEST(RequestActivityPolicy, Basic) EXPECT_EQ("GET", tracer->GetSpans()[1]->GetAttributes().at("http.method")); EXPECT_EQ( request.GetHeaders()["x-ms-client-request-id"], - tracer->GetSpans()[1]->GetAttributes().at("requestId")); - std::string expectedUserAgentPrefix{"azsdk-cpp-my-service-cpp/1.0.0.beta-2 ("}; + tracer->GetSpans()[1]->GetAttributes().at("az.client_request_id")); + std::string expectedUserAgentPrefix{"azsdk-cpp-my-service/1.0.0.beta-2 ("}; EXPECT_EQ(expectedUserAgentPrefix, userAgent.Value().substr(0, expectedUserAgentPrefix.size())); } } @@ -240,7 +241,7 @@ TEST(RequestActivityPolicy, TryRetries) Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.TracingProvider = testTracer; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); Azure::Core::Context callContext = std::move(contextAndSpan.Context); @@ -299,7 +300,7 @@ TEST(RequestActivityPolicy, TryFailures) Azure::Core::_internal::ClientOptions clientOptions; clientOptions.Telemetry.TracingProvider = testTracer; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); Azure::Core::Context callContext = std::move(contextAndSpan.Context); diff --git a/sdk/core/azure-core/test/ut/service_tracing_test.cpp b/sdk/core/azure-core/test/ut/service_tracing_test.cpp index 2b25d6031..215f798c2 100644 --- a/sdk/core/azure-core/test/ut/service_tracing_test.cpp +++ b/sdk/core/azure-core/test/ut/service_tracing_test.cpp @@ -68,48 +68,6 @@ private: } // namespace -TEST(TracingContextFactory, UserAgentTests) -{ - struct - { - const std::string serviceName; - const std::string serviceVersion; - const std::string applicationId; - const std::string expectedPrefix; - } UserAgentTests[] - = {{"storage-blob", "11.0.0", "", "azsdk-cpp-storage-blob/11.0.0 ("}, - {"storage-blob", - "11.0.0", - "AzCopy/10.0.4-Preview", - "AzCopy/10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 ("}, - {"storage-blob", - "11.0.0", - "AzCopy / 10.0.4-Preview ", - "AzCopy / 10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 ("}, - {"storage-blob", - "11.0.0", - " 01234567890123456789abcde ", - "01234567890123456789abcd azsdk-cpp-storage-blob/11.0.0 ("}}; - - constexpr auto UserAgentEnd = ')'; - constexpr auto OSInfoMinLength = 10; - - for (auto const& test : UserAgentTests) - { - Azure::Core::_internal::ClientOptions clientOptions; - clientOptions.Telemetry.ApplicationId = test.applicationId; - Azure::Core::Tracing::_internal::TracingContextFactory traceFactory( - clientOptions, test.serviceName, test.serviceVersion); - std::string userAgent = traceFactory.GetUserAgent(); - - EXPECT_FALSE(userAgent.empty()); - EXPECT_LT( - test.expectedPrefix.size() + OSInfoMinLength + sizeof(UserAgentEnd), userAgent.size()); - EXPECT_EQ(test.expectedPrefix, userAgent.substr(0, test.expectedPrefix.size())); - EXPECT_EQ(UserAgentEnd, userAgent[userAgent.size() - 1]); - } -} - TEST(TracingContextFactory, SimpleServiceSpanTests) { { @@ -118,13 +76,13 @@ TEST(TracingContextFactory, SimpleServiceSpanTests) { Azure::Core::_internal::ClientOptions clientOptions; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "my.service", "my-service-cpp", "1.0b2"); } { Azure::Core::_internal::ClientOptions clientOptions; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "my.service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); EXPECT_FALSE(contextAndSpan.Context.IsCancelled()); @@ -195,7 +153,7 @@ TEST(TracingContextFactory, BasicServiceSpanTests) { Azure::Core::_internal::ClientOptions clientOptions; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); ServiceSpan span = std::move(contextAndSpan.Span); @@ -211,7 +169,7 @@ TEST(TracingContextFactory, BasicServiceSpanTests) auto testTracer = std::make_shared(); clientOptions.Telemetry.TracingProvider = testTracer; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); ServiceSpan span = std::move(contextAndSpan.Span); @@ -232,7 +190,7 @@ TEST(TracingContextFactory, BasicServiceSpanTests) { Azure::Core::_internal::ClientOptions clientOptions; Azure::Core::Tracing::_internal::TracingContextFactory serviceTrace( - clientOptions, "my-service-cpp", "1.0b2"); + clientOptions, "My.Service", "my-service-cpp", "1.0b2"); auto contextAndSpan = serviceTrace.CreateTracingContext("My API", {}); ServiceSpan span = std::move(contextAndSpan.Span); diff --git a/sdk/core/azure-core/test/ut/telemetry_policy_test.cpp b/sdk/core/azure-core/test/ut/telemetry_policy_test.cpp index ad5c061b5..bfc1dddc6 100644 --- a/sdk/core/azure-core/test/ut/telemetry_policy_test.cpp +++ b/sdk/core/azure-core/test/ut/telemetry_policy_test.cpp @@ -34,84 +34,54 @@ private: TEST(TelemetryPolicy, telemetryString) { - std::vector> policy1; - std::vector> policy2; - std::vector> policy3; - std::vector> policy4; - std::string const expected1 = "azsdk-cpp-storage-blob/11.0.0 ("; - policy1.emplace_back(std::make_unique("storage-blob", "11.0.0")); - policy1.emplace_back(std::make_unique()); - HttpPipeline pipeline1(policy1); - - std::string const expected2 = "AzCopy/10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 ("; - Azure::Core::Http::Policies::TelemetryOptions options2; - options2.ApplicationId = "AzCopy/10.0.4-Preview"; - policy2.emplace_back(std::make_unique("storage-blob", "11.0.0", options2)); - policy2.emplace_back(std::make_unique()); - HttpPipeline pipeline2(policy2); - - std::string const expected3 = "AzCopy / 10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 ("; - Azure::Core::Http::Policies::TelemetryOptions options3; - options3.ApplicationId = " AzCopy / 10.0.4-Preview "; - policy3.emplace_back(std::make_unique("storage-blob", "11.0.0", options3)); - policy3.emplace_back(std::make_unique()); - HttpPipeline pipeline3(policy3); - - std::string const expected4 = "01234567890123456789abcd azsdk-cpp-storage-blob/11.0.0 ("; - Azure::Core::Http::Policies::TelemetryOptions options4; - options4.ApplicationId = " 01234567890123456789abcde "; - policy4.emplace_back(std::make_unique("storage-blob", "11.0.0", options4)); - policy4.emplace_back(std::make_unique()); - HttpPipeline pipeline4(policy4); + struct + { + const std::string serviceName; + const std::string serviceVersion; + const std::string applicationId; + const std::string expectedPrefix; + } UserAgentTests[] + = {{"storage-blob", "11.0.0", "", "azsdk-cpp-storage-blob/11.0.0 ("}, + {"storage-blob", + "11.0.0", + "AzCopy/10.0.4-Preview", + "AzCopy/10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 ("}, + {"storage-blob", + "11.0.0", + "AzCopy / 10.0.4-Preview ", + "AzCopy / 10.0.4-Preview azsdk-cpp-storage-blob/11.0.0 ("}, + {"storage-blob", + "11.0.0", + " 01234567890123456789abcde ", + "01234567890123456789abcd azsdk-cpp-storage-blob/11.0.0 ("}}; constexpr auto TelemetryHeader = "user-agent"; constexpr auto ClosingBrace = ')'; - constexpr auto OSInfoMin = 10; + constexpr auto OSInfoMinLength = 10; - auto request1 = Request(HttpMethod::Get, Url("https://www.microsoft.com")); - auto request2 = Request(HttpMethod::Get, Url("https://www.microsoft.com")); - auto request3 = Request(HttpMethod::Get, Url("https://www.microsoft.com")); - auto request4 = Request(HttpMethod::Get, Url("https://www.microsoft.com")); + for (auto const& test : UserAgentTests) + { + std::vector> policies; + Azure::Core::_internal::ClientOptions options; + options.Telemetry.ApplicationId = test.applicationId; + policies.emplace_back(std::make_unique( + test.serviceName, test.serviceVersion, options.Telemetry)); + policies.emplace_back(std::make_unique()); + HttpPipeline pipeline(policies); - Context context; - pipeline1.Send(request1, context); - pipeline2.Send(request2, context); - pipeline3.Send(request3, context); - pipeline4.Send(request4, context); + auto request = Request(HttpMethod::Get, Url("http://microsoft.com")); + Context context; - auto const headers1 = request1.GetHeaders(); - auto const headers2 = request2.GetHeaders(); - auto const headers3 = request3.GetHeaders(); - auto const headers4 = request4.GetHeaders(); + pipeline.Send(request, context); + auto const headers = request.GetHeaders(); + auto telemetryHeader = headers.find(TelemetryHeader); + EXPECT_NE(telemetryHeader, headers.end()); + auto const actualValue = telemetryHeader->second; + EXPECT_GE( + actualValue.size(), test.expectedPrefix.size() + OSInfoMinLength + sizeof(ClosingBrace)); + EXPECT_EQ(actualValue[actualValue.size() - 1], ClosingBrace); - auto telemetryHeader1 = headers1.find(TelemetryHeader); - auto telemetryHeader2 = headers2.find(TelemetryHeader); - auto telemetryHeader3 = headers3.find(TelemetryHeader); - auto telemetryHeader4 = headers4.find(TelemetryHeader); - - EXPECT_NE(telemetryHeader1, headers1.end()); - EXPECT_NE(telemetryHeader2, headers2.end()); - EXPECT_NE(telemetryHeader3, headers3.end()); - EXPECT_NE(telemetryHeader4, headers4.end()); - - auto const actualValue1 = telemetryHeader1->second; - auto const actualValue2 = telemetryHeader2->second; - auto const actualValue3 = telemetryHeader3->second; - auto const actualValue4 = telemetryHeader4->second; - - EXPECT_GE(actualValue1.size(), expected1.size() + OSInfoMin + sizeof(ClosingBrace)); - EXPECT_GE(actualValue2.size(), expected2.size() + OSInfoMin + sizeof(ClosingBrace)); - EXPECT_GE(actualValue3.size(), expected3.size() + OSInfoMin + sizeof(ClosingBrace)); - EXPECT_GE(actualValue4.size(), expected4.size() + OSInfoMin + sizeof(ClosingBrace)); - - EXPECT_EQ(actualValue1[actualValue1.size() - 1], ClosingBrace); - EXPECT_EQ(actualValue2[actualValue2.size() - 1], ClosingBrace); - EXPECT_EQ(actualValue3[actualValue3.size() - 1], ClosingBrace); - EXPECT_EQ(actualValue4[actualValue4.size() - 1], ClosingBrace); - - EXPECT_EQ(actualValue1.substr(0, expected1.size()), expected1); - EXPECT_EQ(actualValue2.substr(0, expected2.size()), expected2); - EXPECT_EQ(actualValue3.substr(0, expected3.size()), expected3); - EXPECT_EQ(actualValue4.substr(0, expected4.size()), expected4); + EXPECT_EQ(actualValue.substr(0, test.expectedPrefix.size()), test.expectedPrefix); + } } diff --git a/sdk/template/azure-template/src/template_client.cpp b/sdk/template/azure-template/src/template_client.cpp index cb1e74e4c..deaf2709f 100644 --- a/sdk/template/azure-template/src/template_client.cpp +++ b/sdk/template/azure-template/src/template_client.cpp @@ -10,9 +10,9 @@ using namespace Azure::Template; using namespace Azure::Template::_detail; +// Create a tracing factory for the service. TemplateClient::TemplateClient(TemplateClientOptions const& options) - : m_tracingFactory(options, "Template", PackageVersion::ToString()) - + : m_tracingFactory{options, "Azure.Template", "azure-template-cpp", PackageVersion::ToString()} { }