1mod resolve;
7
8#[cfg(feature = "telemetry-datadog")]
9mod datadog;
10#[cfg(feature = "telemetry-dev")]
11mod dev;
12#[cfg(feature = "telemetry-otlp")]
13mod otlp;
14#[cfg(feature = "telemetry-posthog")]
15mod posthog;
16
17use crate::core::config::{ExporterConfig, TelemetryConfig};
18use crate::sync::IngestExportBatch;
19use anyhow::Result;
20use std::sync::Arc;
21
22pub use resolve::DatadogResolved;
23pub use resolve::OtlpResolved;
24pub use resolve::PostHogResolved;
25
26pub trait TelemetryExporter: Send + Sync {
28 fn name(&self) -> &str;
29 fn export(&self, batch: &IngestExportBatch) -> Result<()>;
30}
31
32pub struct ExporterRegistry {
34 exporters: Vec<Arc<dyn TelemetryExporter>>,
35}
36
37impl ExporterRegistry {
38 pub fn empty() -> Self {
39 Self {
40 exporters: Vec::new(),
41 }
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.exporters.is_empty()
46 }
47
48 pub fn from_vec(exporters: Vec<Arc<dyn TelemetryExporter>>) -> Self {
49 Self { exporters }
50 }
51
52 pub fn fan_out(&self, fail_open: bool, batch: &IngestExportBatch) -> Result<()> {
54 for e in &self.exporters {
55 let r = e.export(batch);
56 if let Err(err) = r {
57 tracing::warn!(exporter = e.name(), error = %err, "telemetry exporter");
58 if !fail_open {
59 return Err(err);
60 }
61 }
62 }
63 Ok(())
64 }
65}
66
67pub fn load_exporters(cfg: &TelemetryConfig) -> ExporterRegistry {
69 let mut v: Vec<Arc<dyn TelemetryExporter>> = Vec::new();
70 for entry in &cfg.exporters {
71 if let Some(exp) = build_exporter(entry) {
72 v.push(exp);
73 }
74 }
75 ExporterRegistry::from_vec(v)
76}
77
78fn build_exporter(c: &ExporterConfig) -> Option<Arc<dyn TelemetryExporter>> {
79 if !c.is_enabled() {
80 return None;
81 }
82 match c {
83 ExporterConfig::None => None,
84 ExporterConfig::Dev { .. } => {
85 #[cfg(feature = "telemetry-dev")]
86 {
87 Some(Arc::new(dev::DevExporter) as _)
88 }
89 #[cfg(not(feature = "telemetry-dev"))]
90 {
91 tracing::warn!(
92 "telemetry `dev` exporter configured but `telemetry-dev` is not enabled"
93 );
94 None
95 }
96 }
97 ExporterConfig::PostHog { .. } => {
98 let r = PostHogResolved::from_config(c)?;
99 #[cfg(feature = "telemetry-posthog")]
100 {
101 Some(Arc::new(posthog::PostHogExporter::new(&r.host, &r.project_api_key)) as _)
102 }
103 #[cfg(not(feature = "telemetry-posthog"))]
104 {
105 let _ = &r;
106 tracing::warn!(
107 "PostHog configured but the `telemetry-posthog` feature is not enabled"
108 );
109 None
110 }
111 }
112 ExporterConfig::Datadog { .. } => {
113 let r = DatadogResolved::from_config(c)?;
114 #[cfg(feature = "telemetry-datadog")]
115 {
116 Some(Arc::new(datadog::DatadogExporter::new(&r.site, &r.api_key)) as _)
117 }
118 #[cfg(not(feature = "telemetry-datadog"))]
119 {
120 let _ = &r;
121 tracing::warn!(
122 "Datadog configured but the `telemetry-datadog` feature is not enabled"
123 );
124 None
125 }
126 }
127 ExporterConfig::Otlp { .. } => {
128 let r = OtlpResolved::from_config(c)?;
129 #[cfg(feature = "telemetry-otlp")]
130 {
131 Some(Arc::new(otlp::OtlpExporter::new(&r.endpoint)) as _)
132 }
133 #[cfg(not(feature = "telemetry-otlp"))]
134 {
135 let _ = &r;
136 tracing::warn!("OTLP configured but the `telemetry-otlp` feature is not enabled");
137 None
138 }
139 }
140 }
141}