0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-04 09:57:11 -05:00
deno/cli/lsp/trace.rs
Nathan Whitaker 56f67b5851
refactor: put lsp tracing behind flag (#28118)
To reduce binary size impact

---------

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
2025-02-14 17:17:52 +00:00

231 lines
6.2 KiB
Rust

// Copyright 2018-2025 the Deno authors. MIT license.
use std::fmt;
#[cfg(feature = "lsp-tracing")]
pub use real_tracing::*;
use serde::Deserialize;
use serde::Serialize;
#[cfg(not(feature = "lsp-tracing"))]
pub use stub_tracing::*;
pub(crate) struct TracingGuard {
#[allow(dead_code)]
guard: (),
// TODO(nathanwhit): use default guard here so we can change tracing after init
// but needs wiring through the subscriber to the TSC thread, as it can't be a global default
// #[allow(dead_code)] tracing::dispatcher::DefaultGuard,
#[allow(dead_code)]
defused: bool,
}
impl fmt::Debug for TracingGuard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("TracingGuard").finish()
}
}
#[cfg(feature = "lsp-tracing")]
mod real_tracing {
use deno_core::anyhow;
use opentelemetry::trace::TracerProvider;
pub use opentelemetry::Context;
use opentelemetry::KeyValue;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::Resource;
use opentelemetry_semantic_conventions::resource::SERVICE_NAME;
use tracing::level_filters::LevelFilter;
pub use tracing::span::EnteredSpan;
pub use tracing::Span;
use tracing_opentelemetry::OpenTelemetryLayer;
pub use tracing_opentelemetry::OpenTelemetrySpanExt as SpanExt;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use super::TracingCollector;
use super::TracingConfig;
use super::TracingGuard;
pub(crate) fn make_tracer(
endpoint: Option<&str>,
) -> Result<opentelemetry_sdk::trace::Tracer, anyhow::Error> {
let endpoint = endpoint.unwrap_or("http://localhost:4317");
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint(endpoint)
.build()?;
let provider = opentelemetry_sdk::trace::Builder::default()
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.with_resource(Resource::new(vec![KeyValue::new(
SERVICE_NAME,
"deno-lsp",
)]))
.build();
opentelemetry::global::set_tracer_provider(provider.clone());
Ok(provider.tracer("deno-lsp-tracer"))
}
pub(crate) fn init_tracing_subscriber(
config: &TracingConfig,
) -> Result<TracingGuard, anyhow::Error> {
if !config.enable {
return Err(anyhow::anyhow!("Tracing is not enabled"));
}
let filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into());
let filter = if let Some(directive) = config.filter.as_ref() {
filter.parse(directive)?
} else {
filter.with_env_var("DENO_LSP_TRACE").from_env()?
};
let open_telemetry_layer = match config.collector {
TracingCollector::OpenTelemetry => Some(OpenTelemetryLayer::new(
make_tracer(config.collector_endpoint.as_deref())?,
)),
_ => None,
};
let logging_layer = match config.collector {
TracingCollector::Logging => Some(
tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
// Include span events in the log output.
// Without this, only events get logged (and at the moment we have none).
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE),
),
_ => None,
};
tracing::subscriber::set_global_default(
tracing_subscriber::registry()
.with(filter)
.with(logging_layer)
.with(open_telemetry_layer),
)
.unwrap();
let guard = ();
Ok(TracingGuard {
guard,
defused: false,
})
}
impl Drop for TracingGuard {
fn drop(&mut self) {
if !self.defused {
crate::lsp::logging::lsp_debug!("Shutting down tracing");
tokio::task::spawn_blocking(|| {
opentelemetry::global::shutdown_tracer_provider()
});
}
}
}
}
#[cfg(not(feature = "lsp-tracing"))]
mod stub_tracing {
pub trait SpanExt {
#[allow(dead_code)]
fn set_parent(&self, _context: Context);
fn context(&self) -> Context;
}
#[derive(Debug, Clone)]
pub struct Span {}
impl SpanExt for Span {
#[allow(dead_code)]
fn set_parent(&self, _context: Context) {}
fn context(&self) -> Context {
Context {}
}
}
impl Span {
pub fn entered(self) -> EnteredSpan {
EnteredSpan {}
}
pub fn current() -> Self {
Self {}
}
}
#[derive(Debug)]
pub struct EnteredSpan {}
#[derive(Clone, Debug)]
pub struct Context {}
pub(crate) fn init_tracing_subscriber(
_config: &super::TracingConfig,
) -> Result<super::TracingGuard, deno_core::anyhow::Error> {
Ok(super::TracingGuard {
defused: false,
guard: {},
})
}
}
#[derive(
Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Copy, Default,
)]
#[serde(rename_all = "camelCase")]
pub(crate) enum TracingCollector {
#[default]
OpenTelemetry,
Logging,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
#[serde(default, rename_all = "camelCase")]
pub(crate) struct TracingConfig {
/// Enable tracing.
pub(crate) enable: bool,
/// The collector to use. Defaults to `OpenTelemetry`.
/// If `Logging` is used, the collected traces will be written to stderr.
pub(crate) collector: TracingCollector,
/// The filter to use. Defaults to `INFO`.
pub(crate) filter: Option<String>,
/// The endpoint to use for the OpenTelemetry collector.
pub(crate) collector_endpoint: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub(crate) enum TracingConfigOrEnabled {
Config(TracingConfig),
Enabled(bool),
}
impl From<TracingConfig> for TracingConfigOrEnabled {
fn from(value: TracingConfig) -> Self {
TracingConfigOrEnabled::Config(value)
}
}
impl From<TracingConfigOrEnabled> for TracingConfig {
fn from(value: TracingConfigOrEnabled) -> Self {
match value {
TracingConfigOrEnabled::Config(config) => config,
TracingConfigOrEnabled::Enabled(enabled) => TracingConfig {
enable: enabled,
..Default::default()
},
}
}
}
impl TracingConfigOrEnabled {
pub(crate) fn enabled(&self) -> bool {
match self {
TracingConfigOrEnabled::Config(config) => config.enable,
TracingConfigOrEnabled::Enabled(enabled) => *enabled,
}
}
}