05 / CONTROL
logx
Structured logging over moonlog's sink: the four level names an etc/*.yaml carries, the @timestamp / content keys a collector indexes, the WithDuration view, and a logger whose level and destination move at runtime.
WHERE IT SITS
INTERFACE
struct LoggerstructA leveled, structured logger: entries below the configured level are dropped without being rendered, and everything else reaches the sink as one record carrying @timestamp, level, content, an optional duration, and any extra fields. The timestamp is milliseconds from the injected Clock, not a formatted calendar time: MoonBit has no portable date formatter, and the clock is already how every other timed layer here reads the time.
let stamp_key : String = "@timestamp"letThe key the entry's time is written under.
let message_key : String = "content"letThe key the message is written under.
let duration_key : String = "duration"letThe key the elapsed time is written under.
fn entry(record : @moonlog.Record) -> StringfnOne entry as the single-line JSON object emits: @timestamp, level, content, then whatever else the entry carries. This is the default sink's format, not a rule — hand Logger::new another moonlog sink and the same records are rendered that sink's way.
let level_key : String = "level"letThe key the level is written under.
fn level_name(level : @moonlog.Level) -> Stringfnname for a level. fatal reads as severe, which is what calls its top level and what its collectors filter on.
fn Logger::new( clock : @clock.Clock, level~ : LogLevel = Info, sink~ : &@moonlog.Sink = @moonlog.Lines::new(line => println(line))) -> LoggerfnA logger writing to sink at level and above. Every record reaches the sink with its [entry] line already rendered; the default sink prints that line. Logger::new(clock, sink=@moonlog.Discard::new()) is how a test silences one.
fn Logger::level(self : Logger) -> LogLevelfnThe level below which entries are dropped.
fn Logger::set_level(self : Logger, level : LogLevel) -> UnitfnRaise or lower the threshold, for this logger and every view derived from it.
fn Logger::set_sink(self : Logger, sink : &@moonlog.Sink) -> UnitfnSend entries to another sink, for this logger and every view derived from it — a file, a collector client, a buffer under test. The sink is handed the record and line, and may write either.
fn Logger::set_writer(self : Logger, writer : (String) -> Unit) -> UnitfnSend entries somewhere other than stdout, keeping line format. [Logger::set_sink] changes the format too.
fn Logger::enabled(self : Logger, level : LogLevel) -> BoolfnWhether an entry at level would be written.
fn Logger::with_duration(self : Logger, ms : Int64) -> LoggerfnA view of this logger that stamps duration on every entry, sharing the level and sink with the original.
fn Logger::log( self : Logger, level : LogLevel, content : String, fields~ : Array[(String, Json)] = []) -> UnitfnRender and write one entry, unless level is below the threshold.
fn Logger::debug( self : Logger, content : String, fields~ : Array[(String, Json)] = []) -> UnitfnWrite a debug entry.
fn Logger::info( self : Logger, content : String, fields~ : Array[(String, Json)] = []) -> UnitfnWrite an info entry.
fn Logger::error( self : Logger, content : String, fields~ : Array[(String, Json)] = []) -> UnitfnWrite an error entry.
fn Logger::severe( self : Logger, content : String, fields~ : Array[(String, Json)] = []) -> UnitfnWrite a severe entry.
let logx : Logger = Logger::new(@clock.Clock::system())letThe process-wide logger: what the middlewares write to when no logger is handed to them, on the system clock at info. RestEngine::new points its level at the loaded Log.Level.
fn set_log_level(level : LogLevel) -> UnitfnSet the process logger's level.
fn set_log_writer(writer : (String) -> Unit) -> UnitfnSend the process logger's entries to writer.
fn set_log_sink(sink : &@moonlog.Sink) -> UnitfnSend the process logger's entries to sink.
enum LogLevelenum
fn LogLevel::to_string(self : LogLevel) -> StringfnThe canonical lowercase name uses on the wire.
fn LogLevel::as_level(self : LogLevel) -> @moonlog.LevelfnThe moonlog level this one records as. ladder has four rungs and moonlog's has six, so the map is one-way: Severe records as fatal, and [level_name] writes it back out as severe.
fn LogLevel::parse(s : String) -> LogLevelfnParse a level name, falling back to Info for anything unrecognised — the same lenient default applies to a missing/empty level.
extend LogLevel with Compare::item
extend LogLevel with Debug::item
extend LogLevel with Eq::item
extend LogLevel with FromJson::item
impl @json.FromJson for LogLevel with fn from_json(json, path)itemDecode a LogLevel from a JSON string ("debug"/"info"/"error"/ "severe"), reusing LogLevel::parse's lenient fallback to Info. This impl lets ServiceConf's derived FromJson read log_level as a plain string — the way writes it in YAML/JSON config — instead of a tagged variant.