Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-log

Structured event logging for Pudu: message templates, enrichment, filtering, formatting, and sinks

0.1.0Apache-2.01

InstallClose

Logger.md

Markdown121 lines5.6 KB

GitHub ↗

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

  1. 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.
  2. 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.
  3. forContext captures its value immediately under the pipeline's policy and adds a property enricher; a blank name is reported and ignored. forSource adds SourceContext, which also selects level overrides.
  4. tryWrite answers an audit sink's failure; every other write discards it.
  5. reload swaps a reloadable root's pipeline, then flushes and closes the old one; every logger derived from the root follows.
  6. asSink makes 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 tryWrite and 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 forContext values 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]]