use crate::telemetry::TelemetryConfig;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
pub use opentelemetry::trace::{SpanContext, Status, TraceContextExt, TraceId};
#[cfg(not(target_arch = "wasm32"))]
pub struct TracerProvider {
provider: Arc<opentelemetry_sdk::trace::TracerProvider>,
service_name: String,
}
#[cfg(not(target_arch = "wasm32"))]
impl TracerProvider {
pub fn new(config: &TelemetryConfig) -> Result<Self, String> {
use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
if !config.enable_traces {
log::info!("Traces disabled in config, creating no-op tracer provider");
let provider = SdkTracerProvider::builder().build();
return Ok(Self {
provider: Arc::new(provider),
service_name: config.service_name.clone(),
});
}
log::info!(
"Initializing OpenTelemetry tracer provider for service: {}",
config.service_name
);
let provider = SdkTracerProvider::builder().build();
log::info!(
"Tracer provider initialized (no-op exporter - configure OTLP collector for production)"
);
Ok(Self {
provider: Arc::new(provider),
service_name: config.service_name.clone(),
})
}
pub fn tracer(&self, module_name: &str) -> Tracer {
use opentelemetry::trace::TracerProvider as _;
let otel_tracer = self.provider.tracer(module_name.to_string());
Tracer {
tracer: otel_tracer,
service_name: self.service_name.clone(),
}
}
pub fn is_initialized(&self) -> bool {
true }
pub fn shutdown(self) -> Result<(), String> {
log::info!("Shutting down tracer provider");
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
pub struct Tracer {
tracer: opentelemetry_sdk::trace::Tracer,
service_name: String,
}
#[cfg(not(target_arch = "wasm32"))]
impl Tracer {
pub fn service_name(&self) -> &str {
&self.service_name
}
pub fn start_span(&self, name: &str) -> Span {
use opentelemetry::trace::Tracer as _;
let otel_span = self.tracer.start(name.to_string());
Span {
span: Some(otel_span),
}
}
}
#[cfg(not(target_arch = "wasm32"))]
pub struct Span {
span: Option<opentelemetry_sdk::trace::Span>,
}
#[cfg(not(target_arch = "wasm32"))]
impl Span {
pub fn is_recording(&self) -> bool {
use opentelemetry::trace::Span as _;
if let Some(span) = &self.span {
span.is_recording()
} else {
false
}
}
pub fn with_attribute<K, V>(mut self, key: K, value: V) -> Self
where
K: Into<opentelemetry::Key>,
V: Into<opentelemetry::Value>,
{
use opentelemetry::trace::Span as _;
if let Some(span) = &mut self.span {
span.set_attribute(opentelemetry::KeyValue::new(key, value));
}
self
}
pub fn with_parent(self, _parent: &Span) -> Self {
self
}
pub fn add_event(&mut self, name: &str) {
use opentelemetry::trace::Span as _;
if let Some(span) = &mut self.span {
span.add_event(name.to_string(), vec![]);
}
}
pub fn set_status_error(&mut self, description: &str) {
use opentelemetry::trace::{Span as _, Status};
if let Some(span) = &mut self.span {
span.set_status(Status::error(description.to_string()));
}
}
pub fn end(&mut self) {
use opentelemetry::trace::Span as _;
if let Some(mut span) = self.span.take() {
span.end();
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl Drop for Span {
fn drop(&mut self) {
self.end();
}
}
#[cfg(target_arch = "wasm32")]
pub struct TracerProvider;
#[cfg(target_arch = "wasm32")]
impl TracerProvider {
pub fn new(_config: &TelemetryConfig) -> Result<Self, String> {
Err("OTLP tracer is not available on WASM (Phase 4 will add custom exporter)".to_string())
}
}
#[cfg(test)]
#[cfg(not(target_arch = "wasm32"))]
mod tests {
use super::*;
use crate::telemetry::TelemetryConfig;
#[test]
fn test_tracer_provider_creation() {
let config = TelemetryConfig::default();
let result = TracerProvider::new(&config);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_tracer_with_disabled_traces() {
let config = TelemetryConfig::default().with_traces_enabled(false);
let provider = TracerProvider::new(&config).expect("Should create no-op provider");
assert!(provider.is_initialized());
}
}