Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"datadog-opentelemetry",
"datadog-opentelemetry/examples/propagator",
"datadog-opentelemetry/examples/simple_tracing",
"dd-trace-examples",
]

# https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2
Expand Down Expand Up @@ -33,7 +34,7 @@ opentelemetry_sdk = { version = "0.31.0", features = [
opentelemetry = { version = "0.31.0", features = [
"trace",
], default-features = false }
opentelemetry-semantic-conventions = { version = "0.31.0", features = [
opentelemetry-semantic-conventions = { version = "0.30.0", features = [
"semconv_experimental",
] }
tokio = { version = "1.44.1" }
Expand Down
34 changes: 34 additions & 0 deletions dd-trace-examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "dd-trace-examples"
rust-version.workspace = true
edition.workspace = true
version.workspace = true
license.workspace = true
repository.workspace = true
readme.workspace = true
description.workspace = true

[dependencies]
# Datadog OpenTelemetry
datadog-opentelemetry = { path = "../datadog-opentelemetry" }

# Tracing ecosystem
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-opentelemetry = "0.32"

# HTTP server
axum = "0.7"
tokio = { version = "1.44.1", features = ["full"] }

# OpenTelemetry
opentelemetry = { version = "0.31.0", features = ["trace"] }
opentelemetry_sdk = { version = "0.31.0", features = ["trace"] }
opentelemetry-http = { version = "0.31.0" }

# Utilities
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "trace"] }
rand = "0.8"
145 changes: 145 additions & 0 deletions dd-trace-examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Datadog OpenTelemetry Example

This example demonstrates how to use `datadog-opentelemetry` with the `tracing` crate and `tracing-opentelemetry` bridge in an Axum HTTP server.

## Features

- **Datadog OpenTelemetry Integration**: Uses the `datadog-opentelemetry` crate for sending traces to Datadog
- **Tracing Bridge**: Demonstrates the `tracing-opentelemetry` bridge for seamless integration
- **HTTP Server**: Built with Axum framework
- **Structured Logging**: Uses the `tracing` crate for structured logging
- **Custom Spans**: Shows how to create custom spans with attributes
- **REST API**: Includes endpoints for user management and health checks

## Prerequisites

1. **Datadog Agent**: Make sure you have a Datadog agent running locally or accessible
2. **Rust**: Ensure you have Rust 1.84.1+ installed

## Running the Example

### 1. Start the Datadog Agent (if running locally)

```bash
# Using Docker
docker run -d --name datadog-agent \
-e DD_API_KEY=your_api_key \
-e DD_APM_ENABLED=true \
-e DD_APM_NON_LOCAL_TRAFFIC=true \
-p 8126:8126 \
datadog/agent:latest

# Or using the official install script
DD_API_KEY=your_api_key DD_APM_ENABLED=true bash -c "$(curl -L https://s1.datadoghq.com/install_script_agent7.sh)"
```

### 2. Run the Example

```bash
# From the examples directory
cargo run

# Or from the workspace root
cargo run -p dd-trace-examples
```

The server will start on `http://localhost:3000`

### 3. Test the API

```bash
# Health check
curl http://localhost:3000/health

# Get user by ID
curl http://localhost:3000/users/123

# Create a new user
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@example.com"}'
```

## Configuration

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:

```rust
let pipeline = DatadogPipeline::new()
.with_service_name("dd-trace-example")
.with_service_version("1.0.0")
.with_env("development")
.with_trace_endpoint("http://localhost:8126/v0.5/traces")
.build()?;
```

## Key Components

### 1. Datadog Pipeline Setup

```rust
use datadog_opentelemetry::DatadogPipeline;

let pipeline = DatadogPipeline::new()
.with_service_name("dd-trace-example")
.with_service_version("1.0.0")
.with_env("development")
.build()?;
```

### 2. Tracing Integration

```rust
use tracing::{info, instrument};
use tracing_opentelemetry::OpenTelemetrySpanExt;

#[instrument(skip(tracer))]
async fn get_user(Path(id): Path<u32>, tracer: Extension<Tracer>) -> Json<ApiResponse<User>> {
let span = tracer.start("get_user");
span.set_attribute(opentelemetry::KeyValue::new("user.id", id.to_string()));
// ... function logic
span.end();
}
```

### 3. Global Tracer Setup

```rust
use opentelemetry::global;

let tracer = pipeline.tracer();
global::set_tracer_provider(pipeline.trace_provider());
```

## What You'll See

1. **Console Logs**: Structured logging output showing the application flow
2. **Datadog Traces**: Spans and traces sent to your Datadog agent
3. **Custom Attributes**: User ID, name, and email attributes on spans
4. **Performance Metrics**: Timing information for each operation

## Troubleshooting

### Common Issues

1. **Connection Refused**: Make sure the Datadog agent is running and accessible
2. **Permission Denied**: Check that the agent endpoint is correct and accessible
3. **No Traces in Datadog**: Verify your API key and agent configuration

### Debug Mode

The example includes debug logging for the datadog-opentelemetry crate:

```rust
tracing_subscriber::fmt()
.with_env_filter("info,datadog_opentelemetry=debug")
.init();
```

## Next Steps

- Modify the service name, version, and environment
- Add more custom attributes to spans
- Implement error handling and error spans
- Add metrics collection
- Configure sampling and filtering rules
139 changes: 139 additions & 0 deletions dd-trace-examples/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use axum::{extract::Request, http::Method, response::Json, routing::get, Router};
use datadog_opentelemetry::{self, configuration::Config, log::LevelFilter as DdLevelFilter};
use opentelemetry::trace::TracerProvider;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use tower_http::{
cors::{Any, CorsLayer},
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
};
use tracing::{field::Empty, info, instrument, level_filters::LevelFilter, Level};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};

#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct CreateUserRequest {
name: String,
email: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
message: String,
}

