Structured log primitive for the AbsoluteJS substrate. Levels, child
loggers, sinks (console-JSON, console-pretty, memory, rotating file),
optional secret redaction via
@absolutejs/secrets,
optional trace-id correlation via
@absolutejs/telemetry.
bun add @absolutejs/logsimport { createLogger, consoleJsonSink, rotatingFileSink } from '@absolutejs/logs';
import { readActiveTraceId } from '@absolutejs/telemetry';
const log = createLogger({
level: 'info',
fields: { service: 'api', region: 'us-east-2' },
sinks: [
consoleJsonSink(),
rotatingFileSink({ path: '/var/log/api/app.log', maxBytes: 10_000_000, keep: 5 }),
],
redact: (text) => broker.redact(text), // @absolutejs/secrets
readTraceId: readActiveTraceId, // @absolutejs/telemetry
});
log.info('User signed in', { userId: 'u_42', tenant: 'acme' });
// → {"at":1700000000000,"level":"info","message":"User signed in","tenant":"acme","traceId":"abc123","fields":{"service":"api","region":"us-east-2","userId":"u_42"}}
const requestLog = log.child({ requestId: req.id });
requestLog.warn('rate limit exceeded', { remaining: 0 });
// Same as parent, plus requestId in fields.trace → debug → info → warn → error → fatal. Filter at
level; bump at runtime with log.setLevel('debug') (SIGUSR2-style
incident triage).
| Sink | Purpose |
|---|---|
consoleJsonSink() |
One JSON line per event. stdout for < errorThreshold, stderr above. |
consolePrettySink() |
Human-readable lines for local dev. |
memorySink() |
In-process FIFO buffer. .inspect() + .clear() for tests. |
rotatingFileSink({ path, maxBytes, keep }) |
Append-only file with size-based rotation. |
Custom sinks just implement LogSink: { name?, write, flush?, close? }.
@absolutejs/secretsredaction. Passredact: broker.redactand every serialized event flows through the redactor before hitting a sink. Secrets never reach disk.@absolutejs/telemetrytrace correlation. PassreadTraceId: readActiveTraceIdand every event carries the active OTel trace id. Failure (no provider wired) silently leavestraceIdoff — never breaks the log line.@absolutejs/metricsexposure.logger.metrics()returns aLoggerMetricsshape. A@absolutejs/metrics/logscollector subpath is planned for the next release.
logger.metrics();
// {
// logged: { trace: 0, debug: 0, info: 100, warn: 5, error: 2, fatal: 0 },
// writes: 214, // 107 events × 2 sinks
// writeErrors: 0,
// sinkErrors: {}
// }- Fire-and-forget writes.
log.info(...)is synchronous and returns immediately; sink writes run in the background. Useawait log.flush()before shutdown. - Per-sink failures don't block others. One sink throwing
bumps
sinkErrors[name]and callsonError; the rest still receive the event. Same shape as@absolutejs/audit. - Closed loggers drop calls silently. Once
await log.close()has run, furtherlog.info(...)calls are no-ops — no throw, no buffer.
BSL-1.1 with named carveout against hosted log-management platforms. Change date: 2030-05-31 (Apache 2.0).