use std::borrow::BorrowMut;
use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::{env, net};
use opentelemetry::trace::TraceError;
use opentelemetry_sdk::trace::{BatchConfig, Config, TracerProvider};
use opentelemetry_sdk::trace::{BatchSpanProcessor, Tracer};
use crate::exporter::agent::{AgentAsyncClientUdp, AgentSyncClientUdp};
use crate::exporter::config::{
build_config_and_process, install_tracer_provider_and_get_tracer, HasRequiredConfig,
TransformationConfig,
};
use crate::exporter::uploader::{AsyncUploader, SyncUploader, Uploader};
use crate::{Error, Exporter, JaegerTraceRuntime};
const UDP_PACKET_MAX_LENGTH: usize = 65_000;
const ENV_AGENT_HOST: &str = "OTEL_EXPORTER_JAEGER_AGENT_HOST";
const ENV_AGENT_PORT: &str = "OTEL_EXPORTER_JAEGER_AGENT_PORT";
const DEFAULT_AGENT_ENDPOINT_HOST: &str = "127.0.0.1";
const DEFAULT_AGENT_ENDPOINT_PORT: &str = "6831";
#[derive(Debug)]
#[deprecated(
since = "0.21.0",
note = "Please migrate to opentelemetry-otlp exporter."
)]
pub struct AgentPipeline {
transformation_config: TransformationConfig,
trace_config: Option<Config>,
batch_config: Option<BatchConfig>,
agent_endpoint: Option<String>,
max_packet_size: usize,
auto_split_batch: bool,
}
impl Default for AgentPipeline {
fn default() -> Self {
AgentPipeline {
transformation_config: Default::default(),
trace_config: Default::default(),
batch_config: Some(Default::default()),
agent_endpoint: Some(format!(
"{DEFAULT_AGENT_ENDPOINT_HOST}:{DEFAULT_AGENT_ENDPOINT_PORT}"
)),
max_packet_size: UDP_PACKET_MAX_LENGTH,
auto_split_batch: false,
}
}
}
impl HasRequiredConfig for AgentPipeline {
fn set_transformation_config<T>(&mut self, f: T)
where
T: FnOnce(&mut TransformationConfig),
{
f(self.transformation_config.borrow_mut())
}
fn set_trace_config(&mut self, config: Config) {
self.trace_config = Some(config)
}
fn set_batch_config(&mut self, config: BatchConfig) {
self.batch_config = Some(config)
}
}
#[deprecated(
since = "0.21.0",
note = "Please migrate to opentelemetry-otlp exporter."
)]
pub fn new_agent_pipeline() -> AgentPipeline {
AgentPipeline::default()
}
impl AgentPipeline {
pub fn with_endpoint<T: Into<String>>(self, agent_endpoint: T) -> Self {
AgentPipeline {
agent_endpoint: Some(agent_endpoint.into()),
..self
}
}
pub fn with_max_packet_size(self, max_packet_size: usize) -> Self {
AgentPipeline {
max_packet_size,
..self
}
}
pub fn with_auto_split_batch(mut self, should_auto_split: bool) -> Self {
self.auto_split_batch = should_auto_split;
self
}
pub fn with_service_name<T: Into<String>>(mut self, service_name: T) -> Self {
self.set_transformation_config(|config| {
config.service_name = Some(service_name.into());
});
self
}
pub fn with_instrumentation_library_tags(mut self, should_export: bool) -> Self {
self.set_transformation_config(|config| {
config.export_instrument_library = should_export;
});
self
}
pub fn with_trace_config(mut self, config: Config) -> Self {
self.set_trace_config(config);
self
}
pub fn with_batch_processor_config(mut self, config: BatchConfig) -> Self {
self.set_batch_config(config);
self
}
pub fn build_simple(mut self) -> Result<TracerProvider, TraceError> {
let mut builder = TracerProvider::builder();
let (config, process) = build_config_and_process(
self.trace_config.take(),
self.transformation_config.service_name.take(),
);
let exporter = Exporter::new(
process.into(),
self.transformation_config.export_instrument_library,
self.build_sync_agent_uploader()?,
);
builder = builder.with_simple_exporter(exporter);
builder = builder.with_config(config);
Ok(builder.build())
}
pub fn build_batch<R>(mut self, runtime: R) -> Result<TracerProvider, TraceError>
where
R: JaegerTraceRuntime,
{
let mut builder = TracerProvider::builder();
let export_instrument_library = self.transformation_config.export_instrument_library;
let (config, process) = build_config_and_process(
self.trace_config.take(),
self.transformation_config.service_name.take(),
);
let batch_config = self.batch_config.take();
let uploader = self.build_async_agent_uploader(runtime.clone())?;
let exporter = Exporter::new(process.into(), export_instrument_library, uploader);
let batch_processor = BatchSpanProcessor::builder(exporter, runtime)
.with_batch_config(batch_config.unwrap_or_default())
.build();
builder = builder.with_span_processor(batch_processor);
builder = builder.with_config(config);
Ok(builder.build())
}
pub fn install_simple(self) -> Result<Tracer, TraceError> {
let tracer_provider = self.build_simple()?;
install_tracer_provider_and_get_tracer(tracer_provider)
}
pub fn install_batch<R>(self, runtime: R) -> Result<Tracer, TraceError>
where
R: JaegerTraceRuntime,
{
let tracer_provider = self.build_batch(runtime)?;
install_tracer_provider_and_get_tracer(tracer_provider)
}
pub fn build_async_agent_exporter<R>(
mut self,
runtime: R,
) -> Result<crate::Exporter, TraceError>
where
R: JaegerTraceRuntime,
{
let export_instrument_library = self.transformation_config.export_instrument_library;
let (_, process) = build_config_and_process(
self.trace_config.take(),
self.transformation_config.service_name.take(),
);
let uploader = self.build_async_agent_uploader(runtime)?;
Ok(Exporter::new(
process.into(),
export_instrument_library,
uploader,
))
}
pub fn build_sync_agent_exporter(mut self) -> Result<crate::Exporter, TraceError> {
let (_, process) = build_config_and_process(
self.trace_config.take(),
self.transformation_config.service_name.take(),
);
Ok(Exporter::new(
process.into(),
self.transformation_config.export_instrument_library,
self.build_sync_agent_uploader()?,
))
}
fn build_async_agent_uploader<R>(self, runtime: R) -> Result<Arc<dyn Uploader>, TraceError>
where
R: JaegerTraceRuntime,
{
let agent = AgentAsyncClientUdp::new(
self.max_packet_size,
runtime,
self.auto_split_batch,
self.resolve_endpoint()?,
)
.map_err::<Error, _>(Into::into)?;
Ok(Arc::new(AsyncUploader::Agent(
futures_util::lock::Mutex::new(agent),
)))
}
fn build_sync_agent_uploader(self) -> Result<Arc<dyn Uploader>, TraceError> {
let agent = AgentSyncClientUdp::new(
self.max_packet_size,
self.auto_split_batch,
self.resolve_endpoint()?,
)
.map_err::<Error, _>(Into::into)?;
Ok(Arc::new(SyncUploader::Agent(std::sync::Mutex::new(agent))))
}
fn resolve_endpoint(self) -> Result<Vec<net::SocketAddr>, TraceError> {
let endpoint_str = match (env::var(ENV_AGENT_HOST), env::var(ENV_AGENT_PORT)) {
(Ok(host), Ok(port)) => format!("{}:{}", host.trim(), port.trim()),
(Ok(host), _) => format!("{}:{DEFAULT_AGENT_ENDPOINT_PORT}", host.trim()),
(_, Ok(port)) => format!("{DEFAULT_AGENT_ENDPOINT_HOST}:{}", port.trim()),
(_, _) => self.agent_endpoint.unwrap_or(format!(
"{DEFAULT_AGENT_ENDPOINT_HOST}:{DEFAULT_AGENT_ENDPOINT_PORT}"
)),
};
endpoint_str
.to_socket_addrs()
.map(|addrs| addrs.collect())
.map_err(|io_err| {
Error::ConfigError {
pipeline_name: "agent",
config_name: "endpoint",
reason: io_err.to_string(),
}
.into()
})
}
}
#[cfg(test)]
mod tests {
use crate::config::agent::AgentPipeline;
#[test]
fn set_socket_address() {
let test_cases = vec![
("invalid_endpoint", false),
("0.0.0.0.0:9123", false),
("127.0.0.1", false), ("[::0]:9123", true),
("127.0.0.1:1001", true),
];
for (socket_str, is_ok) in test_cases.into_iter() {
let resolved_endpoint = AgentPipeline::default()
.with_endpoint(socket_str)
.resolve_endpoint();
assert_eq!(
resolved_endpoint.is_ok(),
is_ok,
"endpoint string {}",
socket_str
);
}
}
}