Replace ESP_LOGLEVEL and ESP_LOGFILTERby ESP_LOG (#2291)

* Replace `ESP_LOGLEVEL` and `ESP_LOGFILTER`by `ESP_LOG`

* CHANGELOG.md

* Clippy

* Fail build if using the now unsupported env variables
This commit is contained in:
Björn Quentin 2024-10-08 10:53:42 +02:00 committed by GitHub
parent 038d07f5b6
commit ca5e8560bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 175 additions and 26 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
### Changed
- Replace environment variables `ESP_LOGLEVEL` and `ESP_LOGFILTER` with just one environment variable: `ESP_LOG` (#2291)
### Fixed

View File

@ -21,6 +21,7 @@ portable-atomic = { version = "1.7.0", optional = true, default-features = fal
[build-dependencies]
esp-build = { version = "0.1.0", path = "../esp-build" }
log = "0.4.20"
[features]
default = ["critical-section", "colors", "auto"]

View File

@ -76,8 +76,7 @@ init_logger_from_env();
In this case the following environment variables are used:
- `ESP_LOGLEVEL` sets the log level, use values like `trace`, `info` etc.
- `ESP_LOGTARGETS` if set you should provide the crate names of crates (optionally with a path e.g. `esp_wifi::compat::common`) which should get logged, separated by `,` and no additional whitespace between
- `ESP_LOG` log messages you want to show, similar to `RUST_LOG`. RegEx is not supported. e.g. `warn,test::foo=info,test::foo::bar=debug`
If this simple logger implementation isn't sufficient for your needs, you can implement your own logger on top of `esp-println`. See [Implementing a Logger section log documentaion]

View File

@ -1,3 +1,5 @@
use std::{env, path::Path};
use esp_build::assert_unique_used_features;
fn main() {
@ -29,4 +31,157 @@ fn main() {
"cargo:warning=The `colors` feature is only effective when using the `log` feature"
);
}
if std::env::var("ESP_LOGLEVEL").is_ok() || std::env::var("ESP_LOGFILTER").is_ok() {
panic!("`ESP_LOGLEVEL` and `ESP_LOGFILTER` is not supported anymore. Please use `ESP_LOG` instead.");
}
generate_filter_snippet();
#[cfg(target_os = "windows")]
println!("cargo:rustc-cfg=host_is_windows");
println!("cargo:rerun-if-env-changed=ESP_LOG");
println!("cargo:rustc-check-cfg=cfg(host_is_windows)");
}
fn generate_filter_snippet() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("log_filter.rs");
let filter = env::var("ESP_LOG");
let snippet = if let Ok(filter) = filter {
let res = parse_spec(&filter);
if !res.errors.is_empty() {
panic!("Error parsing `ESP_LOG`: {:?}", res.errors);
} else {
let max = res
.directives
.iter()
.map(|v| v.level)
.max()
.unwrap_or(log::LevelFilter::Off);
let max = match max {
log::LevelFilter::Off => "Off",
log::LevelFilter::Error => "Error",
log::LevelFilter::Warn => "Warn",
log::LevelFilter::Info => "Info",
log::LevelFilter::Debug => "Debug",
log::LevelFilter::Trace => "Trace",
};
let mut snippet = String::new();
snippet.push_str(&format!(
"pub(crate) const FILTER_MAX: log::LevelFilter = log::LevelFilter::{};",
max
));
snippet
.push_str("pub(crate) fn is_enabled(level: log::Level, _target: &str) -> bool {");
for directive in res.directives {
let level = match directive.level {
log::LevelFilter::Off => "Off",
log::LevelFilter::Error => "Error",
log::LevelFilter::Warn => "Warn",
log::LevelFilter::Info => "Info",
log::LevelFilter::Debug => "Debug",
log::LevelFilter::Trace => "Trace",
};
if let Some(name) = directive.name {
snippet.push_str(&format!(
"if _target.starts_with(\"{}\") && level <= log::LevelFilter::{} {{ return true; }}",
&name, level
));
} else {
snippet.push_str(&format!(
"if level <= log::LevelFilter::{} {{ return true; }}",
level
));
}
}
snippet.push_str(" false");
snippet.push('}');
snippet
}
} else {
"pub(crate) const FILTER_MAX: log::LevelFilter = log::LevelFilter::Off; pub(crate) fn is_enabled(_level: log::Level, _target: &str) -> bool { true }".to_string()
};
std::fs::write(&dest_path, &snippet).unwrap();
}
#[derive(Default, Debug)]
struct ParseResult {
pub(crate) directives: Vec<Directive>,
pub(crate) errors: Vec<String>,
}
impl ParseResult {
fn add_directive(&mut self, directive: Directive) {
self.directives.push(directive);
}
fn add_error(&mut self, message: String) {
self.errors.push(message);
}
}
#[derive(Debug)]
struct Directive {
pub(crate) name: Option<String>,
pub(crate) level: log::LevelFilter,
}
/// Parse a logging specification string (e.g:
/// `crate1,crate2::mod3,crate3::x=error/foo`) and return a vector with log
/// directives.
fn parse_spec(spec: &str) -> ParseResult {
let mut result = ParseResult::default();
let mut parts = spec.split('/');
let mods = parts.next();
if let Some(m) = mods {
for s in m.split(',').map(|ss| ss.trim()) {
if s.is_empty() {
continue;
}
let mut parts = s.split('=');
let (log_level, name) =
match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
(Some(part0), None, None) => {
// if the single argument is a log-level string or number,
// treat that as a global fallback
match part0.parse() {
Ok(num) => (num, None),
Err(_) => (log::LevelFilter::max(), Some(part0)),
}
}
(Some(part0), Some(""), None) => (log::LevelFilter::max(), Some(part0)),
(Some(part0), Some(part1), None) => {
if let Ok(num) = part1.parse() {
(num, Some(part0))
} else {
result.add_error(format!("invalid logging spec '{part1}'"));
continue;
}
}
_ => {
result.add_error(format!("invalid logging spec '{s}'"));
continue;
}
};
result.add_directive(Directive {
name: name.map(|s| s.to_owned()),
level: log_level,
});
}
}
result
}

