1mod log_tree;23use anyhow::Result;4use clap::Parser;5#[cfg(feature = "indicatif")]6use human_repr::HumanCount;7#[cfg(feature = "indicatif")]8use indicatif::{ProgressState, ProgressStyle};9use log_tree::TreeLayer;10use opentelemetry::trace::TracerProvider;11use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;12use opentelemetry_exporter_env::{13 OtlpBaseSettings, OtlpLogsSettings, OtlpTracesSettings, ResolvedOtlpSettings,14};15use opentelemetry_sdk::{logs::SdkLoggerProvider, trace::SdkTracerProvider};16#[cfg(feature = "indicatif")]17use tracing_indicatif::IndicatifLayer;18use tracing_subscriber::layer::SubscriberExt as _;19use tracing_subscriber::util::SubscriberInitExt as _;20use tracing_subscriber::{EnvFilter, Layer as _};2122#[derive(Parser)]23#[clap(next_help_heading = "Telemetry")]24pub struct LogOpts {25 26 #[clap(long, env = "OTEL_FLEET")]27 otel: bool,28 #[clap(flatten)]29 otlp_base: OtlpBaseSettings,30 #[clap(flatten)]31 otel_logs: OtlpLogsSettings,32 #[clap(flatten)]33 otel_traces: OtlpTracesSettings,34}3536pub fn setup_logging(opts: &LogOpts) -> Result<()> {37 #[cfg(feature = "indicatif")]38 let indicatif_layer = {39 use std::fmt;40 use std::time::Duration;4142 IndicatifLayer::new().with_max_progress_bars(10, Some(ProgressStyle::default_spinner()))43 .with_span_child_prefix_indent(" ")44 .with_span_child_prefix_symbol("")45 .with_progress_style(46 ProgressStyle::with_template(47 "{span_child_prefix:.magenta}{chevron:.magenta}{span_name:<18.blue.bold} {wide_msg} {span_fields:.dim} {color_start}{download_progress} {elapsed}{color_end}",48 )49 .unwrap()50 .with_key("chevron", |_: &ProgressState, writer: &mut dyn fmt::Write| {51 let _ = write!(writer, "> ");52 })53 .with_key("download_progress", |state: &ProgressState, writer: &mut dyn fmt::Write| {54 let Some(len) = state.len() else {55 return;56 };57 let pos = state.pos();58 if pos > len {59 let _ = write!(writer, "{}", pos.human_count_bare());60 } else {61 let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());62 }63 })64 .with_key(65 "color_start",66 |state: &ProgressState, writer: &mut dyn fmt::Write| {67 let elapsed = state.elapsed();6869 if elapsed > Duration::from_secs(60) {70 71 let _ = write!(writer, "\x1b[{}m", 1 + 30);72 } else if elapsed > Duration::from_secs(30) {73 74 let _ = write!(writer, "\x1b[{}m", 3 + 30);75 }76 },77 )78 .with_key(79 "color_end",80 |state: &ProgressState, writer: &mut dyn fmt::Write| {81 if state.elapsed() > Duration::from_secs(30) {82 let _ = write!(writer, "\x1b[0m");83 }84 },85 ),86 )87 };8889 90 let filter =91 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("noq_udp=warn,info"));9293 let tree = {94 #[cfg(feature = "indicatif")]95 let writer = indicatif_layer.get_stderr_writer();96 #[cfg(not(feature = "indicatif"))]97 let writer = || std::io::stderr();98 TreeLayer::new(writer)99 };100101 let reg = tracing_subscriber::registry();102103 #[cfg(feature = "tokio-console")]104 let reg = reg.with(console_subscriber::spawn());105106 let reg = reg.with(tree.with_filter(filter));107108 #[cfg(feature = "indicatif")]109 let reg = reg.with(indicatif_layer);110111 if opts.otel {112 let traces = ResolvedOtlpSettings::traces(&opts.otlp_base, &opts.otel_traces)?;113 let span_exporter = traces.span_exporter()?;114 let logs = ResolvedOtlpSettings::logs(&opts.otlp_base, &opts.otel_logs)?;115 let log_exporter = logs.log_exporter()?;116117 let span_provider = SdkTracerProvider::builder()118 .with_batch_exporter(span_exporter)119 .build();120 let log_provider = SdkLoggerProvider::builder()121 .with_batch_exporter(log_exporter)122 .build();123124 let logger = OpenTelemetryTracingBridge::new(&log_provider);125 let tracer = span_provider.tracer("fleet");126127 reg.with(tracing_opentelemetry::layer().with_tracer(tracer))128 .with(logger)129 .init();130 } else {131 reg.init();132 }133134 Ok(())135}