Wanderland

Wanderland Core: Logging

Block-based lazy logging mixin. Extracted from kremis and dark-lantern where it was duplicated verbatim (only the module namespace differed).

Pattern

Log methods take blocks, not strings. The block is only evaluated if the log level is enabled. This matters when the log message involves expensive computation (serializing objects, string interpolation with method calls).

include Wanderland::Logging

debug { "Expensive #{JSON.generate(large_object)} only runs at debug" }
info  { "Processing #{slug}" }
warn  { "Something looks off: #{details}" }
error { "Failed: #{exception.message}" }

Configuration

# At boot time
Wanderland.configure_logging(
  level: :info,          # :debug, :info, :warn, :error, :fatal
  output: nil,           # nil/:stdout, :stderr, "/path/to/file", "/path/to/dir/"
  name: "lantern"        # used for log filename when output is a directory
)

Output resolution:

Format

[2026-04-01 21:26:17] INFO  [Lantern::RouteMatch] Matched /node/:slug

[timestamp] SEVERITY [class_name] message

The class name comes from self.class.name on the including class. Falls back to "Wanderland" if anonymous.

Mixin Mechanics

module Wanderland::Logging
  def self.included(base)
    base.extend(ClassMethods)     # class-level .logger
  end

  def logger = Wanderland.logger  # instance-level
  def debug(&block)               # delegates to logger with lazy eval
    logger.debug(log_prefix) { block.call } if logger.debug?
  end
end

Both class methods and instance methods delegate to the singleton Wanderland.logger. The singleton is configured once at boot and shared across the process.