View File

@ -1,11 +1,14 @@
use core::str::FromStr;
use log::LevelFilter;
use super::println;
const LOG_TARGETS: Option<&'static str> = option_env!("ESP_LOGTARGETS");
#[cfg(not(host_is_windows))]
include!(concat!(env!("OUT_DIR"), "/log_filter.rs"));
#[cfg(host_is_windows)]
include!(concat!(env!("OUT_DIR"), "\\log_filter.rs"));
/// Initialize the logger with the given maximum log level.
///
/// `ESP_LOG` environment variable will still be honored if set.
pub fn init_logger(level: log::LevelFilter) {
unsafe {
log::set_logger_racy(&EspLogger).unwrap();
@ -13,37 +16,27 @@ pub fn init_logger(level: log::LevelFilter) {
}
}
/// Initialize the logger from the `ESP_LOG` environment variable.
pub fn init_logger_from_env() {
unsafe {
log::set_logger_racy(&EspLogger).unwrap();
}
const LEVEL: Option<&'static str> = option_env!("ESP_LOGLEVEL");
if let Some(lvl) = LEVEL {
let level = LevelFilter::from_str(lvl).unwrap_or_else(|_| LevelFilter::Off);
unsafe { log::set_max_level_racy(level) };
log::set_max_level_racy(FILTER_MAX);
}
}
struct EspLogger;
impl log::Log for EspLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
fn enabled(&self, metadata: &log::Metadata) -> bool {
let level = metadata.level();
let target = metadata.target();
is_enabled(level, target)
}
#[allow(unused)]
fn log(&self, record: &log::Record) {
// check enabled log targets if any
if let Some(targets) = LOG_TARGETS {
if targets
.split(",")
.find(|v| record.target().starts_with(v))
.is_none()
{
return;
}
if !self.enabled(&record.metadata()) {
return;
}
const RESET: &str = "\u{001B}[0m";

View File

@ -27,7 +27,7 @@ rustflags = [
]
[env]
ESP_LOGLEVEL = "info"
ESP_LOG = "info"
SSID = "SSID"
PASSWORD = "PASSWORD"
STATIC_IP = "1.1.1.1 "