use std::sync::Arc;
use autumn_web::{AppState, AutumnError};
use opentelemetry::metrics::Meter;
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::trace::SdkTracerProvider;
use crate::config::OtelConfig;
use crate::error::OtelError;
use crate::plugin::Shared;
#[derive(Clone)]
pub struct OtelTelemetry {
shared: Arc<Shared>,
}
impl OtelTelemetry {
pub(crate) const fn new(shared: Arc<Shared>) -> Self {
Self { shared }
}
#[must_use]
pub fn from_state(state: &AppState) -> Option<Self> {
state.extension::<Self>().map(|handle| (*handle).clone())
}
#[must_use]
pub fn config(&self) -> Option<&OtelConfig> {
self.shared.config()
}
#[must_use]
pub fn is_running(&self) -> bool {
self.shared.is_running()
}
#[must_use]
pub fn tracer_provider(&self) -> Option<SdkTracerProvider> {
self.shared.handles().and_then(|h| h.tracer.clone())
}
#[must_use]
pub fn meter_provider(&self) -> Option<SdkMeterProvider> {
self.shared.handles().and_then(|h| h.meter.clone())
}
#[must_use]
pub fn logger_provider(&self) -> Option<SdkLoggerProvider> {
self.shared.handles().and_then(|h| h.logger.clone())
}
#[must_use]
pub fn meter(&self, name: &'static str) -> Meter {
crate::metrics::meter_named(name)
}
pub fn shutdown(&self) {
self.shared.shutdown();
}
}
impl std::fmt::Debug for OtelTelemetry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OtelTelemetry")
.field("running", &self.is_running())
.field(
"service_name",
&self.config().map(|config| config.service_name.as_str()),
)
.field("signals", &self.config().map(OtelConfig::enabled_signals))
.finish_non_exhaustive()
}
}
impl axum::extract::FromRequestParts<AppState> for OtelTelemetry {
type Rejection = AutumnError;
fn from_request_parts(
_parts: &mut http::request::Parts,
state: &AppState,
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
std::future::ready(
Self::from_state(state).ok_or_else(|| OtelError::NotInstalled.into_autumn()),
)
}
}
#[cfg(test)]
mod tests;