Clean up tracing logic - prepare for new tracing functionality. (#4263)
- Made Context immutable (including marking the static ApplicationContext as const - Put back TelemetryPolicy in all pipeline cases and removed User-Agent generation from RequestActivityPolicy (note: This part of the change may end up being reverted). Also updated parameters for TelemetryPolicy to make it clearer that the parameter is a package name, not a service name. - Changed Az.Namespace value from being the package name to being the service name. - Change test SpanExporter to fully capture exported values rather than keeping references to the values. Co-authored-by: Anton Kolesnyk <41349689+antkmsft@users.noreply.github.com>
This commit is contained in:
parent
0e9fba2623
commit
f757bb06e7
1
.vscode/cspell.json
vendored
1
.vscode/cspell.json
vendored
@ -17,6 +17,7 @@
|
||||
".gitignore",
|
||||
".vscode/cspell.json",
|
||||
"vcpkg-custom-ports",
|
||||
"**/assets.json",
|
||||
"ci.yml",
|
||||
"squid.conf*",
|
||||
"eng/common/**/*",
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -2,5 +2,5 @@
|
||||
"AssetsRepo": "Azure/azure-sdk-assets",
|
||||
"AssetsRepoPrefixPath": "cpp",
|
||||
"TagPrefix": "cpp/attestation",
|
||||
"Tag": "cpp/attestation_b384d96f95"
|
||||
"Tag": "cpp/attestation_10abfdc3e0"
|
||||
}
|
||||
|
||||
@ -42,9 +42,13 @@ AttestationAdministrationClient::AttestationAdministrationClient(
|
||||
std::string const& endpoint,
|
||||
std::shared_ptr<Core::Credentials::TokenCredential const> 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<std::unique_ptr<HttpPolicy>> perRetrypolicies;
|
||||
if (credential)
|
||||
@ -58,7 +62,11 @@ AttestationAdministrationClient::AttestationAdministrationClient(
|
||||
std::vector<std::unique_ptr<HttpPolicy>> perCallpolicies;
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options, std::move(perRetrypolicies), std::move(perCallpolicies));
|
||||
options,
|
||||
"security.attestation",
|
||||
PackageVersion::ToString(),
|
||||
std::move(perRetrypolicies),
|
||||
std::move(perCallpolicies));
|
||||
}
|
||||
|
||||
AttestationAdministrationClient AttestationAdministrationClient::Create(
|
||||
|
||||
@ -31,9 +31,13 @@ AttestationClient::AttestationClient(
|
||||
std::string const& endpoint,
|
||||
std::shared_ptr<Core::Credentials::TokenCredential const> 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<std::unique_ptr<HttpPolicy>> perRetrypolicies;
|
||||
if (credential)
|
||||
@ -47,7 +51,11 @@ AttestationClient::AttestationClient(
|
||||
std::vector<std::unique_ptr<HttpPolicy>> perCallpolicies;
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options, std::move(perRetrypolicies), std::move(perCallpolicies));
|
||||
options,
|
||||
"security.attestation",
|
||||
PackageVersion::ToString(),
|
||||
std::move(perRetrypolicies),
|
||||
std::move(perCallpolicies));
|
||||
}
|
||||
|
||||
Azure::Response<OpenIdMetadata> AttestationClient::GetOpenIdMetadata(
|
||||
|
||||
@ -45,6 +45,10 @@ namespace Azure { namespace Core { namespace Tracing { namespace OpenTelemetry {
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> 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.
|
||||
|
||||
@ -42,7 +42,7 @@ namespace Azure { namespace Core { namespace Tracing { namespace OpenTelemetry {
|
||||
std::string const& version) const
|
||||
{
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> returnTracer(
|
||||
m_tracerProvider->GetTracer(name, version));
|
||||
m_tracerProvider->GetTracer(name, version, OpenTelemetrySchemaUrlCurrent));
|
||||
return std::make_shared<Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryTracer>(
|
||||
returnTracer);
|
||||
}
|
||||
|
||||
@ -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<const std::string, RecordedSpan::Attribute>& 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<bool>(foundAttribute.second);
|
||||
auto actualVal = foundAttribute.second.BoolValue;
|
||||
EXPECT_EQ(expectedAttributes[foundAttribute.first].get<bool>(), 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<std::string>(foundAttribute.second);
|
||||
const auto& actualVal = foundAttribute.second.CStringValue;
|
||||
std::string expectedVal(expectedAttributes[foundAttribute.first].get<std::string>());
|
||||
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::string>());
|
||||
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<double>(foundAttribute.second);
|
||||
auto actualVal = foundAttribute.second.DoubleValue;
|
||||
EXPECT_EQ(expectedAttributes[foundAttribute.first].get<double>(), 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<std::string>(),
|
||||
span->GetInstrumentationScope().GetVersion());
|
||||
EXPECT_EQ(
|
||||
expectedSpanContents["library"]["schema"].get<std::string>(),
|
||||
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<std::string>(attributes.at("az.namespace")));
|
||||
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"
|
||||
}
|
||||
{
|
||||
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<std::string>(attributes.at("az.namespace")));
|
||||
})");
|
||||
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"
|
||||
}
|
||||
|
||||
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());
|
||||
})");
|
||||
|
||||
// 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<std::unique_ptr<HttpPolicy>> policies;
|
||||
policies.emplace_back(std::make_unique<RequestIdPolicy>());
|
||||
policies.emplace_back(std::make_unique<TelemetryPolicy>(
|
||||
"core-opentelemetry-test-service-cpp", "1.0.0.beta-2", clientOptions.Telemetry));
|
||||
policies.emplace_back(std::make_unique<RetryPolicy>(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<RawResponse>(1, 1, HttpStatusCode::Ok, "Something");
|
||||
auto response = std::make_unique<RawResponse>(1, 1, HttpStatusCode::Ok, "Something");
|
||||
|
||||
response->SetHeader("x-ms-request-id", "12345");
|
||||
|
||||
return response;
|
||||
}));
|
||||
|
||||
m_pipeline = std::make_unique<Azure::Core::Http::_internal::HttpPipeline>(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"
|
||||
}
|
||||
})");
|
||||
}
|
||||
|
||||
@ -7,15 +7,211 @@
|
||||
#include <opentelemetry/sdk/trace/exporter.h>
|
||||
|
||||
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<bool> BoolArrayValue;
|
||||
std::vector<int32_t> Int32ArrayValue;
|
||||
std::vector<int64_t> Int64ArrayValue;
|
||||
std::vector<uint32_t> UInt32ArrayValue;
|
||||
std::vector<uint64_t> UInt64ArrayValue;
|
||||
std::vector<double> DoubleArrayValue;
|
||||
std::vector<std::string> StringArrayValue;
|
||||
std::vector<uint8_t> 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<const bool> val)
|
||||
: Type{AttributeType::BoolArray}, BoolArrayValue(val.size(), val.data())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const int32_t> val)
|
||||
: Type{AttributeType::Int32Array}, Int32ArrayValue(val.begin(), val.end())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const uint32_t> val)
|
||||
: Type{AttributeType::UInt32Array}, UInt32ArrayValue(val.begin(), val.end())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const int64_t> val)
|
||||
: Type{AttributeType::Int64Array}, Int64ArrayValue(val.begin(), val.end())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const uint64_t> val)
|
||||
: Type{AttributeType::UInt64Array}, UInt64ArrayValue(val.begin(), val.end())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const double> val)
|
||||
: Type{AttributeType::DoubleArray}, DoubleArrayValue(val.begin(), val.end())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const opentelemetry::nostd::string_view> val)
|
||||
: Type{AttributeType::StringArray}, StringArrayValue(val.begin(), val.end())
|
||||
{
|
||||
}
|
||||
Attribute(opentelemetry::nostd::span<const uint8_t> 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<std::string, Attribute> 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<bool>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeCString: {
|
||||
m_attributes.emplace(key, Attribute{opentelemetry::nostd::get<const char*>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeInt: {
|
||||
m_attributes.emplace(key, Attribute{opentelemetry::nostd::get<int32_t>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeInt64: {
|
||||
m_attributes.emplace(key, Attribute{opentelemetry::nostd::get<int64_t>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeUInt: {
|
||||
m_attributes.emplace(key, Attribute{opentelemetry::nostd::get<uint32_t>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeDouble: {
|
||||
m_attributes.emplace(key, Attribute{opentelemetry::nostd::get<double>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeString: {
|
||||
m_attributes.emplace(
|
||||
key, Attribute{opentelemetry::nostd::get<opentelemetry::nostd::string_view>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanBool: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{opentelemetry::nostd::get<opentelemetry::nostd::span<const bool>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanInt: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{
|
||||
opentelemetry::nostd::get<opentelemetry::nostd::span<const int32_t>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanInt64: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{
|
||||
opentelemetry::nostd::get<opentelemetry::nostd::span<const int64_t>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanUInt: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{
|
||||
opentelemetry::nostd::get<opentelemetry::nostd::span<const uint32_t>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanDouble: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{
|
||||
opentelemetry::nostd::get<opentelemetry::nostd::span<const double>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanString: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{opentelemetry::nostd::get<
|
||||
opentelemetry::nostd::span<const opentelemetry::nostd::string_view>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeUInt64: {
|
||||
m_attributes.emplace(key, Attribute{opentelemetry::nostd::get<uint64_t>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanUInt64: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{
|
||||
opentelemetry::nostd::get<opentelemetry::nostd::span<const uint64_t>>(value)});
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::AttributeType::kTypeSpanByte: {
|
||||
m_attributes.emplace(
|
||||
key,
|
||||
Attribute{
|
||||
opentelemetry::nostd::get<opentelemetry::nostd::span<const uint8_t>>(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<Event> 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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -244,26 +244,10 @@ namespace Azure { namespace Core {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the tracer provider for the current context.
|
||||
*/
|
||||
std::shared_ptr<Tracing::TracerProvider> GetTracerProvider()
|
||||
{
|
||||
return m_contextSharedState->TraceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the tracer provider for the current context.
|
||||
*/
|
||||
void SetTracerProvider(std::shared_ptr<Tracing::TracerProvider> 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
|
||||
|
||||
@ -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<Azure::Core::Tracing::TracerProvider> TracingProvider{
|
||||
Context::ApplicationContext.GetTracerProvider()};
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> 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))
|
||||
{
|
||||
}
|
||||
|
||||
@ -31,7 +31,33 @@ namespace Azure { namespace Core { namespace Http { namespace _internal {
|
||||
* @remark See #policy.hpp
|
||||
*/
|
||||
class HttpPipeline final {
|
||||
private:
|
||||
protected:
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> 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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>& 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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& perRetryPolicies,
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& perCallPolicies,
|
||||
bool includeTelemetryPolicy,
|
||||
std::string const& telemetryServiceName = {},
|
||||
std::string const& telemetryServiceVersion = {})
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& 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<Azure::Core::Http::Policies::_internal::RequestIdPolicy>());
|
||||
|
||||
// Telemetry
|
||||
if (includeTelemetryPolicy)
|
||||
{
|
||||
// Telemetry (user-agent header)
|
||||
m_policies.emplace_back(
|
||||
std::make_unique<Azure::Core::Http::Policies::_internal::TelemetryPolicy>(
|
||||
telemetryServiceName, telemetryServiceVersion, clientOptions.Telemetry));
|
||||
}
|
||||
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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> 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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>& 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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& perRetryPolicies,
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& 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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& perRetryPolicies,
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&& perCallPolicies)
|
||||
: HttpPipeline(
|
||||
clientOptions,
|
||||
std::move(perRetryPolicies),
|
||||
std::move(perCallPolicies),
|
||||
false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct HTTP pipeline with the sequence of HTTP policies provided.
|
||||
*
|
||||
|
||||
@ -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<Azure::Core::Tracing::_internal::Tracer> 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<Azure::Core::Tracing::_internal::AttributeSet> 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<bool>(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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/context.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@ -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<Azure::Core::Tracing::Tracer>
|
||||
*/
|
||||
virtual std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> CreateTracer(
|
||||
std::string const& name,
|
||||
std::string const& version) const = 0;
|
||||
std::string const& version = {}) const = 0;
|
||||
|
||||
virtual ~TracerProviderImpl() = default;
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
using namespace Azure::Core;
|
||||
|
||||
Context Context::ApplicationContext;
|
||||
const Context Context::ApplicationContext;
|
||||
|
||||
Azure::DateTime Azure::Core::Context::GetDeadline() const
|
||||
{
|
||||
|
||||
@ -23,28 +23,13 @@ std::unique_ptr<RawResponse> RequestActivityPolicy::Send(
|
||||
NextHttpPolicy nextPolicy,
|
||||
Context const& context) const
|
||||
{
|
||||
Azure::Nullable<std::string> 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<RawResponse> 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<std::string> requestId = request.GetHeader("x-ms-client-request-id");
|
||||
if (requestId.HasValue())
|
||||
{
|
||||
@ -71,9 +62,12 @@ std::unique_ptr<RawResponse> RequestActivityPolicy::Send(
|
||||
TracingAttributes::RequestId.ToString(), requestId.Value());
|
||||
}
|
||||
|
||||
// We retrieved the value of the user-agent header above.
|
||||
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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<TracerProviderImpl> TracerProviderImplGetter::TracerImplFromTracer(
|
||||
|
||||
@ -514,26 +514,3 @@ TEST(Context, KeyTypePairPrecondition)
|
||||
EXPECT_TRUE(c3.TryGetValue<std::string>(key, strValue));
|
||||
EXPECT_TRUE(strValue == s);
|
||||
}
|
||||
|
||||
TEST(Context, SetTracingProvider)
|
||||
{
|
||||
class TestTracingProvider final : public Azure::Core::Tracing::TracerProvider {
|
||||
public:
|
||||
TestTracingProvider() : TracerProvider() {}
|
||||
~TestTracingProvider() {}
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> 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<TestTracingProvider>();
|
||||
context.SetTracerProvider(testProvider);
|
||||
EXPECT_EQ(testProvider, context.GetTracerProvider());
|
||||
}
|
||||
|
||||
@ -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<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
|
||||
// Add the request ID policy - this adds the x-ms-request-id attribute to the pipeline.
|
||||
policies.emplace_back(std::make_unique<RequestIdPolicy>());
|
||||
policies.emplace_back(std::make_unique<TelemetryPolicy>("my-service", "1.0.0.beta-2"));
|
||||
policies.emplace_back(std::make_unique<RetryPolicy>(RetryOptions{}));
|
||||
policies.emplace_back(
|
||||
std::make_unique<RequestActivityPolicy>(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);
|
||||
|
||||
@ -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<TestTracingProvider>();
|
||||
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);
|
||||
|
||||
@ -34,84 +34,54 @@ private:
|
||||
|
||||
TEST(TelemetryPolicy, telemetryString)
|
||||
{
|
||||
std::vector<std::unique_ptr<HttpPolicy>> policy1;
|
||||
std::vector<std::unique_ptr<HttpPolicy>> policy2;
|
||||
std::vector<std::unique_ptr<HttpPolicy>> policy3;
|
||||
std::vector<std::unique_ptr<HttpPolicy>> policy4;
|
||||
|
||||
std::string const expected1 = "azsdk-cpp-storage-blob/11.0.0 (";
|
||||
policy1.emplace_back(std::make_unique<TelemetryPolicy>("storage-blob", "11.0.0"));
|
||||
policy1.emplace_back(std::make_unique<NoOpPolicy>());
|
||||
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<TelemetryPolicy>("storage-blob", "11.0.0", options2));
|
||||
policy2.emplace_back(std::make_unique<NoOpPolicy>());
|
||||
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<TelemetryPolicy>("storage-blob", "11.0.0", options3));
|
||||
policy3.emplace_back(std::make_unique<NoOpPolicy>());
|
||||
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<TelemetryPolicy>("storage-blob", "11.0.0", options4));
|
||||
policy4.emplace_back(std::make_unique<NoOpPolicy>());
|
||||
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<std::unique_ptr<HttpPolicy>> policies;
|
||||
Azure::Core::_internal::ClientOptions options;
|
||||
options.Telemetry.ApplicationId = test.applicationId;
|
||||
policies.emplace_back(std::make_unique<TelemetryPolicy>(
|
||||
test.serviceName, test.serviceVersion, options.Telemetry));
|
||||
policies.emplace_back(std::make_unique<NoOpPolicy>());
|
||||
HttpPipeline pipeline(policies);
|
||||
|
||||
auto request = Request(HttpMethod::Get, Url("http://microsoft.com"));
|
||||
Context context;
|
||||
pipeline1.Send(request1, context);
|
||||
pipeline2.Send(request2, context);
|
||||
pipeline3.Send(request3, context);
|
||||
pipeline4.Send(request4, 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user