use std::collections::HashMap;
use opentelemetry::KeyValue;
use opentelemetry_otlp::Protocol;
use opentelemetry_resource_detectors::{OsResourceDetector, ProcessResourceDetector};
use opentelemetry_sdk::{
resource::{EnvResourceDetector, SdkProvidedResourceDetector, TelemetryResourceDetector},
Resource,
};
use opentelemetry_semantic_conventions::resource as res;
use serde::{Deserialize, Serialize};
use crate::config::AppConfig;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct TelemetryConfig {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
labels: HashMap<String, String>,
#[serde(default)]
parse_labels: bool,
}
impl TelemetryConfig {
#[must_use]
pub fn with_label<T, U>(&mut self, key: T, value: U) -> &mut Self
where
T: ToString,
U: ToString,
{
self.labels.insert(key.to_string(), value.to_string());
self
}
#[must_use]
pub fn with_labels<'a, T, U, V>(&mut self, kvs: V) -> &mut Self
where
T: ToString + 'a,
U: ToString + 'a,
V: IntoIterator<Item = (&'a T, &'a U)>,
{
self.labels.extend(
kvs.into_iter()
.map(|(key, val)| (key.to_string(), val.to_string())),
);
self
}
pub fn static_resources(&self) -> impl Iterator<Item = KeyValue> + '_ {
self.labels
.iter()
.map(|(key, val)| KeyValue::new(key.clone(), val.clone()))
}
}
impl AppConfig {
#[must_use]
pub fn otel_resource(&self) -> Resource {
let mut static_resources: Vec<_> = self.telemetry.static_resources().collect();
if let Some(val) = &self.app_name {
static_resources.push(KeyValue::new(res::SERVICE_NAME, val.clone()));
}
if let Some(val) = &self.app_version {
static_resources.push(KeyValue::new(res::SERVICE_VERSION, val.clone()));
}
Resource::builder()
.with_detectors(&[
Box::new(OsResourceDetector),
Box::new(ProcessResourceDetector),
Box::new(SdkProvidedResourceDetector),
Box::new(EnvResourceDetector::new()),
Box::new(TelemetryResourceDetector),
])
.with_attributes(static_resources)
.build()
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
pub enum OtlpProtocol {
#[default]
Grpc,
HttpBinary,
HttpJson,
}
impl From<OtlpProtocol> for Protocol {
fn from(value: OtlpProtocol) -> Self {
match value {
OtlpProtocol::Grpc => Self::Grpc,
OtlpProtocol::HttpBinary => Self::HttpBinary,
OtlpProtocol::HttpJson => Self::HttpJson,
}
}
}