1use anyhow::bail;
2use opentelemetry::{global, trace::TracerProvider};
3use opentelemetry_otlp::WithHttpConfig;
4use opentelemetry_sdk::{
5 Resource,
6 resource::{EnvResourceDetector, ResourceDetector, TelemetryResourceDetector},
7 runtime::Tokio,
8};
9use tracing::Subscriber;
10use tracing_opentelemetry::OpenTelemetrySpanExt as _;
11use tracing_subscriber::{EnvFilter, Layer, registry::LookupSpan};
12
13use crate::detector::SpinResourceDetector;
14use crate::env::OtlpProtocol;
15
16pub(crate) fn otel_tracing_layer<S: Subscriber + for<'span> LookupSpan<'span>>(
22 spin_version: String,
23) -> anyhow::Result<impl Layer<S>> {
24 let resource = Resource::builder()
25 .with_detectors(&[
26 Box::new(SpinResourceDetector::new(spin_version)) as Box<dyn ResourceDetector>,
29 Box::new(EnvResourceDetector::new()),
31 Box::new(TelemetryResourceDetector),
33 ])
34 .build();
35
36 let exporter = match OtlpProtocol::traces_protocol_from_env() {
38 OtlpProtocol::Grpc => opentelemetry_otlp::SpanExporter::builder()
39 .with_tonic()
40 .build()?,
41 OtlpProtocol::HttpProtobuf => opentelemetry_otlp::SpanExporter::builder()
42 .with_http()
43 .with_http_client(crate::rustls_reqwest_client()?)
44 .build()?,
45 OtlpProtocol::HttpJson => bail!("http/json OTLP protocol is not supported"),
46 };
47
48 let span_processor =
49 opentelemetry_sdk::trace::span_processor_with_async_runtime::BatchSpanProcessor::builder(
50 exporter, Tokio,
51 )
52 .build();
53
54 let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
55 .with_resource(resource)
56 .with_span_processor(span_processor)
57 .build();
58
59 global::set_tracer_provider(tracer_provider.clone());
60
61 let env_filter = match EnvFilter::try_from_env("SPIN_OTEL_TRACING_LEVEL") {
62 Ok(filter) => filter,
63 Err(_) => EnvFilter::new("info"),
65 };
66
67 Ok(tracing_opentelemetry::layer()
68 .with_tracer(tracer_provider.tracer("spin"))
69 .with_threads(false)
70 .with_filter(env_filter))
71}
72
73#[derive(Debug)]
77pub enum Blame {
78 Guest,
80 Host,
82}
83
84impl Blame {
85 fn as_str(&self) -> &'static str {
86 match self {
87 Blame::Guest => "guest",
88 Blame::Host => "host",
89 }
90 }
91}
92
93pub fn mark_as_error<E: std::fmt::Display>(err: &E, blame: Option<Blame>) {
95 let current_span = tracing::Span::current();
96 current_span.set_status(opentelemetry::trace::Status::error(err.to_string()));
97 if let Some(blame) = blame {
98 current_span.set_attribute("error.blame", blame.as_str());
99 }
100}