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:
Larry Osterman 2023-01-25 11:07:25 -08:00 committed by GitHub
parent 0e9fba2623
commit f757bb06e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 548 additions and 428 deletions

1
.vscode/cspell.json vendored
View File

@ -17,6 +17,7 @@
".gitignore",
".vscode/cspell.json",
"vcpkg-custom-ports",
"**/assets.json",
"ci.yml",
"squid.conf*",
"eng/common/**/*",

View File

@ -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:

View File

@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "cpp",
"TagPrefix": "cpp/attestation",
"Tag": "cpp/attestation_b384d96f95"
"Tag": "cpp/attestation_10abfdc3e0"
}

View File

@ -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(

View File

@ -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(

View File

@ -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.

View File

@ -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);
}

View File

@ -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")));
}
{
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")));
}
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<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"
}
})");
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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))
{
}

View File

@ -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)
{
m_policies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::TelemetryPolicy>(
telemetryServiceName, telemetryServiceVersion, clientOptions.Telemetry));
}
// Telemetry (user-agent header)
m_policies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::TelemetryPolicy>(
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.
*

View File

@ -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

View File

@ -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);
}
/**

View File

@ -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;
};

View File

@ -5,7 +5,7 @@
using namespace Azure::Core;
Context Context::ApplicationContext;
const Context Context::ApplicationContext;
Azure::DateTime Azure::Core::Context::GetDeadline() const
{

View File

@ -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.
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);

View File

@ -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;

View File

@ -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(

View File

@ -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());
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
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);
}
}

View File

@ -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()}
{
}