go-ruby-logger documentation¶
The deterministic core of Ruby's stdlib Logger in pure Go — MRI-compatible, no cgo.
go-ruby-logger/logger is a faithful, pure-Go (zero cgo) reimplementation of the
deterministic core of MRI 4.0.5's stdlib
Logger (the logger-1.7.0
gem): the severity model, the default Logger::Formatter, level gating, and the
LogDevice rotation policy. The module path is
github.com/go-ruby-logger/logger.
It was extracted from rbgo's internals into a reusable standalone library: the
module is standalone and importable by any Go program, and it is the logging
backend bound into go-embedded-ruby
by rbgo as a native module — just like
go-ruby-regexp,
go-ruby-erb and
go-ruby-yaml. The dependency runs the other
way: this library has no dependency on the Ruby runtime.
Status: complete — Logger core, MRI byte-exact
The severity model (DEBUG..UNKNOWN, SeverityLabel, CoerceSeverity), the default formatter ("%.1s, [%s #%d] %5s -- %s: %s\n" with the %6N timestamp and msg2str coercion) over an injected clock and pid, level gating (Add plus the helpers and DebugQ..FatalQ predicates), and the rotation policy (size shifts and daily/weekly/monthly periods) — all pure, no IO. Validated by a differential oracle against the system ruby byte-for-byte, at 100% coverage, gofmt + go vet clean, CI green across the six 64-bit Go targets and three OSes.
What it is — and isn't¶
Building a log line, deciding the severity gate, and computing the rotation
schedule and rotated-file names are fully deterministic and need no
interpreter, so they live here as pure Go. Opening the file, writing the bytes,
renaming on rotation, and reading the live wall clock are the host's job: the
library hands back the bytes and the rename plan, and the host's IO device
performs them — via an injected Sink, Now clock and Pid.
Quick taste¶
log := logger.New(func(line string) { fmt.Print(line) })
log.Progname = "myapp"
log.Level = logger.INFO
log.Debug("filtered out (below INFO)", "")
log.Info("server started", "")
// I, [2026-06-30T09:12:01.004212 #4242] INFO -- myapp: server started
// Rotation is a pure decision the host acts on.
if logger.ShouldRotateBySize(fileSize(), logger.DefaultShiftSize, logger.DefaultShiftAge) {
for _, mv := range logger.ShiftAgeSequence("app.log", logger.DefaultShiftAge) {
os.Rename(mv.From, mv.To) // host performs the rename plan
}
}
Repositories¶
| Repo | What it is |
|---|---|
logger |
the library — Ruby's Logger core in pure Go |
docs |
this documentation site (MkDocs Material, versioned with mike) |
go-ruby-logger.github.io |
the organization landing page (Hugo) |
brand |
logo and brand assets |
Principles¶
- Pure Go,
CGO_ENABLED=0— trivial cross-compilation, a single static binary, no C toolchain. - MRI byte-exact. Line bytes, severity labels and the rotation schedule match
reference Ruby exactly, validated by a differential oracle against the
rubybinary. - Compute core, not the IO device. The library is a pure decision layer; the host performs the IO via an injected sink, clock and pid.
- 100% test coverage is the target, enforced as a CI gate, across 6 arches and 3 OSes.
Where to go next¶
- Why pure Go — why the deterministic slice of
Loggerlives as a standalone, interpreter-independent Go library. - Usage & API — the public surface and worked examples.
- Roadmap — what is done and what is host-side by design.
Source lives at github.com/go-ruby-logger/logger.