Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 98 additions & 1 deletion crates/trusted-server-adapter-axum/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
use edgezero_adapter_axum::dev_server::{AxumDevServer, AxumDevServerConfig};
use edgezero_core::app::Hooks as _;
use log::LevelFilter;
use trusted_server_adapter_axum::app::TrustedServerApp;

/// Environment variable that sets the maximum log level.
const LOG_LEVEL_ENV: &str = "RUST_LOG";
Comment thread
dhruv8sh marked this conversation as resolved.

/// Log level used when `RUST_LOG` is unset or unrecognized.
const DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::Info;

#[allow(clippy::print_stderr)]
fn main() {
if let Err(e) = simple_logger::SimpleLogger::new().init() {
// `var_os` keeps a set but non-UTF-8 value visible, so it is rejected
// with a warning instead of silently falling back.
let configured_level =
std::env::var_os(LOG_LEVEL_ENV).map(|value| value.to_string_lossy().into_owned());
let level = parse_log_level(configured_level.as_deref()).unwrap_or_else(|value| {
eprintln!(
"warning: {LOG_LEVEL_ENV}={value:?} is not a single log level; using {}",
DEFAULT_LOG_LEVEL.as_str().to_ascii_lowercase()
);
DEFAULT_LOG_LEVEL
});
if let Err(e) = simple_logger::SimpleLogger::new().with_level(level).init() {
eprintln!("warning: logger init failed: {e}");
}

Expand All @@ -27,6 +45,26 @@ fn main() {
}
}

/// Parses an optional `RUST_LOG` value into the logger's maximum level.
///
/// Absent and blank values resolve to [`DEFAULT_LOG_LEVEL`]. Any other value
/// must be a single level name (`trace`, `debug`, `info`, `warn`, `error`, or
/// `off`), matched case-insensitively after trimming whitespace. The quiet
/// default keeps trace-level payload and consent-string logging out of
/// standard output unless an operator explicitly asks for it.
///
/// # Errors
///
/// Returns the trimmed value when it is not a single level name, such as the
/// per-module filter `trusted_server=trace`, so the caller can warn before
/// falling back to [`DEFAULT_LOG_LEVEL`].
fn parse_log_level(configured: Option<&str>) -> Result<LevelFilter, &str> {
match configured.map(str::trim) {
None | Some("") => Ok(DEFAULT_LOG_LEVEL),
Some(trimmed) => trimmed.parse().map_err(|_| trimmed),
}
}

/// Read a port number from the `PORT` environment variable.
///
/// Returns `None` when the variable is unset. Exits non-zero if the value
Expand All @@ -46,6 +84,65 @@ fn port_from_env() -> Option<u16> {

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn crate_compiles() {}

#[test]
fn parse_log_level_defaults_to_info_when_unset_or_blank() {
assert_eq!(
parse_log_level(None),
Ok(LevelFilter::Info),
"should default to info when RUST_LOG is unset"
);
assert_eq!(
parse_log_level(Some("")),
Ok(LevelFilter::Info),
"should treat an empty value as unset"
);
assert_eq!(
parse_log_level(Some(" ")),
Ok(LevelFilter::Info),
"should treat a whitespace-only value as unset"
);
}

#[test]
fn parse_log_level_honors_explicit_levels() {
assert_eq!(
parse_log_level(Some("info")),
Ok(LevelFilter::Info),
"should suppress trace when RUST_LOG is info"
);
assert_eq!(
parse_log_level(Some("trace")),
Ok(LevelFilter::Trace),
"should enable trace when RUST_LOG is trace"
);
assert_eq!(
parse_log_level(Some(" DEBUG ")),
Ok(LevelFilter::Debug),
"should accept case-insensitive levels with surrounding whitespace"
);
assert_eq!(
parse_log_level(Some("off")),
Ok(LevelFilter::Off),
"should allow disabling logging"
);
}

#[test]
fn parse_log_level_rejects_unrecognized_values() {
assert_eq!(
parse_log_level(Some("trace,hyper=info")),
Err("trace,hyper=info"),
"should reject module-filter syntax it does not support"
);
assert_eq!(
parse_log_level(Some(" verbose ")),
Err("verbose"),
"should reject an unknown level name and return it trimmed"
);
}
}
15 changes: 15 additions & 0 deletions docs/guide/axum-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ rewrite its origin URL to the Axum listener, and remove the original URL. The
exit trap stops the Axum process and stub origin, then removes the temporary
config, local-push output, bodies, headers, and logs.

## Logging

Axum logs to standard output at `info` by default. Set `RUST_LOG` to a single
level (`trace`, `debug`, `info`, `warn`, `error`, or `off`) to change it, for
example `RUST_LOG=debug cargo run -p trusted-server-adapter-axum`.
Comment thread
dhruv8sh marked this conversation as resolved.
Unrecognized values, including per-module filters such as
`trusted_server=debug`, fall back to `info` and print a warning to standard
error at startup.

The `trace` level can log auction payloads and TCF consent strings, for example
the `adserver_mock` mediation request, and the full Prebid Server response
(including its `ext.debug` echo of the resolved request) when a provider sets
`profile_config.debug = true`. Enable it only for short local debugging
sessions.

## Runtime boundaries

Axum implements the read-only `/_ts/admin/eids` diagnostic. EC record lookup
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Show captured output and serialize a focused test when ordering or shared test
state matters:

```bash
RUST_LOG=debug cargo test-axum test_name -- --nocapture --test-threads=1
cargo test-axum test_name -- --nocapture --test-threads=1
cargo test-fastly test_name -- --nocapture --test-threads=1
```

Expand Down
Loading