stellation_backend_cli/
trace.rs1use std::env;
4
5use console::style;
6use stellation_core::dev::StctlMetadata;
7use tracing::field::Visit;
8use tracing::{Level, Subscriber};
9use tracing_subscriber::filter::filter_fn;
10use tracing_subscriber::layer::Context;
11use tracing_subscriber::prelude::*;
12use tracing_subscriber::registry::LookupSpan;
13use tracing_subscriber::{EnvFilter, Layer};
14
15#[derive(Debug, Default)]
17pub struct AccessLog {}
18
19pub fn pretty_access() -> AccessLog {
21 AccessLog {}
22}
23
24impl<S> Layer<S> for AccessLog
25where
26 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
27{
28 fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
29 if event.metadata().target() != "stellation_backend::endpoint::trace" {
30 return;
31 }
32
33 #[derive(Default, Debug)]
34 struct Values {
35 duration: Option<u128>,
36 path: Option<String>,
37 method: Option<String>,
38 status: Option<u64>,
39 }
40
41 impl Visit for Values {
42 fn record_u128(&mut self, field: &tracing::field::Field, value: u128) {
43 if field.as_ref() == "duration" {
44 self.duration = Some(value);
45 }
46 }
47
48 fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
49 if field.as_ref() == "path" {
50 self.path = Some(value.to_string());
51 }
52 }
53
54 fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
55 if field.as_ref() == "status" {
56 self.status = Some(value);
57 }
58 }
59
60 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
61 if field.as_ref() == "method" {
62 self.method = Some(format!("{value:?}"));
63 }
64 }
65 }
66
67 let mut values = Values::default();
68 event.record(&mut values);
69
70 if let (Some(path), Some(duration), Some(status), Some(method)) =
71 (values.path, values.duration, values.status, values.method)
72 {
73 let duration = Some(duration)
74 .and_then(|m| i32::try_from(m).ok())
75 .map(f64::from)
76 .expect("duration took too long")
77 / 100_000.0;
78
79 let status = match status {
80 m if m < 200 => style(m).cyan(),
81 m if m < 300 => style(m).green(),
82 m if m < 400 => style(m).yellow(),
83 m => style(m).red(),
84 }
85 .bold();
86
87 eprintln!("{method:>6} {status} {duration:>8.2}ms {path}");
88 }
89 }
90}
91
92pub fn init_default<S>(var_name: S)
94where
95 S: Into<String>,
96{
97 let var_name = var_name.into();
98 let env_filter = EnvFilter::builder()
99 .with_default_directive(Level::INFO.into())
100 .with_env_var(var_name)
101 .from_env_lossy();
102
103 match env::var(StctlMetadata::ENV_NAME) {
104 Ok(_) => {
105 tracing_subscriber::registry()
107 .with(pretty_access())
108 .with(
109 tracing_subscriber::fmt::layer()
110 .compact()
111 .with_filter(filter_fn(|metadata| {
113 metadata.target() != "stellation_backend::endpoint::trace"
114 })),
115 )
116 .with(env_filter)
117 .init();
118 }
119 Err(_) => {
120 tracing_subscriber::registry()
121 .with(tracing_subscriber::fmt::layer().compact())
122 .with(env_filter)
123 .init();
124 }
125 }
126}