diff --git a/Cargo.lock b/Cargo.lock index ec7bdaa45..58294ba99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3362,6 +3362,7 @@ dependencies = [ name = "gem_tracing" version = "1.0.0" dependencies = [ + "log", "tracing", "tracing-subscriber", ] diff --git a/apps/api/src/devices/guard.rs b/apps/api/src/devices/guard.rs index a9ba4e591..b93deab96 100644 --- a/apps/api/src/devices/guard.rs +++ b/apps/api/src/devices/guard.rs @@ -1,3 +1,4 @@ +use gem_tracing::info_with_fields; use rocket::Request; use rocket::http::Status; use rocket::outcome::Outcome::{Error, Success}; @@ -31,6 +32,9 @@ fn auth_error_outcome(req: &Request<'_>, error: DeviceError, device_id: Optio Some(id) => format!("{} device_id={}", error, id), None => error.to_string(), }; + let uri = req.uri().to_string(); + let user_agent = req.headers().get_one("User-Agent").unwrap_or("unknown"); + info_with_fields!("Request guard failed", uri = uri, status = status.code, error = message, user_agent = user_agent); cache_error(req, &message); Error((status, message)) } diff --git a/apps/api/src/main.rs b/apps/api/src/main.rs index ad5e6345d..b950ea8d5 100644 --- a/apps/api/src/main.rs +++ b/apps/api/src/main.rs @@ -320,6 +320,8 @@ async fn main() { println!("api start service: {}", service.as_ref()); + gem_tracing::init(); + let rocket = match service { APIService::Api => rocket_api(settings).await, APIService::WebsocketPrices => rocket_ws_prices(settings).await, diff --git a/crates/tracing/Cargo.toml b/crates/tracing/Cargo.toml index a3a792299..e85c84b84 100644 --- a/crates/tracing/Cargo.toml +++ b/crates/tracing/Cargo.toml @@ -6,3 +6,4 @@ edition = { workspace = true } [dependencies] tracing = { version = "0.1.44" } tracing-subscriber = { version = "0.3.22" } +log = { version = "0.4" } diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 054d99278..be0d27f7f 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -8,6 +8,46 @@ pub fn get_subscriber() -> Arc { TRACING_SUBSCRIBER.get_or_init(|| Arc::new(tracing_subscriber::fmt().with_target(false).finish())).clone() } +struct GemLogger; + +impl log::Log for GemLogger { + fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &log::Record<'_>) { + if !self.enabled(record.metadata()) { + return; + } + let subscriber = get_subscriber(); + let message = record.args().to_string(); + tracing::subscriber::with_default(subscriber, || match record.level() { + log::Level::Error => tracing::error!("{}", message), + log::Level::Warn => tracing::warn!("{}", message), + log::Level::Info => tracing::info!("{}", message), + log::Level::Debug => tracing::debug!("{}", message), + log::Level::Trace => tracing::trace!("{}", message), + }); + } + + fn flush(&self) {} +} + +/// Initializes gem_tracing as the global `log` backend. +/// Log level is read from the `ROCKET_LOG_LEVEL` environment variable +/// (values: `off`, `critical`, `normal`, `debug`; default: `normal`). +/// If a global logger is already registered the call is a no-op. +pub fn init() { + let level_filter = match std::env::var("ROCKET_LOG_LEVEL").as_deref() { + Ok("off") => log::LevelFilter::Off, + Ok("critical") => log::LevelFilter::Warn, + Ok("debug") => log::LevelFilter::Trace, + _ => log::LevelFilter::Info, + }; + let _ = log::set_boxed_logger(Box::new(GemLogger)); + log::set_max_level(level_filter); +} + pub fn human_duration(duration: Duration) -> String { if duration.is_zero() { return "0s".to_string();