Log
Levelled, structured logging — the default way to answer "what is my game doing?" and "why did that break?", and a real step up from scattering print calls through your code. Each message carries a level, from trace (most verbose) through debug, info, and warn to error, and a runtime threshold set with Log.set_level decides which ones actually appear — so a development build can be chatty and a release build quiet, without touching the call sites.
Messages go to standard error, kept separate from a program's real stdout, tagged with their level. Beyond the message you can pass **structured fields** as trailing key/value pairs — Log.warn("missing texture", "path", p, "id", n) prints [WARN] missing texture path=... id=... — cheap to write and easy to grep. Numeric values (int, long) are formatted for you; the level tag is chosen at compile time, so a filtered-out level costs only a threshold comparison at runtime.
Logging never touches the simulation — it writes to stderr and returns — so it has no effect on gameplay determinism or replays. This first version ships the console (stderr) sink; a rotating-file sink and an in-engine overlay sink are planned follow-ups.
Log.trace(msg, [key, value]...) -> voidLog at the trace level (0) — the most verbose.Log.debug(msg, [key, value]...) -> voidLog at the debug level (1) — development detail.Log.info(msg, [key, value]...) -> voidLog at the info level (2) — normal operation.Log.warn(msg, [key, value]...) -> voidLog at the warn level (3) — something looks wrong.Log.error(msg, [key, value]...) -> voidLog at the error level (4) — a failure.Log.set_level(n) -> voidShow only messages at level n or above (0 = all).Log.level() -> intThe current logging threshold.