mod batch_metadata;
mod file;
mod resolve;
#[cfg(feature = "telemetry-datadog")]
mod datadog;
#[cfg(feature = "telemetry-dev")]
mod dev;
#[cfg(feature = "telemetry-otlp")]
mod otlp;
#[cfg(feature = "telemetry-posthog")]
mod posthog;
use crate::core::config::{ExporterConfig, TelemetryConfig};
use crate::sync::IngestExportBatch;
use anyhow::Result;
use std::path::Path;
use std::sync::Arc;
pub use batch_metadata::telemetry_file_line;
pub use file::{FileExporter, default_ndjson_path, resolve_file_exporter_path};
pub use resolve::DatadogResolved;
pub use resolve::OtlpResolved;
pub use resolve::PostHogResolved;
pub trait TelemetryExporter: Send + Sync {
fn name(&self) -> &str;
fn export(&self, batch: &IngestExportBatch) -> Result<()>;
}
pub struct ExporterRegistry {
exporters: Vec<Arc<dyn TelemetryExporter>>,
}
impl ExporterRegistry {
pub fn empty() -> Self {
Self {
exporters: Vec::new(),
}
}
pub fn is_empty(&self) -> bool {
self.exporters.is_empty()
}
pub fn from_vec(exporters: Vec<Arc<dyn TelemetryExporter>>) -> Self {
Self { exporters }
}
pub fn fan_out(&self, fail_open: bool, batch: &IngestExportBatch) -> Result<()> {
for e in &self.exporters {
let r = e.export(batch);
if let Err(err) = r {
tracing::warn!(exporter = e.name(), error = %err, "telemetry exporter");
if !fail_open {
return Err(err);
}
}
}
Ok(())
}
}
pub fn load_exporters(cfg: &TelemetryConfig, workspace: &Path) -> ExporterRegistry {
let mut v: Vec<Arc<dyn TelemetryExporter>> = Vec::new();
for entry in &cfg.exporters {
if let Some(exp) = build_exporter(entry, workspace) {
v.push(exp);
}
}
ExporterRegistry::from_vec(v)
}
fn build_exporter(c: &ExporterConfig, workspace: &Path) -> Option<Arc<dyn TelemetryExporter>> {
if !c.is_enabled() {
return None;
}
match c {
ExporterConfig::None => None,
ExporterConfig::File { path, .. } => {
let p = file::resolve_file_exporter_path(path.as_deref(), workspace);
Some(Arc::new(file::FileExporter::new(p)) as _)
}
ExporterConfig::Dev { .. } => {
#[cfg(feature = "telemetry-dev")]
{
Some(Arc::new(dev::DevExporter) as _)
}
#[cfg(not(feature = "telemetry-dev"))]
{
tracing::warn!(
"telemetry `dev` exporter configured but `telemetry-dev` is not enabled"
);
None
}
}
ExporterConfig::PostHog { .. } => {
let r = PostHogResolved::from_config(c)?;
#[cfg(feature = "telemetry-posthog")]
{
Some(Arc::new(posthog::PostHogExporter::new(&r.host, &r.project_api_key)) as _)
}
#[cfg(not(feature = "telemetry-posthog"))]
{
let _ = &r;
tracing::warn!(
"PostHog configured but the `telemetry-posthog` feature is not enabled"
);
None
}
}
ExporterConfig::Datadog { .. } => {
let r = DatadogResolved::from_config(c)?;
#[cfg(feature = "telemetry-datadog")]
{
Some(Arc::new(datadog::DatadogExporter::new(&r.site, &r.api_key)) as _)
}
#[cfg(not(feature = "telemetry-datadog"))]
{
let _ = &r;
tracing::warn!(
"Datadog configured but the `telemetry-datadog` feature is not enabled"
);
None
}
}
ExporterConfig::Otlp { .. } => {
let r = OtlpResolved::from_config(c)?;
#[cfg(feature = "telemetry-otlp")]
{
Some(Arc::new(otlp::OtlpExporter::new(&r.endpoint)) as _)
}
#[cfg(not(feature = "telemetry-otlp"))]
{
let _ = &r;
tracing::warn!("OTLP configured but the `telemetry-otlp` feature is not enabled");
None
}
}
}
}