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