Configure logging
Goal: control what Loco logs, in what shape, and where — stdout for development, structured JSON for production, and (optionally) a rotating log file — without drowning in third-party crate noise.
Loco’s logger is built on tracing. logger.enable, logger.level, and logger.format are required keys in every config file.
1. Set the minimum config
logger: enable: true pretty_backtrace: true level: debug format: compactlevel:off|trace|debug|info|warn|error.format:compact|pretty|json.pretty_backtrace: whentrue, forcesRUST_BACKTRACE=1and nicely-formatted panic backtraces. It’s a development convenience — turn it off in performance-sensitive production deployments.
2. Know the filtering precedence
Loco doesn’t just apply level globally to every crate — by default it whitelists a small set of modules (loco_rs, sea_orm_migration, tower_http, sqlx::query, playground, loco_gen) plus your own app crate, and applies level only to those. Everything else stays quiet.
Three ways to control this, in strict precedence order:
RUST_LOGenvironment variable — if set, it wins outright, ignoring bothlevelandoverride_filter. Use this for one-off debugging on a running process without touching config:Terminal window RUST_LOG=debug cargo loco startlogger.override_filter— a rawtracing-subscriberEnvFilterdirective string. Use this to permanently see traces from libraries outside the built-in whitelist:logger:enable: truelevel: infoformat: compactoverride_filter: "trace" # or a directive like "myapp=debug,tower_http=debug"- The built-in module whitelist +
level— what you get if neither of the above is set. This is the common case: setleveland trust Loco’s whitelist to keep noise down.
3. Choose a format per environment
compact— human-readable single-line output; good default for local development.pretty— multi-line, more spacious human-readable output.json— structured, one JSON object per line; use this in production so a log aggregator (Loki, CloudWatch, Datadog, etc.) can parse fields directly.
logger: enable: true pretty_backtrace: false level: info format: json4. Add a rotating file appender
file_appender writes logs to disk independently of (and with its own level/format, separate from) the stdout logger — useful when you want stdout kept quiet but still capture a full trail on disk.
logger: enable: true level: info format: compact file_appender: enable: true non_blocking: false # true offloads writes to a background thread level: debug format: json rotation: daily # minutely | hourly | daily | never — default is hourly dir: ./logs # default "./logs" if omitted filename_prefix: myapp filename_suffix: log max_log_files: 7 # required — old files beyond this count are prunedWith rotation: daily and the settings above, you’ll get files like ./logs/myapp.<date>.log, rotated once a day, with only the newest 7 kept around.
5. Verify
Start the app and confirm the shape you expect shows up:
cargo loco start# ... watch stdout for compact/pretty/json-formatted lines at the level you setIf you enabled a file appender, tail the log directory:
tail -f ./logs/*.logTo confirm the filtering precedence, try overriding at runtime without touching the config file:
RUST_LOG=loco_rs=trace cargo loco startYou should see much more verbose output than logger.level alone would produce — confirming RUST_LOG took precedence.
Reference
- Every
logger:YAML key, including allfile_appendersub-keys: Configuration reference § logger