Skip to content

Configuring Loggers

Chris Michael edited this page Sep 29, 2026 · 1 revision

Pudu

Configuring Loggers

Configuration.create() keeps Information and above and writes nowhere; Configuration.nested() keeps every level, for a sub-logger whose parent already decided which events arrive. Each method answers a new configuration, so a base can be shared and extended.

Method Decision
minimumLevel, controlledBy the minimum level, fixed or through a LevelSwitch
overrideLevel, overrideSwitch the minimum for a source prefix; the most specific prefix decides
writeTo, writeToAtLeast, writeToControlled, writeToWhen a sink, optionally restricted by level, switch, or condition
writeToLogger another logger, whose enrichers, filters, and sinks then apply
writeToFallible, writeToFallbackChain, auditTo failure handling of sinks
enrichWith, enrichWithProperty, filterWith, filterExcluding, filterIncludingOnly enrichment and filtering
destructureByTransforming, destructureAsScalar, destructureWithRule, destructureByIgnoring, destructureByMasking how structures are captured
maximumDepth, maximumStringLength, maximumCollectionCount capture limits
selfLog, clock where problems are reported and where time comes from
createLogger, createReloadableLogger the logger
module Ex03Configuring

import Std.Io as Io
import PuduLangLog.Configuration as Configuration
import PuduLangLog.LevelSwitch as LevelSwitch
import PuduLangLog as Log
import PuduLangLog.Logger as Logger
import PuduLangLog.Sinks.Memory as Memory
import PuduLangLog.Value as Value

fn report(label: Str, memory: &Memory.Memory) -> () {
  let _line = Io.writeLine(label + ": " + Memory.messages(memory).join(" | "))
}

export fn main() -> Int {
  let everything = Memory.create()
  let errors = Memory.create()
  let audits = Memory.create()
  let control = LevelSwitch.create(Log.Information)
  LevelSwitch.onChange(&control, fn(before: Log.Level, after: Log.Level) -> () {
      let _told = Io.writeLine("switch moved from " + show(before) + " to " + show(after))
    })
  let errorsOnly = Configuration.nested().filterIncludingOnly(fn(event: &Log.Event) -> Bool { event.level == Log.Error }).writeTo(Memory.sink(&errors)).createLogger()
  let logger = Configuration.create()
    .controlledBy(&control)
    .overrideLevel("Shop.Http", Log.Warning)
    .writeTo(Memory.sink(&everything))
    .writeToLogger(&errorsOnly)
    .writeToAtLeast(Memory.sink(&audits), Log.Warning)
    .createLogger()
  logger.debug("hidden until the switch moves", [])
  logger.forSource("Shop.Http.Client").information("hidden by the override", [])
  logger.forSource("Shop.Http.Client").warning("slow response", [])
  LevelSwitch.set(&control, Log.Debug)
  logger.debug("now visible", [])
  logger.error("Order \{Id\} failed", [Value.int(9)])
  report("everything", &everything)
  report("errors only", &errors)
  report("warning and above", &audits)
  let early = Memory.create()
  let bootstrap = Configuration.create().writeTo(Memory.sink(&early)).createReloadableLogger()
  bootstrap.information("starting", [])
  let later = Memory.create()
  let _reloaded = Logger.reload(&bootstrap, Configuration.pipelineOf(&Configuration.create().writeTo(Memory.sink(&later))))
  bootstrap.information("configured", [])
  report("before reload", &early)
  report("after reload", &later)
  Logger.close(&logger)
  Logger.close(&bootstrap)
  0
}

Check it and run it:

pudu check src/Ex03Configuring.pudu
pudu run src/Ex03Configuring.pudu

Output:

switch moved from Information to Debug
everything: slow response | now visible | Order 9 failed
errors only: Order 9 failed
warning and above: slow response | Order 9 failed
before reload: starting
after reload: configured

A level switch tells its listeners of each real change. createReloadableLogger builds a logger for startup; Logger.reload replaces its pipeline with the full configuration's, flushing and closing the previous one, and every logger derived from it follows.

Related

Clone this wiki locally