commonware_runtime/tokio/
telemetry.rs1use super::{
4 tracing::{export, Config},
5 Context,
6};
7use crate::{Metrics as _, Spawner, Supervisor as _};
8use axum::{
9 body::Body,
10 http::{header, Response, StatusCode},
11 routing::get,
12 serve, Extension, Router,
13};
14use cfg_if::cfg_if;
15use std::{net::SocketAddr, sync::Arc};
16use tokio::net::TcpListener;
17use tracing::Level;
18use tracing_subscriber::{filter::filter_fn, layer::SubscriberExt, Layer, Registry};
19
20pub struct Logs {
22 pub level: Level,
24
25 pub json: bool,
31}
32
33pub fn init(context: Context, logs: Logs, metrics: Option<SocketAddr>, traces: Option<Config>) {
40 let log_layer = tracing_subscriber::fmt::layer()
42 .with_line_number(true)
43 .with_thread_ids(true)
44 .with_file(true);
45
46 let log_layer = if logs.json {
48 log_layer.json().boxed()
49 } else {
50 log_layer.compact().boxed()
51 };
52 let log_layer = match &traces {
53 None => log_layer
54 .with_filter(filter_fn(move |metadata| {
55 metadata.is_event() && *metadata.level() <= logs.level
56 }))
57 .boxed(),
58 Some(_) => log_layer
59 .with_filter(tracing_subscriber::EnvFilter::new(logs.level.to_string()))
60 .boxed(),
61 };
62
63 let trace_layer = traces.map(|cfg| {
65 let tracer = export(cfg).expect("Failed to initialize tracer");
66 tracing_opentelemetry::layer()
67 .with_tracer(tracer)
68 .with_filter(tracing_subscriber::EnvFilter::new(logs.level.to_string()))
69 });
70
71 cfg_if! {
73 if #[cfg(feature = "tokio-console")] {
74 let console_layer = console_subscriber::spawn();
75 let registry = Registry::default()
76 .with(log_layer)
77 .with(trace_layer)
78 .with(console_layer);
79 } else {
80 let registry = Registry::default().with(log_layer).with(trace_layer);
81 }
82 }
83
84 tracing::subscriber::set_global_default(registry).expect("Failed to set subscriber");
86
87 if let Some(cfg) = metrics {
89 context.child("metrics").spawn(move |context| async move {
90 let listener = TcpListener::bind(cfg)
96 .await
97 .expect("Failed to bind metrics server");
98
99 let shared = Arc::new(context);
101 let app = Router::new()
102 .route(
103 "/metrics",
104 get(|Extension(ctx): Extension<Arc<Context>>| async move {
105 Response::builder()
106 .status(StatusCode::OK)
107 .header(header::CONTENT_TYPE, "text/plain; version=0.0.4")
108 .body(Body::from(ctx.encode()))
109 .expect("Failed to create response")
110 }),
111 )
112 .layer(Extension(shared));
113
114 serve(listener, app.into_make_service())
119 .await
120 .expect("Could not serve metrics");
121 });
122 }
123}