npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

Readme

Monthly Downloads NPM CI

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-logger

Quick 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.js

Child Loggers

const requestLogger = logger.child({ requestId: 'abc-123', service: 'api' })
requestLogger.info('Processing request')
// All entries include requestId and service

Error 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.jsprocess.stdout.write / process.stderr.write (bypasses console overhead)
  • DenoDeno.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