function_sdk_rust/
logging.rs1use tracing_subscriber::filter::{LevelFilter, ParseError, Targets};
4use tracing_subscriber::layer::SubscriberExt;
5use tracing_subscriber::util::SubscriberInitExt;
6
7const TRANSPORT_TARGETS: [&str; 6] = ["h2", "hyper", "hyper_util", "tonic", "tower", "rustls"];
11
12pub fn configure(debug: bool) {
22 Builder::new(debug).init();
23}
24
25#[derive(Debug)]
36pub struct Builder {
37 debug: bool,
38 quiet: Vec<String>,
39}
40
41impl Builder {
42 pub fn new(debug: bool) -> Self {
44 Self {
45 debug,
46 quiet: Vec::new(),
47 }
48 }
49
50 pub fn quiet<I, S>(mut self, targets: I) -> Self
55 where
56 I: IntoIterator<Item = S>,
57 S: Into<String>,
58 {
59 self.quiet.extend(targets.into_iter().map(Into::into));
60 self
61 }
62
63 pub fn init(self) {
66 let rust_log = std::env::var("RUST_LOG").ok();
67 let (filter, ignored) = self.filter(rust_log.as_deref());
68 let builder = tracing_subscriber::fmt()
70 .with_max_level(LevelFilter::TRACE)
71 .with_writer(std::io::stderr)
72 .with_file(true)
73 .with_line_number(true);
74 if self.debug {
75 builder.finish().with(filter).init();
76 } else {
77 builder.json().finish().with(filter).init();
78 }
79 if let Some(error) = ignored {
80 tracing::warn!(%error, "ignoring RUST_LOG, which takes only target=level directives");
81 }
82 }
83
84 fn filter(&self, rust_log: Option<&str>) -> (Targets, Option<ParseError>) {
89 let ignored = match rust_log.map(str::trim).filter(|s| !s.is_empty()) {
90 None => None,
91 Some(set) => match set.parse() {
92 Ok(filter) => return (filter, None),
93 Err(error) => Some(error),
94 },
95 };
96 if !self.debug {
97 return (Targets::new().with_default(LevelFilter::INFO), ignored);
98 }
99 let filter = TRANSPORT_TARGETS
100 .iter()
101 .copied()
102 .chain(self.quiet.iter().map(String::as_str))
103 .fold(
104 Targets::new().with_default(LevelFilter::DEBUG),
105 |filter, target| filter.with_target(target, LevelFilter::INFO),
106 );
107 (filter, ignored)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use tracing::Level;
115
116 fn enabled(builder: &Builder, rust_log: Option<&str>, target: &str, level: Level) -> bool {
117 builder.filter(rust_log).0.would_enable(target, &level)
118 }
119
120 #[test]
121 fn info_by_default() {
122 let builder = Builder::new(false);
123 for rust_log in [None, Some(" ")] {
124 assert!(enabled(&builder, rust_log, "my_function", Level::INFO));
125 assert!(!enabled(&builder, rust_log, "my_function", Level::DEBUG));
126 }
127 }
128
129 #[test]
130 fn debug_leaves_the_transport_at_info() {
131 let builder = Builder::new(true);
132 assert!(enabled(&builder, None, "my_function", Level::DEBUG));
133 for target in TRANSPORT_TARGETS {
134 let module = format!("{target}::proto");
135 assert!(!enabled(&builder, None, &module, Level::DEBUG), "{target}");
136 assert!(enabled(&builder, None, &module, Level::INFO), "{target}");
137 }
138 }
139
140 #[test]
141 fn quiet_targets_join_the_transport_under_debug() {
142 let builder = Builder::new(true).quiet(["wasmtime", "cranelift_codegen"]);
143 assert!(!enabled(&builder, None, "wasmtime::module", Level::DEBUG));
144 assert!(!enabled(&builder, None, "cranelift_codegen", Level::DEBUG));
145 assert!(enabled(&builder, None, "my_function", Level::DEBUG));
146 let builder = Builder::new(false).quiet(["wasmtime"]);
147 assert!(!enabled(&builder, None, "my_function", Level::DEBUG));
148 }
149
150 #[test]
151 fn rust_log_replaces_the_flag_and_the_quiet_targets() {
152 let builder = Builder::new(true).quiet(["wasmtime"]);
153 let rust_log = Some("info,my_function=trace,wasmtime=debug");
154 assert!(enabled(&builder, rust_log, "my_function", Level::TRACE));
155 assert!(enabled(&builder, rust_log, "wasmtime", Level::DEBUG));
156 assert!(!enabled(&builder, rust_log, "h2", Level::DEBUG));
157 assert!(builder.filter(rust_log).1.is_none());
158 }
159
160 #[test]
161 fn an_unsupported_rust_log_falls_back_to_the_flag() {
162 let builder = Builder::new(false);
163 let rust_log = Some("my_function[run{tag=x}]=debug");
164 let (filter, ignored) = builder.filter(rust_log);
165 assert!(ignored.is_some());
166 assert!(filter.would_enable("my_function", &Level::INFO));
167 assert!(!filter.would_enable("my_function", &Level::DEBUG));
168 }
169}