use crate::error::{GlideError, Result};
use glide_core::{
DEFAULT_TRACE_SAMPLE_PERCENTAGE, GlideOpenTelemetry, GlideOpenTelemetryConfigBuilder,
GlideOpenTelemetrySignalsExporter,
};
use std::path::PathBuf;
use std::time::Duration;
pub const DEFAULT_TRACE_SAMPLE_PERCENT: u32 = DEFAULT_TRACE_SAMPLE_PERCENTAGE;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TelemetryExporter {
Grpc(String),
Http(String),
File(PathBuf),
}
impl TelemetryExporter {
pub fn grpc(endpoint: impl Into<String>) -> Self {
TelemetryExporter::Grpc(endpoint.into())
}
pub fn http(endpoint: impl Into<String>) -> Self {
TelemetryExporter::Http(endpoint.into())
}
pub fn file(path: impl Into<PathBuf>) -> Self {
TelemetryExporter::File(path.into())
}
}
impl From<TelemetryExporter> for GlideOpenTelemetrySignalsExporter {
fn from(e: TelemetryExporter) -> Self {
match e {
TelemetryExporter::Grpc(s) => GlideOpenTelemetrySignalsExporter::Grpc(s),
TelemetryExporter::Http(s) => GlideOpenTelemetrySignalsExporter::Http(s),
TelemetryExporter::File(p) => GlideOpenTelemetrySignalsExporter::File(p),
}
}
}
#[derive(Clone)]
pub struct OpenTelemetryConfig {
inner: GlideOpenTelemetryConfigBuilder,
}
impl OpenTelemetryConfig {
pub fn builder() -> OpenTelemetryConfigBuilder {
OpenTelemetryConfigBuilder {
inner: GlideOpenTelemetryConfigBuilder::default(),
}
}
}
#[derive(Clone)]
pub struct OpenTelemetryConfigBuilder {
inner: GlideOpenTelemetryConfigBuilder,
}
impl OpenTelemetryConfigBuilder {
#[must_use]
pub fn with_flush_interval(mut self, interval: Duration) -> Self {
self.inner = self.inner.with_flush_interval(interval);
self
}
#[must_use]
pub fn with_trace_exporter(
mut self,
exporter: TelemetryExporter,
sample_percentage: Option<u32>,
) -> Self {
self.inner = self
.inner
.with_trace_exporter(exporter.into(), sample_percentage);
self
}
#[must_use]
pub fn with_metrics_exporter(mut self, exporter: TelemetryExporter) -> Self {
self.inner = self.inner.with_metrics_exporter(exporter.into());
self
}
pub fn build(self) -> OpenTelemetryConfig {
OpenTelemetryConfig { inner: self.inner }
}
}
pub fn init(config: OpenTelemetryConfig) -> Result<()> {
GlideOpenTelemetry::initialise(config.inner.build())
.map_err(|e| GlideError::Configuration(format!("OpenTelemetry init failed: {e}")))
}
pub fn is_initialized() -> bool {
GlideOpenTelemetry::is_initialized()
}
pub fn shutdown() {
GlideOpenTelemetry::shutdown();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exporter_constructors_and_mapping() {
assert_eq!(
TelemetryExporter::grpc("http://h:4317"),
TelemetryExporter::Grpc("http://h:4317".into())
);
assert_eq!(
TelemetryExporter::http("http://h:4318"),
TelemetryExporter::Http("http://h:4318".into())
);
assert_eq!(
TelemetryExporter::file("/tmp/sig"),
TelemetryExporter::File(PathBuf::from("/tmp/sig"))
);
let core: GlideOpenTelemetrySignalsExporter = TelemetryExporter::grpc("g").into();
assert!(matches!(core, GlideOpenTelemetrySignalsExporter::Grpc(s) if s == "g"));
}
#[test]
fn default_sample_percent_matches_core() {
assert_eq!(
DEFAULT_TRACE_SAMPLE_PERCENT,
DEFAULT_TRACE_SAMPLE_PERCENTAGE
);
}
#[tokio::test]
async fn init_rejects_zero_flush_interval() {
let config = OpenTelemetryConfig::builder()
.with_flush_interval(Duration::from_millis(0))
.with_trace_exporter(TelemetryExporter::file("/tmp/otel-zero"), Some(1))
.build();
let err = init(config).expect_err("zero flush interval must be rejected");
assert!(matches!(err, GlideError::Configuration(_)));
}
#[tokio::test]
async fn init_rejects_sample_percentage_over_100() {
let config = OpenTelemetryConfig::builder()
.with_flush_interval(Duration::from_millis(100))
.with_trace_exporter(TelemetryExporter::file("/tmp/otel-bad-sample"), Some(101))
.build();
let err = init(config).expect_err("sample percentage > 100 must be rejected");
assert!(matches!(err, GlideError::Configuration(_)));
}
}