use std::sync::Arc;
use opentelemetry::InstrumentationScope;
use opentelemetry::logs::LoggerProvider as _;
use opentelemetry::metrics::{Meter, MeterProvider as _};
use opentelemetry_prometheus_text_exporter::PrometheusExporter;
use opentelemetry_sdk::Resource;
use opentelemetry_sdk::logs::{SdkLogger, SdkLoggerProvider};
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::trace::SdkTracerProvider;
#[derive(Clone)]
pub struct ObservabilityRuntime {
inner: Arc<RuntimeInner>,
}
struct RuntimeInner {
resource: Resource,
meter_provider: SdkMeterProvider,
prometheus_exporter: Option<PrometheusExporter>,
audit_logger_provider: Option<SdkLoggerProvider>,
tracer_provider: Option<SdkTracerProvider>,
}
impl ObservabilityRuntime {
pub fn builder(meter_provider: SdkMeterProvider) -> ObservabilityRuntimeBuilder {
ObservabilityRuntimeBuilder {
resource: None,
meter_provider,
prometheus_exporter: None,
audit_logger_provider: None,
tracer_provider: None,
}
}
pub fn noop() -> Self {
Self::builder(SdkMeterProvider::default()).build()
}
pub fn meter(&self, scope_name: &'static str) -> Meter {
self.inner
.meter_provider
.meter_with_scope(InstrumentationScope::builder(scope_name).build())
}
pub fn audit_logger(&self, scope_name: &'static str) -> Option<SdkLogger> {
let provider = self.inner.audit_logger_provider.as_ref()?;
let scope = InstrumentationScope::builder(scope_name).build();
Some(provider.logger_with_scope(scope))
}
pub fn prometheus_exporter(&self) -> Option<PrometheusExporter> {
self.inner.prometheus_exporter.clone()
}
pub fn meter_provider(&self) -> &SdkMeterProvider {
&self.inner.meter_provider
}
pub fn resource(&self) -> &Resource {
&self.inner.resource
}
pub fn audit_logger_provider(&self) -> Option<&SdkLoggerProvider> {
self.inner.audit_logger_provider.as_ref()
}
pub fn tracer_provider(&self) -> Option<&SdkTracerProvider> {
self.inner.tracer_provider.as_ref()
}
pub fn shutdown(&self) {
if let Err(err) = self.inner.meter_provider.shutdown() {
tracing::warn!(target: "surrealdb::observe", "meter provider shutdown failed: {err}");
}
if let Some(provider) = self.inner.audit_logger_provider.as_ref()
&& let Err(err) = provider.shutdown()
{
tracing::warn!(target: "surrealdb::observe", "audit log provider shutdown failed: {err}");
}
if let Some(provider) = self.inner.tracer_provider.as_ref()
&& let Err(err) = provider.shutdown()
{
tracing::warn!(target: "surrealdb::observe", "tracer provider shutdown failed: {err}");
}
}
}
#[must_use]
pub struct ObservabilityRuntimeBuilder {
resource: Option<Resource>,
meter_provider: SdkMeterProvider,
prometheus_exporter: Option<PrometheusExporter>,
audit_logger_provider: Option<SdkLoggerProvider>,
tracer_provider: Option<SdkTracerProvider>,
}
impl ObservabilityRuntimeBuilder {
pub fn with_resource(mut self, resource: Resource) -> Self {
self.resource = Some(resource);
self
}
pub fn with_prometheus_exporter(mut self, exporter: PrometheusExporter) -> Self {
self.prometheus_exporter = Some(exporter);
self
}
pub fn with_audit_logger_provider(mut self, provider: SdkLoggerProvider) -> Self {
self.audit_logger_provider = Some(provider);
self
}
pub fn with_tracer_provider(mut self, provider: SdkTracerProvider) -> Self {
self.tracer_provider = Some(provider);
self
}
pub fn build(self) -> ObservabilityRuntime {
ObservabilityRuntime {
inner: Arc::new(RuntimeInner {
resource: self.resource.unwrap_or_else(|| Resource::builder_empty().build()),
meter_provider: self.meter_provider,
prometheus_exporter: self.prometheus_exporter,
audit_logger_provider: self.audit_logger_provider,
tracer_provider: self.tracer_provider,
}),
}
}
}