|
| 1 | +// Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +// or more contributor license agreements. See the NOTICE file |
| 3 | +// distributed with this work for additional information |
| 4 | +// regarding copyright ownership. The ASF licenses this file |
| 5 | +// to you under the Apache License, Version 2.0 (the |
| 6 | +// "License"); you may not use this file except in compliance |
| 7 | +// with the License. You may obtain a copy of the License at |
| 8 | +// |
| 9 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +// |
| 11 | +// Unless required by applicable law or agreed to in writing, |
| 12 | +// software distributed under the License is distributed on an |
| 13 | +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +// KIND, either express or implied. See the License for the |
| 15 | +// specific language governing permissions and limitations |
| 16 | +// under the License. |
| 17 | +// |
| 18 | +// This product includes software developed at Datadog (https://www.datadoghq.com/) Copyright 2025 Datadog, Inc. |
| 19 | + |
| 20 | +//! # Distributed OTLP Example |
| 21 | +//! |
| 22 | +//! This example demonstrates OpenTelemetry integration with DataFusion for distributed tracing. |
| 23 | +//! It shows how to: |
| 24 | +//! |
| 25 | +//! - Configure OpenTelemetry with an OTLP exporter |
| 26 | +//! - Set up service metadata and tracing layers |
| 27 | +//! - Instrument DataFusion query execution for distributed plans |
| 28 | +//! - Execute multiple queries with separate contexts |
| 29 | +//! |
| 30 | +//! ## Prerequisites |
| 31 | +//! |
| 32 | +//! Before running this example, you'll need an OpenTelemetry collector. For local development, |
| 33 | +//! Jaeger is recommended: |
| 34 | +//! |
| 35 | +//! ```bash |
| 36 | +//! docker run --rm --name jaeger \ |
| 37 | +//! -p 16686:16686 \ |
| 38 | +//! -p 4317:4317 \ |
| 39 | +//! jaegertracing/jaeger:2.7.0 |
| 40 | +//! ``` |
| 41 | +//! |
| 42 | +//! After starting Jaeger, you can view traces at http://localhost:16686 |
| 43 | +//! |
| 44 | +//! ## Running the Example |
| 45 | +//! |
| 46 | +//! ```bash |
| 47 | +//! cargo run --example distributed_otlp |
| 48 | +//! ``` |
| 49 | +//! |
| 50 | +//! This example executes all 22 TPCH benchmark queries with distributed query execution enabled. |
| 51 | +
|
| 52 | +use std::time::Duration; |
| 53 | + |
| 54 | +use datafusion::{common::internal_datafusion_err, error::Result}; |
| 55 | +use integration_utils::{init_session, run_traced_query}; |
| 56 | +use opentelemetry::{KeyValue, trace::TracerProvider}; |
| 57 | +use opentelemetry_otlp::WithExportConfig; |
| 58 | +use opentelemetry_sdk::{Resource, trace::Sampler}; |
| 59 | +use tracing::{Instrument, Level}; |
| 60 | +use tracing_subscriber::{Registry, fmt, prelude::*}; |
| 61 | + |
| 62 | +#[tokio::main] |
| 63 | +async fn main() -> Result<()> { |
| 64 | + // Initialize tracing infrastructure and obtain a tracer provider. |
| 65 | + let tracer_provider = init_tracing()?; |
| 66 | + |
| 67 | + // Run the example under the root span. |
| 68 | + run_distributed_otlp_example().await?; |
| 69 | + |
| 70 | + // Properly shutdown tracing to ensure all data is flushed. |
| 71 | + tracer_provider |
| 72 | + .shutdown() |
| 73 | + .map_err(|e| internal_datafusion_err!("Tracer shutdown error: {}", e)) |
| 74 | +} |
| 75 | + |
| 76 | +async fn run_distributed_otlp_example() -> Result<()> { |
| 77 | + // Loop over all 22 TPCH queries |
| 78 | + for i in 1..=22 { |
| 79 | + let query_name = format!("tpch/q{}", i); |
| 80 | + |
| 81 | + // Create a new root span for each query to ensure independent traces. |
| 82 | + // This span will be the root of a new trace tree. |
| 83 | + let span = tracing::info_span!("tpch_query", query = %query_name, query_num = i); |
| 84 | + |
| 85 | + // Execute the query within the new root span context. |
| 86 | + async { |
| 87 | + tracing::info!("Running TPCH query: {}", query_name); |
| 88 | + |
| 89 | + // Initialize a distinct DataFusion session context for each query. |
| 90 | + let ctx = init_session(false, true, 5, true, true).await?; |
| 91 | + |
| 92 | + // Run the SQL query with tracing enabled. |
| 93 | + run_traced_query(&ctx, &query_name).await |
| 94 | + } |
| 95 | + .instrument(span) |
| 96 | + .await?; |
| 97 | + } |
| 98 | + |
| 99 | + Ok(()) |
| 100 | +} |
| 101 | + |
| 102 | +/// Initializes OpenTelemetry and tracing infrastructure to enable tracing of query execution. |
| 103 | +fn init_tracing() -> Result<opentelemetry_sdk::trace::SdkTracerProvider> { |
| 104 | + // Set service metadata for tracing. |
| 105 | + let resource = Resource::builder() |
| 106 | + .with_attribute(KeyValue::new("service.name", "datafusion-tracing")) |
| 107 | + .build(); |
| 108 | + |
| 109 | + // Configure an OTLP exporter to send tracing data. |
| 110 | + let exporter = opentelemetry_otlp::SpanExporter::builder() |
| 111 | + .with_tonic() |
| 112 | + .with_endpoint("http://localhost:4317") // Endpoint for OTLP collector. |
| 113 | + .with_timeout(Duration::from_secs(10)) |
| 114 | + .build() |
| 115 | + .map_err(|e| internal_datafusion_err!("OTLP exporter error: {}", e))?; |
| 116 | + |
| 117 | + // Create a tracer provider configured with the exporter and sampling strategy. |
| 118 | + let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() |
| 119 | + .with_batch_exporter(exporter) |
| 120 | + .with_resource(resource) |
| 121 | + .with_sampler(Sampler::AlwaysOn) |
| 122 | + .build(); |
| 123 | + |
| 124 | + // Obtain a tracer instance for recording tracing information. |
| 125 | + let tracer = tracer_provider.tracer("datafusion-tracing-query"); |
| 126 | + |
| 127 | + // Create a telemetry layer using the tracer to collect and filter tracing data at INFO level. |
| 128 | + let telemetry_layer = tracing_opentelemetry::layer() |
| 129 | + .with_tracer(tracer) |
| 130 | + .with_filter(tracing::level_filters::LevelFilter::INFO); |
| 131 | + |
| 132 | + // Create a formatting layer to output logs to stdout, including thread IDs and names. |
| 133 | + let fmt_layer = fmt::layer() |
| 134 | + .with_thread_ids(true) |
| 135 | + .with_thread_names(true) |
| 136 | + .with_writer(std::io::stdout.with_max_level(Level::INFO)); |
| 137 | + |
| 138 | + // Combine the telemetry and formatting layers into a tracing subscriber and initialize it. |
| 139 | + Registry::default() |
| 140 | + .with(telemetry_layer) |
| 141 | + .with(fmt_layer) |
| 142 | + .init(); |
| 143 | + |
| 144 | + // Return the configured tracer provider |
| 145 | + Ok(tracer_provider) |
| 146 | +} |
0 commit comments