use crate::observability::config::{LogRemoteConfig, TraceConfig};
use crate::observability::filter::SharedOrderedFilter;
use anyhow::{Context as AnyhowContext, Result};
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::{LogExporter, Protocol, WithExportConfig};
use opentelemetry_sdk::{logs::SdkLoggerProvider, Resource};
use tracing_subscriber::{Layer, Registry};
pub fn build_remote_log_layer(
remote: &LogRemoteConfig,
trace: &TraceConfig,
filter: SharedOrderedFilter,
) -> Result<(Box<dyn Layer<Registry> + Send + Sync>, SdkLoggerProvider)> {
let endpoint = append_otlp_path(&remote.endpoint, "/v1/logs");
let exporter = LogExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary)
.with_endpoint(endpoint)
.build()
.context("failed to create OpenTelemetry log exporter")?;
let provider = SdkLoggerProvider::builder()
.with_batch_exporter(exporter)
.with_resource(resource(trace))
.build();
let layer = OpenTelemetryTracingBridge::new(&provider)
.with_filter(filter)
.boxed();
Ok((layer, provider))
}
pub(crate) fn resource(trace: &TraceConfig) -> Resource {
let mut builder = Resource::builder().with_service_name(trace.service_name.clone());
if let Some(version) = &trace.service_version {
builder = builder.with_attribute(opentelemetry::KeyValue::new(
"service.version",
version.clone(),
));
}
builder.build()
}
pub(crate) fn append_otlp_path(endpoint: &str, path: &str) -> String {
if endpoint.ends_with(path) {
endpoint.to_string()
} else {
format!("{}{}", endpoint.trim_end_matches('/'), path)
}
}