Config and logging
Settings come from .env and the environment, and real environment variables always win, so production sets values in the environment and development keeps them in .env. Logs follow the familiar PSR-3 levels.
Reading settings#
use rustclamp::config::Config;
let config = Config::load(); // .env, then the environment on top
let name = config.get("APP_NAME"); // Option<&str>
let per_minute: u32 = config.get_or("API_PER_MINUTE", 60);
let mail_from = config.require("MAIL_FROM"); // panics at startup, naming the key
.envformat:KEY=valuelines, optionalexport,#comments, and quotes around a value are removed. Loading never changes the process environment.- Fail at startup, not at 3 a.m.:
get_orpanics when a key is set but does not parse, andrequirewhen it is missing. Both name the key. - No leaks:
DebugonConfigprints keys only, never values.
Typed settings#
The web template reads everything once, into a struct the rest of the app uses:
pub struct Settings {
pub api_per_minute: u32,
pub trust_proxy: bool,
}
impl Settings {
pub fn from(config: &Config) -> Self {
Self {
api_per_minute: config.get_or("API_PER_MINUTE", 60),
trust_proxy: config.get_or("TRUST_PROXY", false),
}
}
}
.env.example lists every key, and .env itself stays out of git.
Logging#
use rustclamp::log::Log;
Log::info("server started");
Log::warning(format_args!("slow request: {} ms", 850));
| Setting | Values |
|---|---|
LOG_LEVEL | debug (default), info, notice, warning, error, critical, alert, emergency, or silent |
LOG_CHANNEL | file (default) appends to storage/logs/app.log; stderr for systemd and containers |
APP_ENV | The environment name in each line, local by default |
Each entry is one line, [2026-09-29 14:03:07] local.INFO: message, in UTC, with line breaks escaped so an entry never spans lines. A failed write is ignored: logging never takes the app down. For a separate file, or in tests, build a Logger with Logger::file or Logger::stderr.