opentelemetry_jaeger/exporter/config/
mod.rs

1//! Configurations to build a jaeger exporter.
2//!
3//! The jaeger exporter can send spans to [jaeger agent] or [jaeger collector]. The agent is usually
4//! deployed along with the application like a sidecar. The collector is usually deployed a stand alone
5//! application and receive spans from multiple sources. The exporter will use UDP to send spans to
6//! agents and use HTTP/TCP to send spans to collectors. See [jaeger deployment guide] for more details.
7//!
8//! [jaeger agent]: https://www.jaegertracing.io/docs/1.31/deployment/#agent
9//! [jaeger collector]: https://www.jaegertracing.io/docs/1.31/deployment/#collector
10//! [jaeger deployment guide]: https://www.jaegertracing.io/docs/1.31/deployment
11
12use crate::Process;
13use opentelemetry::{global, trace::TraceError, KeyValue};
14use opentelemetry_sdk::trace::{BatchConfig, Config, Tracer, TracerProvider};
15use opentelemetry_semantic_conventions as semcov;
16
17/// Config a exporter that sends the spans to a [jaeger agent](https://www.jaegertracing.io/docs/1.31/deployment/#agent).
18pub mod agent;
19/// Config a exporter that bypass the agent and send spans directly to [jaeger collector](https://www.jaegertracing.io/docs/1.31/deployment/#collector).
20#[cfg(any(feature = "collector_client", feature = "wasm_collector_client"))]
21pub mod collector;
22
23// configurations and overrides on how to transform OTLP spans to Jaeger spans.
24#[derive(Debug)]
25struct TransformationConfig {
26    export_instrument_library: bool,
27    service_name: Option<String>,
28}
29
30impl Default for TransformationConfig {
31    fn default() -> Self {
32        TransformationConfig {
33            export_instrument_library: true,
34            service_name: None,
35        }
36    }
37}
38
39// pipeline must have transformation config, trace config and batch config.
40trait HasRequiredConfig {
41    fn set_transformation_config<T>(&mut self, f: T)
42    where
43        T: FnOnce(&mut TransformationConfig);
44
45    fn set_trace_config(&mut self, config: Config);
46
47    fn set_batch_config(&mut self, config: BatchConfig);
48}
49
50// To reduce the overhead of copying service name in every spans. We convert resource into jaeger tags
51// and store them into process. And set the resource in trace config to empty.
52//
53// There are multiple ways to set the service name. A `service.name` tag will be always added
54// to the process tags.
55fn build_config_and_process(
56    config: Option<Config>,
57    service_name_opt: Option<String>,
58) -> (Config, Process) {
59    let config = config.unwrap_or_default();
60
61    let service_name = service_name_opt.unwrap_or_else(|| {
62        config
63            .resource
64            .get(semcov::resource::SERVICE_NAME.into())
65            .map(|v| v.to_string())
66            .unwrap_or_else(|| "unknown_service".to_string())
67    });
68
69    // merge the tags and resource. Resources take priority.
70    let mut tags = config
71        .resource
72        .iter()
73        .filter(|(key, _)| key.as_str() != semcov::resource::SERVICE_NAME)
74        .map(|(key, value)| KeyValue::new(key.clone(), value.clone()))
75        .collect::<Vec<KeyValue>>();
76
77    tags.push(KeyValue::new(
78        semcov::resource::SERVICE_NAME,
79        service_name.clone(),
80    ));
81
82    (config, Process { service_name, tags })
83}
84
85pub(crate) fn install_tracer_provider_and_get_tracer(
86    tracer_provider: TracerProvider,
87) -> Result<Tracer, TraceError> {
88    let tracer = opentelemetry::trace::TracerProvider::tracer_builder(
89        &tracer_provider,
90        "opentelemetry-jaeger",
91    )
92    .with_version(env!("CARGO_PKG_VERSION"))
93    .with_schema_url(semcov::SCHEMA_URL)
94    .build();
95    let _ = global::set_tracer_provider(tracer_provider);
96    Ok(tracer)
97}
98
99#[cfg(test)]
100mod tests {
101    use crate::exporter::config::build_config_and_process;
102    use crate::new_agent_pipeline;
103    use opentelemetry::KeyValue;
104    use opentelemetry_sdk::{trace::Config, Resource};
105    use std::env;
106
107    #[test]
108    fn test_set_service_name() {
109        let service_name = "halloween_service".to_string();
110
111        // set via builder's service name, it has highest priority
112        let (_, process) = build_config_and_process(None, Some(service_name.clone()));
113        assert_eq!(process.service_name, service_name);
114
115        // make sure the tags in resource are moved to process
116        let trace_config = Config::default()
117            .with_resource(Resource::new(vec![KeyValue::new("test-key", "test-value")]));
118        let (_, process) = build_config_and_process(Some(trace_config), Some(service_name));
119        assert_eq!(process.tags.len(), 2);
120    }
121
122    #[tokio::test]
123    async fn test_read_from_env() {
124        // OTEL_SERVICE_NAME env var also works
125        env::set_var("OTEL_SERVICE_NAME", "test service");
126        let builder = new_agent_pipeline();
127        let exporter = builder.build_sync_agent_exporter().unwrap();
128        assert_eq!(exporter.process.service_name, "test service");
129        env::set_var("OTEL_SERVICE_NAME", "")
130    }
131}