Skip to content

Repository files navigation

Archivist

A structured logging library for Luau that runs on both Lute and Roblox.

Note

Archivist is pre-release and under active development. The API may change before v1.

Features

  • Dual runtime: one library, first-class support for Lute (CLI) and Roblox
  • Log levels: trace, debug, info, warn, err, filtered by a minimum level
  • LOG_LEVEL: level resolution from the environment on Lute, _G.LOG_LEVEL on Roblox
  • Colorful output: ANSI-colored console output on Lute; plain print/warn on Roblox
  • Structured records: every entry is a LogRecord object (timestamp, level, name, args) that sinks can consume
  • Sinks: route records anywhere: console, files (Lute), or your own callback (e.g. an in-app log viewer)
  • Dev/prod deferral: sequential print-like output in dev; in prod, records batch and each sink is written once per batch instead of once per record

Installation

Lute (Loom)

Add to your loom.config.luau dependencies:

Archivist = {
	rev = "v0.1.0",
	sourceKind = "github",
	source = "https://github.com/flipbook-labs/archivist",
},

Then run lute pkg install.

Roblox (Wally)

[dependencies]
Archivist = "flipbook-labs/archivist@0.1.0"

Usage

local Archivist = require("@pkg/Archivist")

local logger = Archivist.createLogger("MyModule")

logger.info("loaded", 3, "stories")
logger.info("structured data:", { port = 8080, ready = true })
logger.warn("something looks off")
logger.err("something broke")

-- Children share the parent's sinks and attach fields to every record:
local child = logger.child("requests", { requestId = "abc123" })
child.debug("handling request")

-- Prod mode batches: these queue and flush to each sink as ONE write.
local batched = Archivist.createLogger("Telemetry", { mode = "Prod" })
batched.info("queued 1")
batched.info("queued 2")
batched.flush() -- optional; pending batches also flush on defer and at exit

-- Show everything, regardless of LOG_LEVEL:
local verbose = Archivist.createLogger("MyModule", { level = "Trace" })

Pretty output middleware

The default console formatter emits the level and message:

[info] loaded 3 stories

Add the built-in middleware when you want logger names or timestamps:

local logger = Archivist.createLogger("MyModule", {
	middleware = {
		Archivist.middleware.name,
		Archivist.middleware.timestamp,
	},
})

Middleware runs in declaration order. Each function receives the formatted line and a context containing the structured record, platform, color setting, and a color-aware style helper. It can prepend, append, or replace text.

The logger-level middleware option configures its default console sink. When you provide sinks, pass middleware to the pretty formatter instead:

local formatter = Archivist.createPrettyFormatter({
	middleware = {
		Archivist.middleware.name,
	},
})
local logger = Archivist.createLogger("MyModule", {
	sinks = {
		Archivist.createConsoleSink({ formatter = formatter }),
	},
})

Sinks

local logger = Archivist.createLogger("App", {
	sinks = {
		-- The runtime's console: colored stdout on Lute, print/warn on Roblox.
		Archivist.createConsoleSink(),

		-- Any custom destination, e.g. an in-app log viewer panel:
		Archivist.createCallbackSink(function(record)
			LogsStore.addLine(record)
		end),

		-- A JSON-lines log file (Lute only):
		Archivist.createFileSink({ path = "logs/app.log" }),

		-- Ship batches to an external platform; bring your own HTTP client:
		Archivist.createTransportSink({
			batchSize = 50,
			send = function(records)
				httpClient.post(INGEST_URL, records)
			end,
		}),
	},
})

Formatters control how text-oriented sinks render records: createPrettyFormatter (human-readable, optional ANSI color and middleware) and createJsonFormatter (one JSON object per line). Sinks that consume records structurally (createCallbackSink, createTransportSink) bypass formatting entirely.

The minimum level resolves from, in order: the level option, the LOG_LEVEL environment variable (Lute), _G.LOG_LEVEL (Roblox), then defaults to info.

Archivist.getLogger(name) returns a per-name cached logger so modules can share one configured logger without threading it through requires.

Development

Archivist uses Rokit for toolchain management.

rokit install
lute run example   # run the demo script under Lute
lute test          # run the unit tests
lute run build     # build the Roblox model (Archivist.rbxm)

License

MIT

About

A structured logging library for Luau that runs on both Lute and Roblox

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages