jsonl-logger
v0.5.1
Published
Lightweight ESM-only JSON Lines logger with pluggable formatters for Google Cloud Logging, VictoriaLogs, and more
Downloads
1,276
Maintainers
Readme
JSONL Logger
Lightweight JSON Lines logger with pluggable formatters (Google Cloud Logging, VictoriaLogs). Modern ESM-only, zero dependencies, Bun-first, works on Node.js and Deno.
Next.js (and other hardcoded plain text logs) become JSON-only logging for systems where it is required.
Install
bun add jsonl-logger
# or
npm install jsonl-loggerQuick Start
import { logger } from 'jsonl-logger'
logger.info('Server started', { port: 3000 })
logger.log('Neutral message', { note: 'no level icon' })
logger.error('Request failed', { path: '/api' }, new Error('timeout'))Without LOG_FORMAT, the logger outputs colored plain text with Unicode icons — ideal for local development. Set LOG_FORMAT to enable structured JSON for production (see Formatters below).
Formatters
Set LOG_FORMAT to enable JSON output with a specific formatter:
Google Cloud Logging
LOG_FORMAT=google-cloud-logging bun run server.ts
# Output: {"message":"...","timestamp":"...","severity":"INFO",...}VictoriaLogs
LOG_FORMAT=victoria-logs bun run server.ts
# Output: {"_msg":"...","_time":"...","level":"info",...}Custom Formatter
import type { Formatter } from 'jsonl-logger'
const myFormatter: Formatter = {
messageKey: 'msg',
format: (record) => ({
msg: record.message,
ts: record.timestamp,
lvl: record.level,
...record.context,
}),
}Console Interception
Monkey-patch console.* methods to output structured JSON — captures logs from third-party libraries:
import { intercept, originalConsole } from 'jsonl-logger/intercept'
intercept({
// Optional: custom formatter (default: GoogleCloudLogging)
formatter: VictoriaLogs,
// Optional: filter out noisy messages
filter: (level, message) => !message.includes('deprecation'),
// Optional: minimum log level
level: 'warn',
})
// Already-formatted JSON from the Logger class passes through unchanged
console.log('plain text') // → structured JSON
originalConsole.log('bypass interception')OpenTelemetry
The logger supports automatic trace context injection. Supply a traceContext getter that returns the active span's trace/span IDs — the formatter maps them to platform-specific fields automatically.
With @opentelemetry/api
import { trace } from '@opentelemetry/api'
import { Logger } from 'jsonl-logger'
const logger = new Logger({}, {
traceContext: () => {
const span = trace.getActiveSpan()
if (!span) return undefined
const { traceId, spanId, traceFlags } = span.spanContext()
return { traceId, spanId, traceFlags }
},
})
logger.info('request handled', { path: '/api' })
// GCL output includes "logging.googleapis.com/trace", "logging.googleapis.com/spanId", etc.
// VictoriaLogs output includes "trace_id", "span_id", etc.Custom trace context
const logger = new Logger({}, {
traceContext: () => ({
traceId: myTracer.currentTraceId(),
spanId: myTracer.currentSpanId(),
}),
})The traceContext option is also available on intercept():
import { intercept } from 'jsonl-logger/intercept'
intercept({
traceContext: () => {
const span = trace.getActiveSpan()
if (!span) return undefined
const { traceId, spanId, traceFlags } = span.spanContext()
return { traceId, spanId, traceFlags }
},
})Child loggers inherit the traceContext getter from their parent.
Next.js Integration
The preload module reads LOG_FORMAT and only activates when it's set. Safe to include unconditionally — it's a no-op without LOG_FORMAT.
Instrumentation
Next.js auto-detects instrumentation.ts at the project root. Use it to load the preload module on the server:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs' || typeof Bun !== 'undefined') {
await import('jsonl-logger/preload')
}
}Dockerfile (Standalone with Bun)
Next.js standalone output doesn't include all node_modules. Copy jsonl-logger explicitly from the build stage:
COPY --from=build /app/node_modules/jsonl-logger ./node_modules/jsonl-logger
ENV LOG_FORMAT=victoria-logs
CMD ["bun", "--preload", "jsonl-logger/preload", "server.js"]Node.js
For non-Bun deployments, use --import to preload:
LOG_FORMAT=google-cloud-logging node --import jsonl-logger/preload server.jsChild Loggers
const requestLogger = logger.child({ requestId: 'abc-123', service: 'api' })
requestLogger.info('Processing request')
// All entries include requestId and serviceError Handling
Errors passed to error() / fatal() capture the full stack trace and error.cause chain:
const inner = new Error('ECONNREFUSED')
const outer = new Error('fetch failed', { cause: inner })
logger.error('API call failed', { endpoint: '/users' }, outer)Dev mode (no LOG_FORMAT) — colored plain text with full stack:
18:42:05 ✖ API call failed {"endpoint":"/users"}
Error: fetch failed
at handler (/app/api/route.ts:42:5)
Caused by: Error: ECONNREFUSED
at connect (/app/db.ts:10:3)Production (LOG_FORMAT set) — structured JSON with error.* and error.cause.* fields:
{
"message": "API call failed",
"severity": "ERROR",
"endpoint": "/users",
"error.name": "Error",
"error.message": "fetch failed",
"error.stack": "Error: fetch failed\n at handler ...",
"error.cause.name": "Error",
"error.cause.message": "ECONNREFUSED",
"error.cause.stack": "Error: ECONNREFUSED\n at connect ..."
}The errorInfo() helper is exported for use in custom formatters:
import { errorInfo } from 'jsonl-logger'
const info = errorInfo(caughtError)
// { name, message, stack, cause?: { name, message, stack, cause?: ... } }Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| LOG_FORMAT | (unset) | Set to enable JSON: google-cloud-logging or victoria-logs |
| LOG_LEVEL | info/debug | Minimum log level (info when JSON, debug otherwise) |
Runtime Detection
The logger auto-detects the runtime and uses the fastest available I/O:
- Bun / Node.js —
process.stdout.write/process.stderr.write(bypasses console overhead) - Deno —
Deno.stdout.writeSync/Deno.stderr.writeSync - Browser / unknown — falls back to
console.log/console.error
Exports
| Subpath | Export |
|---------|--------|
| jsonl-logger | Logger, logger, errorInfo(), types (ErrorInfo, LogRecord, TraceContext, etc.) |
| jsonl-logger/google-cloud-logging | GoogleCloudLogging formatter |
| jsonl-logger/victoria-logs | VictoriaLogs formatter |
| jsonl-logger/intercept | intercept(), originalConsole |
| jsonl-logger/preload | Side-effect auto-intercept |
License
MIT