#[instrument]
async fn health_check() -> Json<ApiResponse<()>> {
// Simulate health check
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;

info!("Health check performed");

Json(ApiResponse {
success: true,
data: None,
message: "Service is healthy".to_string(),
})
}

#[instrument]
async fn root() -> &'static str {
info!("Root endpoint accessed");
"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"
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("Starting Datadog OpenTelemetry example application...");

// Initialize Datadog OpenTelemetry pipeline
let tracer_provider = datadog_opentelemetry::tracing()
.with_config(
Config::builder()
.set_trace_agent_url("http://0.0.0.0:8126".into())
.set_service("dd-trace-example".to_string())
.set_version("1.0.0".to_string())
.set_env("development".to_string())
.set_log_level_filter(DdLevelFilter::Info)
.build(),
)
.init();

info!("Datadog pipeline initialized successfully");
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer().with_filter(LevelFilter::DEBUG))
.with(
tracing_opentelemetry::layer().with_tracer(tracer_provider.tracer("dd-trace-example")),
)
.try_init()?;

// Create CORS layer
let cors = CorsLayer::new()
.allow_methods([Method::GET, Method::POST])
.allow_origin(Any);

// Build our application with a route
let app = Router::new()
.route("/", get(root))
.route("/health", get(health_check))
.layer(cors)
.layer(
TraceLayer::new_for_http()
.make_span_with(make_span)
.on_request(DefaultOnRequest::new().level(Level::DEBUG))
.on_response(DefaultOnResponse::new().level(Level::DEBUG))
.on_failure(DefaultOnFailure::new().level(Level::ERROR)),
);

// Run it
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
info!("Starting server on {}", addr);

let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;

// Shutdown the tracer provider
tracer_provider.shutdown()?;

Ok(())
}

fn make_span(request: &Request) -> tracing::Span {
let route = request.uri().path();
let span = tracing::info_span!(
target: "otel::tracing",
"incoming request",
// Tracing span name must be a static string, but we can use this field to use a
// dynamic string for the opentelemetry span name.
// See https://github.com/tokio-rs/tracing/pull/732.
otel.name = %route,
http.grpc_status = Empty,
http.grpc_status_str = Empty,
error.message = Empty,
rpc.system = "grpc",
uri = %request.uri(),
route = route,
org_id = Empty,
upstream_req_id = Empty,
query_source = Empty,
);

// Extract tracing information from incoming request and propagate it
// to this span
let remote_parent_ctx = opentelemetry::global::get_text_map_propagator(|propagator| {
let extractor = opentelemetry_http::HeaderExtractor(request.headers());
propagator.extract(&extractor)
});

let _ = span.set_parent(remote_parent_ctx.clone());
span
}
33 changes: 33 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "dd-trace-examples"
rust-version.workspace = true
edition.workspace = true
version.workspace = true
license.workspace = true
repository.workspace = true
readme.workspace = true
description.workspace = true

[dependencies]
# Datadog OpenTelemetry
datadog-opentelemetry = { path = "../datadog-opentelemetry" }

# Tracing ecosystem
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-opentelemetry = "0.24"

# HTTP server
axum = "0.7"
tokio = { version = "1.44.1", features = ["full"] }

# OpenTelemetry
opentelemetry = { version = "0.30.0", features = ["trace"] }
opentelemetry_sdk = { version = "0.30.0", features = ["trace"] }

# Utilities
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "trace"] }
rand = "0.8"
Loading
Loading