1use std::io::IsTerminal;
4use std::sync::Once;
5
6use tokio_util::sync::CancellationToken;
7use tracing_subscriber::prelude::*;
8use tracing_subscriber::EnvFilter;
9
10static TRACING_INIT: Once = Once::new();
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum LogFormat {
17 Pretty,
19 Json,
21}
22
23impl LogFormat {
24 #[must_use]
26 pub fn auto() -> Self {
27 if std::io::stderr().is_terminal() {
28 Self::Pretty
29 } else {
30 Self::Json
31 }
32 }
33}
34
35pub fn install_tracing(format: LogFormat) {
38 TRACING_INIT.call_once(|| {
39 let env_filter =
40 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
41
42 let registry = tracing_subscriber::registry().with(env_filter);
43
44 match format {
45 LogFormat::Pretty => {
46 let layer = tracing_subscriber::fmt::layer()
47 .with_target(false)
48 .compact()
49 .with_writer(std::io::stderr);
50 let _ = registry.with(layer).try_init();
51 }
52 LogFormat::Json => {
53 let layer = tracing_subscriber::fmt::layer()
54 .json()
55 .with_target(true)
56 .with_writer(std::io::stderr);
57 let _ = registry.with(layer).try_init();
58 }
59 }
60 });
61}
62
63pub fn bind_shutdown_signals(token: CancellationToken) {
67 tokio::spawn(async move {
68 let ctrl_c = async {
69 if let Err(e) = tokio::signal::ctrl_c().await {
70 tracing::warn!(error = %e, "failed to install Ctrl-C handler");
71 }
72 };
73
74 #[cfg(unix)]
75 {
76 use tokio::signal::unix::{signal, SignalKind};
77 let mut term = match signal(SignalKind::terminate()) {
78 Ok(s) => s,
79 Err(e) => {
80 tracing::warn!(error = %e, "failed to install SIGTERM handler");
81 ctrl_c.await;
82 token.cancel();
83 return;
84 }
85 };
86 tokio::select! {
87 () = ctrl_c => tracing::info!("received Ctrl-C — shutting down"),
88 _ = term.recv() => tracing::info!("received SIGTERM — shutting down"),
89 }
90 }
91
92 #[cfg(not(unix))]
93 {
94 ctrl_c.await;
95 tracing::info!("received Ctrl-C — shutting down");
96 }
97
98 token.cancel();
99 });
100}