Skip to main content

LOG

Constant LOG 

Source
pub const LOG: &str = "//! Log lines, each with a time and a level, written to standard error or\n//! standard output.\n//!\n//! `Log::trace`, `Log::debug`, `Log::info`, `Log::warn` and `Log::error` each\n//! write one line made of the time in UTC, the level and the message, such as\n//! `2026-08-19T02:40:13.123Z INFO server started`. The message is written as\n//! given, so a newline in it splits it over lines. A call below the\n//! configured `LogLevel` writes nothing. `Log::init_with` sets that level and\n//! where lines go; until it is called, the level is `Info` and lines go to\n//! standard error. Every module sees these names without an import, and a\n//! module\'s own definition of one of them shadows the one here.\n//!\n//! Logging is not `async`: each call writes its line before it returns, so\n//! any function can log. The configuration holds for the whole program, and\n//! the last call to `Log::init` or `Log::init_with` sets it.\n//!\n//! Logging at the default level, then raising the level so `info` lines are\n//! dropped:\n//!\n//! ```\n//! // Writes a line such as `2026-08-19T02:40:13.123Z INFO cache warmed` to\n//! // standard error.\n//! Log::info(\"cache warmed\")\n//!\n//! // From here on, only `Warn` and `Error` are written.\n//! Log::init_with(LogConfig { level: Warn, format: Human, output: Stderr })\n//! Log::info(\"dropped: below the configured level\")\n//! Log::warn(\"disk almost full\")\n//!\n//! // Levels compare by rank.\n//! assert(Warn.to_int() > Info.to_int(), \"Warn is more severe than Info\")\n//! assert_eq(Warn.label(), \"WARN\")\n//! ```\n\n// Levels, ordered by `to_int` below. The order is load-bearing: filtering\n// compares those integers, because Nova has no `==` on sum types\n// (`E0013`), so `to_int` is the only mechanism available rather than a\n// convenience.\n/// How severe a log message is, from least to most: `Trace`, `Debug`,\n/// `Info`, `Warn`, `Error`.\n///\n/// The configured level lets through the messages at that level and above,\n/// and drops the rest.\npub type LogLevel =\n    /// Fine-grained detail; the least severe level.\n    | Trace\n    /// Detail for debugging.\n    | Debug\n    /// A normal event; the default level.\n    | Info\n    /// Something unexpected that the program can carry on from.\n    | Warn\n    /// A failure; the most severe level.\n    | Error\n\n// One variant today. `Json` arrives with the increment that brings string\n// escaping; declaring the field now means adding that variant later breaks\n// only exhaustive matches, where adding a *field* to `LogConfig` would\n// break every construction site.\n/// How a log line is laid out. `Human` is the only layout.\npub type LogFormat =\n    /// The time, the level\'s label and the message, separated by single\n    /// spaces, such as `2026-08-19T02:40:13.123Z WARN disk almost full`.\n    | Human\n\n// `File(String)` arrives with the increment that adds a synchronous\n// file-write path; every `std/fs` write is `async fn` today.\n/// Where log lines are written.\npub type LogOutput =\n    /// Standard error; the default.\n    | Stderr\n    /// Standard output.\n    | Stdout\n\n/// A logger configuration, for `Log::init_with`.\npub record LogConfig {\n    /// The least severe level written; messages below it are dropped.\n    pub level: LogLevel,\n    /// How each line is laid out.\n    pub format: LogFormat,\n    /// Where the lines are written.\n    pub output: LogOutput,\n}\n\n// The logger\'s namespace. An empty record because Nova has no free-standing\n// namespaces, and associated functions rather than top-level ones because a\n// top-level `pub fn` is glob-imported into every module: `error`, `info` and\n// `debug` at top level would take those names from all user code, and\n// `import_std_module` resolves such a collision silently in the *user\'s*\n// favour \u{2014} making this module\'s `error` unreachable with no diagnostic.\n// `std/strings` refused the same trade for `join`.\n/// The logger, whose functions are called on the type itself, as in\n/// `Log::warn(\"disk almost full\")`.\n///\n/// It holds nothing. Its functions write log lines and set the logger\'s\n/// configuration.\npub record Log {}\n\nimpl LogLevel {\n    /// Returns the level\'s rank, from 0 for `Trace` to 4 for `Error`: a higher\n    /// rank is more severe, so two levels compare by their ranks.\n    pub fn to_int(self) -> Int {\n        match self {\n            Trace => 0\n            Debug => 1\n            Info => 2\n            Warn => 3\n            Error => 4\n        }\n    }\n\n    /// Returns the level\'s name as log lines show it: `TRACE`, `DEBUG`,\n    /// `INFO`, `WARN` or `ERROR`.\n    pub fn label(self) -> String {\n        match self {\n            Trace => \"TRACE\"\n            Debug => \"DEBUG\"\n            Info => \"INFO\"\n            Warn => \"WARN\"\n            Error => \"ERROR\"\n        }\n    }\n}\n\n// `match` rather than `out == Stderr`: there is no equality on sum types.\nfn to_stderr_flag(out: LogOutput) -> Int {\n    match out {\n        Stderr => 1\n        Stdout => 0\n    }\n}\n\n// Emit one line if `level` clears the configured threshold.\n//\n// Private, so the glob import never binds it and it takes no name from user\n// code. The threshold is checked *before* the clock is read and the line is\n// built, so a filtered-out call costs one intrinsic and no allocation.\n//\n// The comparison is `<`, which makes the threshold inclusive: at threshold\n// `Info`, an `Info` message is emitted.\nfn emit(level: LogLevel, msg: String) {\n    if level.to_int() < log_config_level() { return }\n    let line = \"${SystemTime::now().to_iso8601()} ${level.label()} ${msg}\"\n    if log_config_to_stderr() == 1 { eprintln(line) } else { println(line) }\n}\n\nimpl Log {\n    // Install the default: `Info`, `Human`, `Stderr`. Not a prerequisite \u{2014}\n    // the runtime\'s getter installs the same default on first read, so a\n    // program that never calls this still logs. This is the override spelled\n    // explicitly.\n    //\n    // This literal duplicates the runtime\'s own default (`DEFAULT` in\n    // `crates/nova-runtime/src/log.rs`) rather than reading it back \u{2014} there\n    // is no intrinsic that returns \"the default\", only `log_set_config`,\n    // which takes one explicit configuration to install. The duplication is\n    // pinned equal by `log_init_resets_to_default_run`\n    // (`crates/nova-cli/tests/run_tests.rs`), which overwrites a\n    // configuration wrong in both fields with a call to `init()` and checks\n    // that the runtime\'s actual default comes back, not the wrong one.\n    /// Restores the default configuration: level `Info`, the `Human` layout,\n    /// and standard error.\n    ///\n    /// Calling it is optional, since a program that never configures the\n    /// logger has this configuration already. It replaces any configuration\n    /// set before.\n    pub fn init() { Log::init_with(LogConfig { level: Info, format: Human, output: Stderr }) }\n\n    // `config.format` is deliberately unread: `LogFormat` has one variant,\n    // so there is nothing to branch on yet.\n    /// Sets the logger\'s configuration, replacing any set before.\n    ///\n    /// From then on, messages below `config.level` are dropped, and lines go\n    /// to `config.output`. `config.format` has one choice, `Human`.\n    pub fn init_with(config: LogConfig) {\n        log_set_config(config.level.to_int(), to_stderr_flag(config.output))\n    }\n\n    /// Writes `msg` as a log line at level `Trace`, the least severe, if the\n    /// configured level is `Trace`.\n    pub fn trace(msg: String) { emit(Trace, msg) }\n    /// Writes `msg` as a log line at level `Debug`, if the configured level is\n    /// `Debug` or `Trace`.\n    pub fn debug(msg: String) { emit(Debug, msg) }\n    /// Writes `msg` as a log line at level `Info`, if the configured level is\n    /// `Info`, as it is by default, or less severe.\n    pub fn info(msg: String) { emit(Info, msg) }\n    /// Writes `msg` as a log line at level `Warn`, unless the configured level\n    /// is `Error`.\n    pub fn warn(msg: String) { emit(Warn, msg) }\n    /// Writes `msg` as a log line at level `Error`, the most severe, whatever\n    /// the configured level.\n    pub fn error(msg: String) { emit(Error, msg) }\n}\n";
Expand description

std/log/lib.nova.