Block-based lazy logging mixin. Extracted from kremis and dark-lantern where it was duplicated verbatim (only the module namespace differed).
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}" }
# 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:
nil or :stdout → $stdout:stderr → $stderr{name}.log in that directory.write → used directly (StringIO for testing)[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. Classes that include Wanderland::Boundary get a more specific prefix — see Boundary log prefix below.
Single-character glyphs commonly used in log lines. Reference them by short name from any class that includes Wanderland::Logging (or fully qualified as Wanderland::Logging::PLAY from outside) so glyph use stays consistent across the codebase.
| Constant | Glyph | Used for |
|---|---|---|
PLAY |
▶ | entering a phase / starting work |
DONE |
◀ | completed / returning |
OK |
✓ | success outcome |
FAIL |
✗ | failure outcome |
ARROW |
→ | transition / mapping (in prose, not in code) |
WARN |
⚠ | warning / caution |
PAUSE |
… | awaiting / paused / yielded |
class CompileJar
include Wanderland::Boundary # transitively includes Wanderland::Logging
def call(input)
info { "#{PLAY} mvn package #{source_path}" }
# logs: [compile_jar] ▶ mvn package /path/to/repo
end
end
Wanderland::Boundary prepends a small BoundaryLogPrefix module to every including class so the log prefix becomes the registered boundary name (compile_jar) rather than the Ruby class name (WanderlandPipelinesPack::Boundaries::CompileJar). The prepend sits above Wanderland::Logging in the lookup chain, so calls to info, debug, warn, error from inside a boundary tag the log line with the boundary's registry name automatically — no hardcoded namespace strings, no copy-paste of the boundary name into every log call.
When a class that uses Wanderland::Logging directly (without Wanderland::Boundary), the prefix falls back to self.class.name as before.
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.
wanderland.dev