Skip to content

Commit 7588bc7

Browse files
committed
test(o11y): add spanner e2e trace context propagation test
1 parent f26ef9e commit 7588bc7

5 files changed

Lines changed: 167 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/o11y/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ google-cloud-lro.workspace = true
3737
google-cloud-test-utils = { workspace = true }
3838
google-cloud-showcase-v1beta1 = { workspace = true, features = ["default"] }
3939
google-cloud-storage = { workspace = true, features = ["default"] }
40+
google-cloud-spanner = { workspace = true }
4041
google-cloud-wkt = { workspace = true }
4142
storage-samples = { workspace = true }
4243
google-cloud-trace-v1 = { workspace = true, features = ["default"] }

tests/o11y/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub mod mock_collector;
2828
#[cfg(google_cloud_unstable_tracing)]
2929
pub mod otlp;
3030
#[cfg(google_cloud_unstable_tracing)]
31+
pub mod spanner_tracing;
32+
#[cfg(google_cloud_unstable_tracing)]
3133
pub mod storage_tracing;
3234
#[cfg(google_cloud_unstable_tracing)]
3335
pub mod tracing;

tests/o11y/src/spanner_tracing.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use crate::e2e::wait_for_trace;
16+
use google_cloud_spanner::client::Spanner;
17+
use google_cloud_test_utils::runtime_config::project_id;
18+
use opentelemetry::trace::TraceContextExt;
19+
use std::collections::BTreeSet;
20+
use tracing_opentelemetry::OpenTelemetrySpanExt;
21+
22+
const ROOT_SPAN_NAME: &str = "e2e-spanner-test";
23+
24+
pub async fn spanner_e2e_tracing() -> anyhow::Result<()> {
25+
let project_id = project_id()?;
26+
// Create a trace with a number of interesting spans from the
27+
// `google-cloud-spanner` client.
28+
let trace_id = send_trace(&project_id).await?;
29+
let required = BTreeSet::from_iter([
30+
ROOT_SPAN_NAME,
31+
"google.spanner.v1.Spanner/CreateSession",
32+
"google.spanner.v1.Spanner/BeginTransaction",
33+
"Spanner.CreateSession",
34+
"Spanner.BeginTransaction",
35+
]);
36+
let trace = wait_for_trace(&project_id, &trace_id, &required).await?;
37+
38+
println!("TRACE SPANS DUMP:");
39+
for span in &trace.spans {
40+
println!("Span: {:?}", span);
41+
}
42+
43+
// Verify the expected spans appear in the trace:
44+
let span_names = trace
45+
.spans
46+
.iter()
47+
.map(|s| s.name.as_str())
48+
.collect::<BTreeSet<_>>();
49+
let missing = required.difference(&span_names).collect::<Vec<_>>();
50+
assert!(missing.is_empty(), "missing={missing:?}\n\n{trace:?}");
51+
52+
Ok(())
53+
}
54+
55+
async fn send_trace(project_id: &str) -> anyhow::Result<String> {
56+
// 1. Setup Telemetry (Google Cloud Destination)
57+
let creds = google_cloud_auth::credentials::Builder::default().build()?;
58+
let (provider, _, _) = crate::e2e::set_up_providers(
59+
project_id,
60+
"e2e-telemetry-test",
61+
"spanner-test".to_string(),
62+
creds,
63+
)
64+
.await?;
65+
66+
// 2. Generate Trace
67+
// Start a root span
68+
let root_span = tracing::info_span!("e2e_root", { "otel.name" } = ROOT_SPAN_NAME);
69+
let trace_id = root_span
70+
.context()
71+
.span()
72+
.span_context()
73+
.trace_id()
74+
.to_string();
75+
76+
use tracing::Instrument;
77+
let _ = client_library_operations(project_id)
78+
.instrument(root_span)
79+
.await;
80+
81+
println!(
82+
"View generated trace in Console: https://console.cloud.google.com/traces/explorer;traceId={}?project={}",
83+
trace_id, project_id
84+
);
85+
86+
// 4. Force flush to ensure spans are sent.
87+
if let Err(e) = provider.force_flush() {
88+
tracing::error!("error flushing provider: {e:}");
89+
}
90+
Ok(trace_id)
91+
}
92+
93+
async fn client_library_operations(project: &str) -> anyhow::Result<()> {
94+
// Explicitly opt-in to E2E tracing headers for the test
95+
unsafe {
96+
std::env::set_var(
97+
"GOOGLE_CLOUD_TEST_EXTRA_HEADERS",
98+
"x-goog-spanner-end-to-end-tracing=true",
99+
);
100+
}
101+
let instance = std::env::var("GOOGLE_CLOUD_SPANNER_TEST_INSTANCE")
102+
.unwrap_or_else(|_| "trace-propagation-test-instance".to_string());
103+
let db_id = std::env::var("GOOGLE_CLOUD_SPANNER_TEST_DATABASE")
104+
.unwrap_or_else(|_| "test-database".to_string());
105+
106+
let db_path = format!(
107+
"projects/{}/instances/{}/databases/{}",
108+
project, instance, db_id
109+
);
110+
111+
use google_cloud_auth::credentials::Builder as CredentialsBuilder;
112+
let creds = CredentialsBuilder::default().build()?;
113+
let spanner_client = Spanner::builder()
114+
.with_credentials(creds.clone())
115+
.with_tracing()
116+
.build()
117+
.await?;
118+
119+
// Calling `build()` on the database client triggers a `CreateSession` RPC
120+
let db_client = spanner_client.database_client(db_path).build().await?;
121+
122+
use google_cloud_spanner::model::{
123+
BeginTransactionRequest, TransactionOptions, transaction_options,
124+
};
125+
let mut req = BeginTransactionRequest::default();
126+
req.session = db_client.session.name.clone();
127+
128+
let mut options = TransactionOptions::default();
129+
options.mode = Some(transaction_options::Mode::ReadOnly(Box::default()));
130+
req.options = Some(options);
131+
132+
let _ = db_client
133+
.spanner
134+
.begin_transaction(req, google_cloud_gax::options::RequestOptions::default())
135+
.await;
136+
137+
Ok(())
138+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#[cfg(all(google_cloud_unstable_tracing, feature = "run-integration-tests"))]
16+
mod spanner_tracing {
17+
use google_cloud_test_utils::errors::anydump;
18+
19+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
20+
async fn spanner_e2e_tracing() -> anyhow::Result<()> {
21+
integration_tests_o11y::spanner_tracing::spanner_e2e_tracing()
22+
.await
23+
.inspect_err(anydump)
24+
}
25+
}

0 commit comments

Comments
 (0)