Skip to content

Added ability to test distributed tracing functionality. #2896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 13, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions sdk/core/azure_core/src/http/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,19 @@ impl Pipeline {
let tracer = core_client_options
.instrumentation
.tracer_provider
.as_ref()
.map(|provider| {
// Note that the choice to use "None" as the namespace here
// is intentional.
// The `azure_namespace` parameter is used to populate the `az.namespace`
// span attribute, however that information is only known by the author of the
// client library, not the core library.
// It is also *not* a constant that can be derived from the crate information -
// it is a value that is determined from the list of resource providers
// listed [here](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers).
//
// This information can only come from the package owner. It doesn't make sense
// to burden all users of the azure_core pipeline with determining this
// information, so we use `None` here.
provider.get_tracer(None, crate_name.unwrap_or("Unknown"), crate_version)
});

Expand All @@ -75,18 +86,6 @@ impl Pipeline {

let mut per_try_policies = per_try_policies.clone();
if let Some(ref tracer) = tracer {
// Note that the choice to use "None" as the namespace here
// is intentional.
// The `azure_namespace` parameter is used to populate the `az.namespace`
// span attribute, however that information is only known by the author of the
// client library, not the core library.
// It is also *not* a constant that can be derived from the crate information -
// it is a value that is determined from the list of resource providers
// listed [here](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers).
//
// This information can only come from the package owner. It doesn't make sense
// to burden all users of the azure_core pipeline with determining this
// information, so we use `None` here.
let request_instrumentation_policy =
RequestInstrumentationPolicy::new(Some(tracer.clone()));
push_unique(&mut per_try_policies, request_instrumentation_policy);
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/azure_core_opentelemetry/assets.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "rust",
"Tag": "rust/azure_core_opentelemetry_2bc5efc404",
"Tag": "rust/azure_core_opentelemetry_3a7ac50edd",
"TagPrefix": "rust/azure_core_opentelemetry"
}
148 changes: 144 additions & 4 deletions sdk/core/azure_core_opentelemetry/tests/telemetry_service_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,21 @@ impl TestServiceClientWithMacros {
mod tests {
use super::*;
use ::tracing::{info, trace};
use azure_core::http::{ExponentialRetryOptions, RetryOptions};
use azure_core::tracing::TracerProvider;
use azure_core::Result;
use azure_core_test::{recorded, TestContext};
use azure_core::{
http::{ExponentialRetryOptions, RetryOptions},
tracing::TracerProvider,
Result,
};
use azure_core_test::{
recorded,
tracing::{InstrumentationInformation, InstrumentedApiInformation},
TestContext,
};
use opentelemetry::trace::{
SpanKind as OpenTelemetrySpanKind, Status as OpenTelemetrySpanStatus,
};
use opentelemetry::Value as OpenTelemetryAttributeValue;
use typespec_client_core::http;

fn create_exportable_tracer_provider() -> (Arc<SdkTracerProvider>, InMemorySpanExporter) {
let otel_exporter = InMemorySpanExporter::default();
Expand Down Expand Up @@ -665,4 +672,137 @@ mod tests {

Ok(())
}

#[recorded::test()]
async fn test_http_tracing_tests(ctx: TestContext) -> Result<()> {
let recording = ctx.recording();
let credential = recording.credential().clone();
let package_name = recording.var("CARGO_PKG_NAME", None);
let package_version = recording.var("CARGO_PKG_VERSION", None);
azure_core_test::tracing::test_instrumentation_for_api(
|tracer_provider| {
TestServiceClientWithMacros::new(
"https://azuresdkforcpp.azurewebsites.net",
credential,
Some(TestServiceClientWithMacrosOptions {
client_options: ClientOptions {
instrumentation: Some(InstrumentationOptions {
tracer_provider: Some(tracer_provider.clone()),
}),
..Default::default()
},
..Default::default()
}),
)
},
|client| {
let client = client;
Box::pin(async move { client.get("get", None).await })
},
InstrumentationInformation {
package_name,
package_version,
package_namespace: Some("Az.TestServiceClient"),
api_calls: vec![InstrumentedApiInformation {
api_name: None,
..Default::default()
}],
},
)
.await?;

Ok(())
}

#[recorded::test()]
async fn test_function_tracing_tests(ctx: TestContext) -> Result<()> {
let recording = ctx.recording();
let credential = recording.credential().clone();
let package_name = recording.var("CARGO_PKG_NAME", None);
let package_version = recording.var("CARGO_PKG_VERSION", None);
azure_core_test::tracing::test_instrumentation_for_api(
|tracer_provider| {
TestServiceClientWithMacros::new(
"https://azuresdkforcpp.azurewebsites.net",
credential,
Some(TestServiceClientWithMacrosOptions {
client_options: ClientOptions {
instrumentation: Some(InstrumentationOptions {
tracer_provider: Some(tracer_provider.clone()),
}),
..Default::default()
},
..Default::default()
}),
)
},
|client| {
let client = client;
Box::pin(async move { client.get_with_function_tracing("get", None).await })
},
InstrumentationInformation {
package_name,
package_version,
package_namespace: Some("Az.TestServiceClient"),
api_calls: vec![InstrumentedApiInformation {
api_name: Some("macros_get_with_tracing"),
additional_api_attributes: vec![
("a.b", 1.into()),
("az.telemetry", "Abc".into()),
("string attribute", "get".into()),
],
..Default::default()
}],
},
)
.await?;

Ok(())
}
#[recorded::test()]
async fn test_function_tracing_tests_error(ctx: TestContext) -> Result<()> {
let recording = ctx.recording();
let credential = recording.credential().clone();
let package_name = recording.var("CARGO_PKG_NAME", None);
let package_version = recording.var("CARGO_PKG_VERSION", None);
azure_core_test::tracing::test_instrumentation_for_api(
|tracer_provider| {
TestServiceClientWithMacros::new(
"https://azuresdkforcpp.azurewebsites.net",
credential,
Some(TestServiceClientWithMacrosOptions {
client_options: ClientOptions {
instrumentation: Some(InstrumentationOptions {
tracer_provider: Some(tracer_provider.clone()),
}),
..Default::default()
},
..Default::default()
}),
)
},
|client| {
let client = client;
Box::pin(async move { client.get_with_function_tracing("index.htm", None).await })
},
InstrumentationInformation {
package_name,
package_version,
package_namespace: Some("Az.TestServiceClient"),
api_calls: vec![InstrumentedApiInformation {
api_name: Some("macros_get_with_tracing"),
expected_status_code: http::StatusCode::NotFound,
additional_api_attributes: vec![
("a.b", 1.into()),
("az.telemetry", "Abc".into()),
("string attribute", "index.htm".into()),
],
..Default::default()
}],
},
)
.await?;

Ok(())
}
}
Loading
Loading