juncture_tracing/lib.rs
1//! OpenTelemetry integration and tracing for Juncture applications
2//!
3//! This crate provides instrumentation capabilities for Juncture graph execution,
4//! including structured logging, span management, and metrics collection.
5//!
6//! # Feature Flags
7//!
8//! - `otel` (default: off) - Enable OpenTelemetry trace/metrics export configuration
9//!
10//! # Basic Usage
11//!
12//! Initialize tracing for your Juncture application:
13//!
14//! ```no_run
15//! use juncture_tracing::init_tracing;
16//!
17//! let _ = init_tracing();
18//! // Your application code here
19//! ```
20//!
21//! # With OpenTelemetry
22//!
23//! When the `otel` feature is enabled, you can configure OTLP export:
24//!
25//! ```ignore
26//! use juncture_tracing::{init, config::TracingConfig};
27//! use tracing::Level;
28//!
29//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
30//! init()
31//! .with_service_name("my-agent-service")
32//! .with_log_level(Level::INFO)
33//! .install()?;
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! # Span Constants
39//!
40//! Use the provided span and attribute constants for consistency:
41//!
42//! ```
43//! use juncture_tracing::{spans::names, spans::attrs};
44//!
45//! assert_eq!(names::GRAPH_INVOKE, "juncture.graph.invoke");
46//! assert_eq!(attrs::NODE_NAME, "juncture.node.name");
47//! ```
48//!
49//! # Distributed Trace Propagation
50//!
51//! Use the propagation module for W3C `TraceContext` injection/extraction:
52//!
53//! ```ignore
54//! use juncture_tracing::propagation::{inject_trace_context, extract_trace_context};
55//! use std::collections::HashMap;
56//!
57//! // Inject before crossing process boundaries
58//! let mut carrier = HashMap::new();
59//! inject_trace_context(&mut carrier);
60//!
61//! // Extract on the receiving side
62//! let ctx = extract_trace_context(&carrier);
63//! ```
64
65pub mod callback;
66pub mod spans;
67pub mod test_utils;
68pub mod types;
69
70#[cfg(feature = "otel")]
71pub mod config;
72#[cfg(feature = "otel")]
73pub mod metrics;
74#[cfg(feature = "otel")]
75pub mod propagation;
76
77// Re-exports for convenience
78pub use callback::{
79 CallbackHandlerAdapter, GraphCallbackHandler, GraphInterruptEvent, GraphResumeEvent,
80};
81pub use test_utils::TestMetricsCollector;
82pub use types::{LlmCacheKeyInput, LlmCachePolicy, ServerInfo};
83
84#[cfg(feature = "otel")]
85pub use config::{TracingConfig, TracingInstallResult, init};
86#[cfg(feature = "otel")]
87pub use metrics::{
88 CounterBuilder, GaugeBuilder, HistogramBuilder, MetricsRegistry, RegistryMetricsCollector,
89 registry,
90};
91#[cfg(feature = "otel")]
92pub use propagation::{attach_context, extract_trace_context, inject_trace_context};
93
94// Re-export span constants
95pub use spans::{attrs, names};
96
97/// Initialize basic tracing without OpenTelemetry
98///
99/// Sets up a simple `tracing-subscriber` for structured logging.
100/// Use this when you don't need OTLP export.
101///
102/// # Panics
103///
104/// This function panics if a global tracing subscriber is already installed.
105///
106/// # Examples
107///
108/// ```no_run
109/// use juncture_tracing::init_tracing;
110///
111/// let _ = init_tracing();
112/// // Your application code
113/// ```
114pub fn init_tracing() {
115 let _ = tracing_subscriber::fmt()
116 .with_env_filter(
117 tracing_subscriber::EnvFilter::builder()
118 .with_default_directive(tracing::Level::INFO.into())
119 .from_env_lossy(),
120 )
121 .try_init();
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_span_constants_exist() {
130 // Verify all important span names are defined
131 assert_eq!(names::GRAPH_INVOKE, "juncture.graph.invoke");
132 assert_eq!(names::NODE_EXECUTE, "juncture.node.execute");
133 assert_eq!(names::LLM_CALL, "juncture.llm.call");
134 assert_eq!(names::TOOL_CALL, "juncture.tool.call");
135
136 // Verify attributes are defined
137 assert_eq!(attrs::THREAD_ID, "juncture.thread.id");
138 assert_eq!(attrs::NODE_NAME, "juncture.node.name");
139 assert_eq!(attrs::LLM_MODEL, "juncture.llm.model");
140 }
141
142 #[test]
143 fn test_init_tracing_doesnt_panic() {
144 // Calling init_tracing multiple times is safe (it will just fail silently)
145 init_tracing();
146 init_tracing();
147 }
148
149 #[test]
150 fn test_types_reexport() {
151 // Verify types are accessible from the root
152 let server_info = ServerInfo::new();
153 assert!(server_info.assistant_id.is_none());
154
155 let cache_policy = LlmCachePolicy::new();
156 assert!(cache_policy.key_func.is_none());
157 }
158
159 #[test]
160 fn test_test_utils_reexport() {
161 // Verify test utilities are accessible from the root
162 let collector = TestMetricsCollector::new();
163 assert_eq!(collector.get_counter("test"), 0);
164 }
165}
166
167// Rust guideline compliant 2026-05-19