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