Logger.md
PuduLangLog.Logger
type: module path: "@root/src/PuduLangLog/Logger.pudu" fidelity: Active grammar: "[[grammar/pudu]]" depth_score: 0.85 depth_status: DEEP tags: [module, backbone] aliases: [PuduLangLog.Logger]
Purpose
What a program writes through: logger.information("Disk {Disk} is {Percent}% full", args) and the other level methods, contextual loggers from forContext and forSource, trace identifiers, and the lifecycle of flushing, closing, and [[domain/Bootstrap|reloading]].
Interface
Signatures
export type Logger = { root: Root, context: Array[Enricher.Enricher], source: Option[Str], trace: Option[(Str, Str)] }
export type Root = Fixed(Pipeline.Pipeline) | Reloadable(Sync.Cell[Pipeline.Pipeline])
export fn fromPipeline(pipeline: Pipeline.Pipeline) -> Logger
export fn reloadable(pipeline: Pipeline.Pipeline) -> Logger
export fn reload(logger: &Logger, pipeline: Pipeline.Pipeline) -> Bool
export fn none() -> Logger
export fn pipelineOf(logger: &Logger) -> Pipeline.Pipeline
export fn flush(logger: &Logger) -> ()
export fn close(logger: &Logger) -> ()
export fn asSink(logger: &Logger) -> Sink.Sink
export trait Logging {
fn isEnabled(self: &Self, level: Log.Level) -> Bool
fn write(self: &Self, level: Log.Level, template: Str, arguments: Array[Log.Value]) -> ()
fn writeFailure(self: &Self, level: Log.Level, failure: Log.Failure, template: Str, arguments: Array[Log.Value]) -> ()
fn tryWrite(self: &Self, level: Log.Level, failure: Option[Log.Failure], template: Str, arguments: Array[Log.Value]) -> Result[(), Str]
fn emit(self: &Self, event: Log.Event) -> Result[(), Str]
fn verbose(self: &Self, template: Str, arguments: Array[Log.Value]) -> ()
fn debug(self: &Self, template: Str, arguments: Array[Log.Value]) -> ()
fn information(self: &Self, template: Str, arguments: Array[Log.Value]) -> ()
fn warning(self: &Self, template: Str, arguments: Array[Log.Value]) -> ()
fn error(self: &Self, template: Str, arguments: Array[Log.Value]) -> ()
fn fatal(self: &Self, template: Str, arguments: Array[Log.Value]) -> ()
fn forContext(self: &Self, name: Str, held: Log.Value) -> Self
fn forContextDestructured(self: &Self, name: Str, held: Log.Value) -> Self
fn forSource(self: &Self, source: Str) -> Self
fn forEnricher(self: &Self, enricher: Enricher.Enricher) -> Self
fn withTrace(self: &Self, traceId: Str, spanId: Str) -> Self
fn bindTemplate(self: &Self, template: Str, arguments: Array[Log.Value]) -> (Log.Template, Array[Log.Property])
fn bindProperty(self: &Self, name: Str, held: Log.Value, destructure: Bool) -> Option[Log.Property]
}Linkage
- Requires: [[src/PuduLangLog]], [[src/PuduLangLog/Constants/Names]], [[src/PuduLangLog/Domain/Capture]], [[src/PuduLangLog/Domain/Parser]], [[src/PuduLangLog/Enricher]], [[src/PuduLangLog/Pipeline]], [[src/PuduLangLog/SelfLog]], [[src/PuduLangLog/Sink]],
Std.Sync. - Consumed by: package users, [[src/PuduLangLog/Configuration]], and every integration.
Algorithm
- A logger is a root pipeline (fixed, or reloadable through a shared cell), the enrichers of its context, its source context, and its trace and span.
- A write checks the level first and does nothing more when it fails. Otherwise it parses the template (through the pipeline's store), binds the arguments, reports binding problems to the self-log, stamps the event with the pipeline's clock and the logger's trace, and processes it.
forContextcaptures its value immediately under the pipeline's policy and adds a property enricher; a blank name is reported and ignored.forSourceaddsSourceContext, which also selects level overrides.tryWriteanswers an audit sink's failure; every other write discards it.reloadswaps a reloadable root's pipeline, then flushes and closes the old one; every logger derived from the root follows.asSinkmakes a logger a sink of another pipeline, applying its own level check, enrichers, and filters.
Negative Logic (Prohibited Paths)
- A disabled level costs one comparison: nothing is parsed or captured.
- No write fails the program except through
tryWriteand an audit sink.
Edge Cases
- A message property wins over a context property of the same name, and an inner context over an outer one.
Depth
DEPTH 0.85 (DEEP). Tested by test/PuduLangLog/LoggerTest and test/PuduLangLog/TopologyTest.
Grill Log
- Q: Why methods on the logger rather than module functions? A:
logger.forSource("App.Api").information(...)reads in the order it happens, without nesting calls. _Rejected:_Logger.information(&Logger.forSource(&logger, …), …). - Q: Why no global default logger? A: Pudu has no mutable module state; a program passes its logger, or a reloadable one built at startup, to the code that writes. See [[decisions/ADR-0002-explicit-logger]]. _Rejected:_ hidden global state.
- Q: Why capture
forContextvalues at once? A: The value is the one current when the context was made; capturing later could observe a changed value. _Rejected:_ capturing per event.
Referenced by
[[architecture/_MOC]] · [[CHANGELOG]] · [[domain/Bootstrap]] · [[src/PuduLangLog/_MOC]] · [[src/PuduLangLog/Bridge]] · [[src/PuduLangLog/Configuration]] · [[src/PuduLangLog/Constants/Names]] · [[src/PuduLangLog/Domain/Capture]] · [[src/PuduLangLog/Domain/Levels]] · [[src/PuduLangLog/Domain/Parser]] · [[src/PuduLangLog/Enricher]] · [[src/PuduLangLog/Pipeline]] · [[src/PuduLangLog/SelfLog]] · [[src/PuduLangLog/Sink]] · [[src/PuduLangLog/Timing]] · [[src/PuduLangLog/Web/Correlation]] · [[src/PuduLangLog/Web/RequestLogging]] · [[subsystems/Pipeline]]
