apollo_opentelemetry/lib.rs
1#![allow(dead_code)]
2//! OpenTelemetry crate for Apollo platform.
3//!
4//! This crate provides a standard way to configure and use OpenTelemetry.
5//!
6//! # Getting Started
7//!
8//! Create telemetry from configuration:
9//!
10//! ```no_run
11//! use apollo_configuration::parse_yaml;
12//! use apollo_opentelemetry::{OpenTelemetryConfig, Telemetry};
13//!
14//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
15//! let yaml = r#"
16//! tracer_provider:
17//! processors:
18//! - batch:
19//! exporter:
20//! otlp_http:
21//! endpoint: http://localhost:4318
22//! "#;
23//! let config: OpenTelemetryConfig = parse_yaml(yaml, &Default::default())?;
24//! let telemetry = Telemetry::new(config)?;
25//! # Ok(())
26//! # }
27//! ```
28//!
29//! Or use the builder for more control:
30//!
31//! ```no_run
32//! use apollo_configuration::parse_yaml;
33//! use apollo_opentelemetry::{OpenTelemetryConfig, Telemetry};
34//!
35//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
36//! let yaml = r#"
37//! tracer_provider:
38//! processors:
39//! - batch:
40//! exporter:
41//! otlp_http:
42//! endpoint: http://localhost:4318
43//! "#;
44//! let config: OpenTelemetryConfig = parse_yaml(yaml, &Default::default())?;
45//! let telemetry = Telemetry::builder(config)
46//! .with_global_tracer_provider()
47//! .with_global_meter_provider()
48//! .build()?;
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! # Feature Flags
54//!
55//! - `otlp` - Enable OTLP exporters (HTTP and gRPC)
56//! - `honeycomb` - Enable [Honeycomb](https://honeycomb.io) exporter
57//! - `new-relic` - Enable [New Relic](https://newrelic.com) exporter
58//! - `grafana-cloud` - Enable [Grafana Cloud](https://grafana.com/products/cloud/) exporter
59//! - `datadog-otlp` - Enable [Datadog OTLP intake](https://docs.datadoghq.com/opentelemetry/) exporter
60
61// Re-export crate as `apollo_opentelemetry` for proc macro generated code.
62// This allows `::apollo_opentelemetry::` paths to work both within this crate
63// and from external crates that depend on it.
64extern crate self as apollo_opentelemetry;
65
66mod config;
67mod error;
68mod exporters;
69mod processors;
70mod providers;
71mod telemetry;
72mod to_value;
73#[cfg(feature = "tower")]
74pub mod tower;
75
76pub mod metrics;
77
78pub use config::OpenTelemetryConfig;
79pub use error::{InitError, RateLimitProcessorConfigError};
80pub use telemetry::{Telemetry, TelemetryBuilder};
81pub use to_value::ToValue;
82mod macros;
83pub use macros::traced;
84
85/// Creates a new span using OpenTelemetry.
86///
87/// This macro provides a concise syntax for creating spans with optional
88/// attributes, span kind, parent context, and body (closure or async block).
89///
90/// # Syntax
91///
92/// ```text
93/// span!(name [, scope: expr] [, parent: cx] [, kind: K] [, attrs...] [, body])
94/// ```
95///
96/// Where:
97/// - `name` - Span name (string literal or expression)
98/// - `scope: expr` - Optional instrumentation scope. If omitted, uses calling crate's scope.
99/// - `parent: cx` - Optional parent context. If omitted, uses `Context::current()`.
100/// - `kind: K` - Optional span kind (e.g., `kind: SpanKind::Server`)
101/// - `attrs...` - Optional attributes as `"key" = value` pairs
102/// - `body` - Optional body: `|| { }`, `move || { }`, `async { }`, or `async move { }`
103///
104/// Without a body, returns the span for manual lifetime management. With a body,
105/// the span is active for the duration of the closure/async block.
106///
107/// # Examples
108///
109/// ```
110/// use apollo_opentelemetry::span;
111/// use opentelemetry::trace::SpanKind;
112///
113/// // Simple span
114/// let _span = span!("my-operation");
115///
116/// // With closure
117/// span!("process", || {
118/// // work happens here
119/// });
120///
121/// // With kind and attributes
122/// span!("db-query", kind: SpanKind::Client, "db.system" = "postgresql", || {
123/// // execute query
124/// });
125///
126/// // Async
127/// # async fn example() {
128/// span!("async-operation", async {
129/// // async work
130/// }).await;
131/// # }
132/// ```
133///
134/// ## Spawned tasks
135///
136/// The async block form returns a `Future`, making it composable with
137/// `tokio::spawn()`, `select!`, `join!`, etc. The span context is captured
138/// when the span is created, so spawned tasks inherit the parent automatically:
139///
140/// ```
141/// use apollo_opentelemetry::span;
142///
143/// # async fn do_background_work() {}
144/// # async fn example() {
145/// span!("parent", async {
146/// tokio::spawn(span!("child", async {
147/// do_background_work().await;
148/// })).await.unwrap();
149/// }).await;
150/// # }
151/// ```
152///
153/// ## Dynamic span names
154///
155/// Span names can be expressions — variables, `format!`, function calls, etc.:
156///
157/// ```
158/// use apollo_opentelemetry::span;
159///
160/// let name = String::from("dynamic-span");
161/// let _span = span!(name);
162///
163/// let id = 42u32;
164/// span!(format!("request-{id}"), || { /* work */ });
165/// ```
166///
167/// ## Optional attributes
168///
169/// Attribute values of `Option<T>` are silently skipped when `None`:
170///
171/// ```
172/// use apollo_opentelemetry::span;
173///
174/// let user_id: Option<i64> = Some(42);
175/// let _span = span!("request", "user.id" = user_id);
176/// ```
177///
178/// ## Automatic span status from Result
179///
180/// Closure and async block forms automatically set the span status based on
181/// the return value:
182///
183/// - `Ok(_)` → span status set to `Ok`
184/// - `Err(e)` → span status set to `Error` with the error's display message
185/// - Any other type → span status unchanged
186///
187/// ```
188/// use apollo_opentelemetry::span;
189///
190/// # fn do_work() -> Result<(), String> { Ok(()) }
191/// fn process() -> Result<(), String> {
192/// span!("process", || {
193/// do_work()?;
194/// Ok(())
195/// })
196/// }
197/// ```
198///
199/// ## Custom Instrumentation Scope
200///
201/// Use `scope:` to override the instrumentation scope (library name and version
202/// reported in telemetry). If omitted, the calling crate's scope is used.
203///
204/// ```
205/// use apollo_opentelemetry::span;
206/// use opentelemetry::InstrumentationScope;
207///
208/// # fn my_scope() -> InstrumentationScope { InstrumentationScope::builder("example").build() }
209/// let _span = span!("my-operation", scope: my_scope());
210/// ```
211///
212/// # Explicit Parent Context
213///
214/// Use `parent:` to create a span with a specific parent context:
215///
216/// ```
217/// use apollo_opentelemetry::span;
218/// use opentelemetry::Context;
219/// use opentelemetry::trace::TraceContextExt;
220///
221/// # fn example(parent_cx: &Context) {
222/// span!("child", parent: parent_cx, || {
223/// // This span's parent comes from parent_cx
224/// });
225/// # }
226/// ```
227pub use apollo_opentelemetry_macros::span;
228
229/// Private module for macro implementation details.
230/// Not part of the public API - may change without notice.
231#[doc(hidden)]
232pub mod __private {
233 pub use opentelemetry;
234
235 pub use crate::macros::span_result::{RecordSpanResult, SpanResultRef};
236}