Skip to content

Commit b10da18

Browse files
committed
feat: add example app to debug issue with propagation
1 parent 183e30c commit b10da18

7 files changed

Lines changed: 662 additions & 1 deletion

File tree

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ members = [
66
"datadog-opentelemetry",
77
"datadog-opentelemetry/examples/propagator",
88
"datadog-opentelemetry/examples/simple_tracing",
9+
"dd-trace-examples",
910
]
1011

1112
# https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2
@@ -33,7 +34,7 @@ opentelemetry_sdk = { version = "0.31.0", features = [
3334
opentelemetry = { version = "0.31.0", features = [
3435
"trace",
3536
], default-features = false }
36-
opentelemetry-semantic-conventions = { version = "0.31.0", features = [
37+
opentelemetry-semantic-conventions = { version = "0.30.0", features = [
3738
"semconv_experimental",
3839
] }
3940
tokio = { version = "1.44.1" }

dd-trace-examples/Cargo.toml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[package]
2+
name = "dd-trace-examples"
3+
rust-version.workspace = true
4+
edition.workspace = true
5+
version.workspace = true
6+
license.workspace = true
7+
repository.workspace = true
8+
readme.workspace = true
9+
description.workspace = true
10+
11+
[dependencies]
12+
# Datadog OpenTelemetry
13+
datadog-opentelemetry = { path = "../datadog-opentelemetry" }
14+
dd-trace = { path = "../dd-trace" }
15+
16+
# Tracing ecosystem
17+
tracing = "0.1"
18+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
19+
tracing-opentelemetry = "0.31"
20+
21+
# HTTP server
22+
axum = "0.7"
23+
tokio = { version = "1.44.1", features = ["full"] }
24+
25+
# OpenTelemetry
26+
opentelemetry = { version = "0.30.0", features = ["trace"] }
27+
opentelemetry_sdk = { version = "0.30.0", features = ["trace"] }
28+
opentelemetry-http = { version = "0.30.0" }
29+
30+
# Utilities
31+
serde = { version = "1.0", features = ["derive"] }
32+
serde_json = "1.0"
33+
tower = "0.4"
34+
tower-http = { version = "0.5", features = ["cors", "trace"] }
35+
rand = "0.8"

dd-trace-examples/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Datadog OpenTelemetry Example
2+
3+
This example demonstrates how to use `datadog-opentelemetry` with the `tracing` crate and `tracing-opentelemetry` bridge in an Axum HTTP server.
4+
5+
## Features
6+
7+
- **Datadog OpenTelemetry Integration**: Uses the `datadog-opentelemetry` crate for sending traces to Datadog
8+
- **Tracing Bridge**: Demonstrates the `tracing-opentelemetry` bridge for seamless integration
9+
- **HTTP Server**: Built with Axum framework
10+
- **Structured Logging**: Uses the `tracing` crate for structured logging
11+
- **Custom Spans**: Shows how to create custom spans with attributes
12+
- **REST API**: Includes endpoints for user management and health checks
13+
14+
## Prerequisites
15+
16+
1. **Datadog Agent**: Make sure you have a Datadog agent running locally or accessible
17+
2. **Rust**: Ensure you have Rust 1.84.1+ installed
18+
19+
## Running the Example
20+
21+
### 1. Start the Datadog Agent (if running locally)
22+
23+
```bash
24+
# Using Docker
25+
docker run -d --name datadog-agent \
26+
-e DD_API_KEY=your_api_key \
27+
-e DD_APM_ENABLED=true \
28+
-e DD_APM_NON_LOCAL_TRAFFIC=true \
29+
-p 8126:8126 \
30+
datadog/agent:latest
31+
32+
# Or using the official install script
33+
DD_API_KEY=your_api_key DD_APM_ENABLED=true bash -c "$(curl -L https://s1.datadoghq.com/install_script_agent7.sh)"
34+
```
35+
36+
### 2. Run the Example
37+
38+
```bash
39+
# From the examples directory
40+
cargo run
41+
42+
# Or from the workspace root
43+
cargo run -p dd-trace-examples
44+
```
45+
46+
The server will start on `http://localhost:3000`
47+
48+
### 3. Test the API
49+
50+
```bash
51+
# Health check
52+
curl http://localhost:3000/health
53+
54+
# Get user by ID
55+
curl http://localhost:3000/users/123
56+
57+
# Create a new user
58+
curl -X POST http://localhost:3000/users \
59+
-H "Content-Type: application/json" \
60+
-d '{"name": "John Doe", "email": "john@example.com"}'
61+
```
62+
63+
## Configuration
64+
65+
The example is configured to send traces to `http://localhost:8126/v0.5/traces` (default Datadog agent endpoint). You can modify the configuration in the `main()` function:
66+
67+
```rust
68+
let pipeline = DatadogPipeline::new()
69+
.with_service_name("dd-trace-example")
70+
.with_service_version("1.0.0")
71+
.with_env("development")
72+
.with_trace_endpoint("http://localhost:8126/v0.5/traces")
73+
.build()?;
74+
```
75+
76+
## Key Components
77+
78+
### 1. Datadog Pipeline Setup
79+
80+
```rust
81+
use datadog_opentelemetry::DatadogPipeline;
82+
83+
let pipeline = DatadogPipeline::new()
84+
.with_service_name("dd-trace-example")
85+
.with_service_version("1.0.0")
86+
.with_env("development")
87+
.build()?;
88+
```
89+
90+
### 2. Tracing Integration
91+
92+
```rust
93+
use tracing::{info, instrument};
94+
use tracing_opentelemetry::OpenTelemetrySpanExt;
95+
96+
#[instrument(skip(tracer))]
97+
async fn get_user(Path(id): Path<u32>, tracer: Extension<Tracer>) -> Json<ApiResponse<User>> {
98+
let span = tracer.start("get_user");
99+
span.set_attribute(opentelemetry::KeyValue::new("user.id", id.to_string()));
100+
// ... function logic
101+
span.end();
102+
}
103+
```
104+
105+
### 3. Global Tracer Setup
106+
107+
```rust
108+
use opentelemetry::global;
109+
110+
let tracer = pipeline.tracer();
111+
global::set_tracer_provider(pipeline.trace_provider());
112+
```
113+
114+
## What You'll See
115+
116+
1. **Console Logs**: Structured logging output showing the application flow
117+
2. **Datadog Traces**: Spans and traces sent to your Datadog agent
118+
3. **Custom Attributes**: User ID, name, and email attributes on spans
119+
4. **Performance Metrics**: Timing information for each operation
120+
121+
## Troubleshooting
122+
123+
### Common Issues
124+
125+
1. **Connection Refused**: Make sure the Datadog agent is running and accessible
126+
2. **Permission Denied**: Check that the agent endpoint is correct and accessible
127+
3. **No Traces in Datadog**: Verify your API key and agent configuration
128+
129+
### Debug Mode
130+
131+
The example includes debug logging for the datadog-opentelemetry crate:
132+
133+
```rust
134+
tracing_subscriber::fmt()
135+
.with_env_filter("info,datadog_opentelemetry=debug")
136+
.init();
137+
```
138+
139+
## Next Steps
140+
141+
- Modify the service name, version, and environment
142+
- Add more custom attributes to spans
143+
- Implement error handling and error spans
144+
- Add metrics collection
145+
- Configure sampling and filtering rules

dd-trace-examples/src/main.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use axum::{extract::Request, http::Method, response::Json, routing::get, Router};
2+
use datadog_opentelemetry;
3+
use dd_trace::Config;
4+
use opentelemetry::trace::TracerProvider;
5+
use serde::{Deserialize, Serialize};
6+
use std::net::SocketAddr;
7+
use tower_http::{
8+
cors::{Any, CorsLayer},
9+
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
10+
};
11+
use tracing::{field::Empty, info, instrument, level_filters::LevelFilter, Level};
12+
use tracing_opentelemetry::OpenTelemetrySpanExt;
13+
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
14+
15+
#[derive(Debug, Serialize, Deserialize)]
16+
struct User {
17+
id: u32,
18+
name: String,
19+
email: String,
20+
}
21+
22+
#[derive(Debug, Serialize, Deserialize)]
23+
struct CreateUserRequest {
24+
name: String,
25+
email: String,
26+
}
27+
28+
#[derive(Debug, Serialize, Deserialize)]
29+
struct ApiResponse<T> {
30+
success: bool,
31+
data: Option<T>,
32+
message: String,
33+
}
34+
35+
#[instrument]
36+
async fn health_check() -> Json<ApiResponse<()>> {
37+
// Simulate health check
38+
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
39+
40+
info!("Health check performed");
41+
42+
Json(ApiResponse {
43+
success: true,
44+
data: None,
45+
message: "Service is healthy".to_string(),
46+
})
47+
}
48+
49+
#[instrument]
50+
async fn root() -> &'static str {
51+
info!("Root endpoint accessed");
52+
"Datadog OpenTelemetry Example API\n\nAvailable endpoints:\n- GET /health - Health check\n- GET /users/{id} - Get user by ID\n- POST /users - Create new user"
53+
}
54+
55+
#[tokio::main]
56+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
57+
info!("Starting Datadog OpenTelemetry example application...");
58+
59+
// Initialize Datadog OpenTelemetry pipeline
60+
let tracer_provider = datadog_opentelemetry::tracing()
61+
.with_config(
62+
Config::builder()
63+
.set_trace_agent_url("http://0.0.0.0:8126".into())
64+
.set_service("dd-trace-example".to_string())
65+
.set_version("1.0.0".to_string())
66+
.set_env("development".to_string())
67+
.set_log_level_filter(dd_trace::log::LevelFilter::Info)
68+
.build(),
69+
)
70+
.init();
71+
72+
info!("Datadog pipeline initialized successfully");
73+
tracing_subscriber::registry()
74+
.with(tracing_subscriber::fmt::layer().with_filter(LevelFilter::DEBUG))
75+
.with(
76+
tracing_opentelemetry::layer().with_tracer(tracer_provider.tracer("dd-trace-example")),
77+
)
78+
.try_init()?;
79+
80+
// Create CORS layer
81+
let cors = CorsLayer::new()
82+
.allow_methods([Method::GET, Method::POST])
83+
.allow_origin(Any);
84+
85+
// Build our application with a route
86+
let app = Router::new()
87+
.route("/", get(root))
88+
.route("/health", get(health_check))
89+
.layer(cors)
90+
.layer(
91+
TraceLayer::new_for_http()
92+
.make_span_with(make_span)
93+
.on_request(DefaultOnRequest::new().level(Level::DEBUG))
94+
.on_response(DefaultOnResponse::new().level(Level::DEBUG))
95+
.on_failure(DefaultOnFailure::new().level(Level::ERROR)),
96+
);
97+
98+
// Run it
99+
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
100+
info!("Starting server on {}", addr);
101+
102+
let listener = tokio::net::TcpListener::bind(addr).await?;
103+
axum::serve(listener, app).await?;
104+
105+
// Shutdown the tracer provider
106+
tracer_provider.shutdown()?;
107+
108+
Ok(())
109+
}
110+
111+
fn make_span(request: &Request) -> tracing::Span {
112+
let route = request.uri().path();
113+
let span = tracing::info_span!(
114+
target: "otel::tracing",
115+
"incoming request",
116+
// Tracing span name must be a static string, but we can use this field to use a
117+
// dynamic string for the opentelemetry span name.
118+
// See https://github.com/tokio-rs/tracing/pull/732.
119+
otel.name = %route,
120+
http.grpc_status = Empty,
121+
http.grpc_status_str = Empty,
122+
error.message = Empty,
123+
rpc.system = "grpc",
124+
uri = %request.uri(),
125+
route = route,
126+
org_id = Empty,
127+
upstream_req_id = Empty,
128+
query_source = Empty,
129+
);
130+
131+
// Extract tracing information from incoming request and propagate it
132+
// to this span
133+
let remote_parent_ctx = opentelemetry::global::get_text_map_propagator(|propagator| {
134+
let extractor = opentelemetry_http::HeaderExtractor(request.headers());
135+
propagator.extract(&extractor)
136+
});
137+
138+
span.set_parent(remote_parent_ctx.clone());
139+
span
140+
}

examples/Cargo.toml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[package]
2+
name = "dd-trace-examples"
3+
rust-version.workspace = true
4+
edition.workspace = true
5+
version.workspace = true
6+
license.workspace = true
7+
repository.workspace = true
8+
readme.workspace = true
9+
description.workspace = true
10+
11+
[dependencies]
12+
# Datadog OpenTelemetry
13+
datadog-opentelemetry = { path = "../datadog-opentelemetry" }
14+
15+
# Tracing ecosystem
16+
tracing = "0.1"
17+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
18+
tracing-opentelemetry = "0.24"
19+
20+
# HTTP server
21+
axum = "0.7"
22+
tokio = { version = "1.44.1", features = ["full"] }
23+
24+
# OpenTelemetry
25+
opentelemetry = { version = "0.30.0", features = ["trace"] }
26+
opentelemetry_sdk = { version = "0.30.0", features = ["trace"] }
27+
28+
# Utilities
29+
serde = { version = "1.0", features = ["derive"] }
30+
serde_json = "1.0"
31+
tower = "0.4"
32+
tower-http = { version = "0.5", features = ["cors", "trace"] }
33+
rand = "0.8"

0 commit comments

Comments
 (0)