Next Generation WASM Microkernel Operating System
at main 280 lines 8.7 kB view raw
1// Copyright 2025 Jonas Kruckenberg 2// 3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5// http://opensource.org/licenses/MIT>, at your option. This file may not be 6// copied, modified, or distributed except according to those terms. 7 8use alloc::format; 9use alloc::string::String; 10use alloc::vec::Vec; 11use core::cmp::Ordering; 12use core::fmt; 13use core::fmt::Formatter; 14use core::str::FromStr; 15 16use fallible_iterator::{FallibleIterator, IteratorExt}; 17use smallvec::SmallVec; 18use tracing::level_filters::STATIC_MAX_LEVEL; 19use tracing_core::metadata::ParseLevelFilterError; 20use tracing_core::{Level, LevelFilter, Metadata}; 21 22#[derive(Debug)] 23pub enum Error { 24 UnexpectedEof, 25 TooManyEqualSigns, 26 InvalidLevelFilter, 27} 28 29impl From<ParseLevelFilterError> for Error { 30 fn from(_: ParseLevelFilterError) -> Self { 31 Self::InvalidLevelFilter 32 } 33} 34 35impl fmt::Display for Error { 36 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 37 match self { 38 Error::UnexpectedEof => writeln!(f, "input must not be empty"), 39 Error::TooManyEqualSigns => { 40 writeln!(f, "too many '=' in filter directive, expected 0 or 1") 41 } 42 // Error::TooManyFieldListBegins => { 43 // writeln!(f, "too many '[{{' in filter directive, expected 0 or 1") 44 // } 45 // Error::MissingFieldListEnd => writeln!(f, "expected fields list to end with '}}]'"), 46 Error::InvalidLevelFilter => writeln!(f, "encountered invalid level filter str"), 47 } 48 } 49} 50 51impl core::error::Error for Error {} 52 53pub struct Filter { 54 directives: SmallVec<[Directive; 8]>, 55 max_level: LevelFilter, 56} 57 58impl Default for Filter { 59 fn default() -> Self { 60 Self { 61 directives: SmallVec::new(), 62 max_level: LevelFilter::DEBUG, 63 } 64 } 65} 66 67impl FromStr for Filter { 68 type Err = Error; 69 70 fn from_str(s: &str) -> Result<Self, Self::Err> { 71 if s.is_empty() { 72 return Ok(Self::default()); 73 } 74 75 let iter = s 76 .split(',') 77 .filter(|s| !s.is_empty()) 78 .map(Directive::from_str) 79 .transpose_into_fallible(); 80 81 Self::from_directives(iter) 82 } 83} 84 85impl Filter { 86 fn from_directives( 87 mut directives: impl FallibleIterator<Item = Directive, Error = Error>, 88 ) -> Result<Self, Error> { 89 let mut disabled = Vec::new(); 90 let mut enabled = SmallVec::new(); 91 let mut max_level = LevelFilter::OFF; 92 93 while let Some(directive) = directives.next()? { 94 if directive.level > STATIC_MAX_LEVEL { 95 disabled.push(directive); 96 } else { 97 if directive.level > max_level { 98 max_level = directive.level; 99 } 100 101 // insert the directive into the vec of directives, ordered by 102 // specificity (length of target + number of field filters). this 103 // ensures that, when finding a directive to match a span or event, we 104 // search the directive set in most specific first order. 105 match enabled.binary_search(&directive) { 106 Ok(i) => enabled[i] = directive, 107 Err(i) => enabled.insert(i, directive), 108 } 109 } 110 } 111 112 if !disabled.is_empty() { 113 tracing::warn!( 114 "some trace filter directives would enable traces that are disabled statically" 115 ); 116 for directive in disabled { 117 let target = if let Some(target) = &directive.target { 118 format!("the `{target}` target") 119 } else { 120 "all targets".into() 121 }; 122 let level = directive 123 .level 124 .into_level() 125 .expect("=off would not have enabled any filters"); 126 127 tracing::warn!("`{directive:?}` would enable the {level} level for {target}"); 128 } 129 130 tracing::warn!("the static max level is `{STATIC_MAX_LEVEL}`"); 131 132 let help_msg = || { 133 let (feature, filter) = match STATIC_MAX_LEVEL.into_level() { 134 Some(Level::TRACE) => unreachable!( 135 "if the max level is trace, no static filtering features are enabled" 136 ), 137 Some(Level::DEBUG) => ("max_level_debug", Level::TRACE), 138 Some(Level::INFO) => ("max_level_info", Level::DEBUG), 139 Some(Level::WARN) => ("max_level_warn", Level::INFO), 140 Some(Level::ERROR) => ("max_level_error", Level::WARN), 141 None => return ("max_level_off", String::new()), 142 }; 143 (feature, format!("{filter} ")) 144 }; 145 let (feature, earlier_level) = help_msg(); 146 tracing::warn!( 147 "to enable {earlier_level}logging, remove the `{feature}` feature from the `tracing` crate" 148 ); 149 } 150 151 tracing::debug!("{enabled:?} {max_level:?}"); 152 153 Ok(Self { 154 directives: enabled, 155 max_level, 156 }) 157 } 158 159 pub fn max_level(&self) -> LevelFilter { 160 self.max_level 161 } 162 163 pub(super) fn enabled(&self, meta: &Metadata<'_>) -> bool { 164 let level = *meta.level(); 165 if self.max_level < level { 166 return false; 167 }; 168 169 match self.directives_for(meta).next() { 170 Some(d) => d.level >= level, 171 None => true, 172 } 173 } 174 175 fn directives_for<'a>( 176 &self, 177 meta: &'a Metadata<'a>, 178 ) -> impl Iterator<Item = &Directive> + use<'_, 'a> { 179 self.directives.iter().filter(|d| d.cares_about(meta)) 180 } 181} 182 183#[derive(Debug, PartialEq, Eq)] 184struct Directive { 185 level: LevelFilter, 186 target: Option<String>, 187} 188 189impl Directive { 190 fn cares_about(&self, meta: &Metadata<'_>) -> bool { 191 // Does this directive have a target filter, and does it match the 192 // metadata's target? 193 if let Some(ref target) = self.target 194 && !meta.target().starts_with(&target[..]) 195 { 196 return false; 197 } 198 199 true 200 } 201} 202 203impl FromStr for Directive { 204 type Err = Error; 205 206 fn from_str(s: &str) -> Result<Self, Self::Err> { 207 // This method parses a filtering directive in one of the following 208 // forms: 209 // 210 // * `foo=trace` (TARGET=LEVEL) 211 // * `trace` (bare LEVEL) 212 // * `foo` (bare TARGET) 213 let mut split = s.split('='); 214 let part0 = split.next().ok_or(Error::UnexpectedEof)?; 215 216 // Directive includes an `=`: 217 // * `foo=trace` 218 if let Some(part1) = split.next() { 219 if split.next().is_some() { 220 return Err(Error::TooManyEqualSigns); 221 } 222 223 let mut split = part0.split("[{"); 224 let target = split.next().map(String::from); 225 226 let level = part1.parse()?; 227 return Ok(Self { level, target }); 228 } 229 230 // Okay, the part after the `=` was empty, the directive is either a 231 // bare level or a bare target. 232 // * `foo` 233 // * `info` 234 Ok(match part0.parse::<LevelFilter>() { 235 Ok(level) => Self { 236 level, 237 target: None, 238 }, 239 Err(_) => Self { 240 target: Some(String::from(part0)), 241 level: LevelFilter::TRACE, 242 }, 243 }) 244 } 245} 246 247impl PartialOrd<Self> for Directive { 248 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 249 Some(self.cmp(other)) 250 } 251} 252 253impl Ord for Directive { 254 fn cmp(&self, other: &Self) -> Ordering { 255 // We attempt to order directives by how "specific" they are. This 256 // ensures that we try the most specific directives first when 257 // attempting to match a piece of metadata. 258 259 // First, we compare based on whether a target is specified, and the 260 // lengths of those targets if both have targets. 261 let ordering = self 262 .target 263 .as_ref() 264 .map(String::len) 265 .cmp(&other.target.as_ref().map(String::len)) 266 .reverse(); 267 268 #[cfg(debug_assertions)] 269 { 270 if ordering == Ordering::Equal { 271 debug_assert_eq!( 272 self.target, other.target, 273 "invariant violated: Ordering::Equal must imply a.target == b.target" 274 ); 275 } 276 } 277 278 ordering 279 } 280}