Skip to main content

Command Palette

Search for a command to run...

logfx v1.0.0: One Logger for Development and Production

Pretty output when you're debugging, structured JSON when you're shipping. No config. Zero dependencies. 13 integrations.

Updated
โ€ข8 min read
logfx v1.0.0: One Logger for Development and Production

TL;DR logfx v1.0.0 gives you one logger instance that outputs pretty, readable logs in development and structured JSON in production. No config. Zero dependencies. 13 integrations for Datadog, Elasticsearch, Sentry, and more.

If you saw my earlier post on logfx here , v1.0.0 is the follow-up.

Who this is for: Backend developers, SREs, and frontend teams shipping to Edge or browser. Anyone tired of configuring separate dev and prod log formats.

Every logger forces a choice: readable output for development, or structured JSON for production. You either configure both manually or pick one and live with it.

What's New in v1.0.0

  • 13 official integrations - Datadog, Elasticsearch, Sentry, CloudWatch, GCP, Azure, Slack, Loki, Papertrail, Splunk, Honeycomb, Logtail, OpenTelemetry

  • Webhook transport - Circuit breaker, dead letter queue, retry with exponential backoff, multi-region failover

  • Pattern-based PII redaction - Email, SSN, credit card, phone, IP, JWT out of the box; custom patterns supported

  • Beacon transport - Reliable log delivery when the user closes the tab (SPAs)

  • Edge runtime support - Works in Cloudflare Workers, Vercel Edge without polyfills

  • Zero dependencies - Core package has no npm dependencies. Smaller audit surface, fewer transitive vulnerabilities.
    See each integration package README for full config options.

Getting Started

npm install logfx
import { createLogger } from 'logfx'
const log = createLogger()
log.info('Hello from logfx', { env: process.env.NODE_ENV })

Same instance. Pretty output when NODE_ENV !== 'production', JSON when it is.

The Core Dilemma

If you've ever deployed a Node.js app, you know the logging dilemma. In development, you want logs you can read. Colors, emojis, clear structure. When something breaks at 2am, you need to scan output quickly.

In production, you need structured JSON. Datadog, Elasticsearch, Splunk, and every log aggregator expect JSON. Timestamps, levels, correlation IDs. Machine-readable.

You either spend hours configuring both manually using brittle if (process.env.NODE_ENV === 'development') blocks, or you pick one format and learn to live with the pain.

I got tired of making that choice. logfx v1.0.0 removes it entirely. Same logger instance, environment-driven: it reads `NODE_ENV` and switches format automatically. No API to toggle. No config. No `if (isProd)` checks.

import { createLogger } from 'logfx'

const log = createLogger()

log.info('Server started', { port: 3000 })

Development output:

๐Ÿ’ก INFO  Server started { port: 3000 }

Production output (NODE_ENV=production):

{"timestamp":"2026-01-14T12:00:00.000Z","level":"info","message":"Server started","data":{"port":3000}}

No format flag. No environment checks in your code. It just works.

Log schema: Production JSON includes `timestamp`, `level`, `message`, `data` (your custom fields), and `error` (with `message`, `stack`, `code`) when present. Integrations map these to their native schemas (Datadog, ECS, etc.).

What logfx Actually Does

Core Logging

Five levels (debug, info, success, warn, error), namespaced loggers, context metadata. Errors serialize with stack traces. Lazy evaluation so you can defer expensive operations until they're actually logged.

const authLog = createLogger({ namespace: 'auth' })
authLog.info('Login attempt', { userId: 123 })
// ๐Ÿ’ก INFO [auth] Login attempt { userId: 123 }

PII Redaction

Logging user data is risky. Emails, SSNs, credit cards, JWTs can leak. logfx has built-in patterns and key-based redaction. Opt-in: pass `redact` config. Redaction runs before serialization on structured fields and message text. Customize patterns to reduce false positives (e.g. strict regex for credit cards).

const log = createLogger({
  redact: {
    keys: ['password', 'token', 'apiKey'],
    patterns: ['email', 'ssn', 'creditCard', 'phone', 'ip', 'jwt'],
    customPatterns: [
      { name: 'apiKey', regex: /sk_(live|test)_[a-zA-Z0-9]+/g }
    ]
  },
  transports: [transports.console({ format: 'json' })]
})

log.info('User signup', { email: 'user@example.com', password: 'secret' })
// {"email":"[REDACTED]","password":"[REDACTED]",...}

You can add custom patterns or masking functions. Useful when you need partial redaction for debugging (e.g. last 4 digits of a card).

Production Reliability (Webhook Transport)

When you send logs to a remote endpoint, networks fail. logfx's webhook transport handles that:

  • Retry - Exponential backoff with jitter. Configurable max retries and delay.

  • Circuit breaker - Stops sending after N failures, reopens after a timeout.

  • Dead letter queue - Failed logs go to an in-memory queue. Optionally persist to disk for recovery.

  • Multi-region failover - Multiple URLs with round-robin or priority. Optional health checks.

transports.webhook({
  url: 'https://logs.example.com/ingest',
  retry: { maxRetries: 5, backoff: 'exponential' },
  circuitBreaker: { enabled: true, threshold: 5, timeout: 30000 },
  dlq: { enabled: true, maxSize: 1000, persist: './logs/dlq.json' }
})

You get this without writing retry logic or circuit breaker code. It's built in.

Framework Middleware

Express, Fastify, and Next.js integrations give you `req.log` and request IDs:

import express from 'express'
import { expressLogger } from 'logfx/middleware'

const app = express()
app.use(expressLogger())

app.get('/users', (req, res) => {
  req.log.info('Fetching users', { userId: req.query.id })
  res.json({ users: [] })
})

Output:

๐Ÿ’ก INFO [http] Incoming request { method: 'GET', path: '/users', requestId: 'abc123' }
๐Ÿ’ก INFO [http] Fetching users { userId: '42' }
๐Ÿ’ก INFO [http] Request completed { method: 'GET', path: '/users', status: 200, durationMs: 45 }

Each request gets a unique ID. Status codes set log level (5xx = error, 4xx = warn). Skip health checks or customize ID extraction.

13 Integrations

Separate packages for each platform. Install only what you need:

npm install logfx logfx-datadog
# or logfx-elasticsearch, logfx-sentry, logfx-cloudwatch, etc.
import { createLogger } from 'logfx'
import { datadogTransport } from 'logfx-datadog'

const log = createLogger({
  transports: [
    datadogTransport({
      apiKey: process.env.DD_API_KEY,
      service: 'my-api',
      batchSize: 100,
      flushInterval: 5000
    })
  ]
})

Integrations by category:

Category Package Use Case
APM/Observability logfx-datadog, logfx-elasticsearch, logfx-sentry, logfx-otel Datadog, Elasticsearch, Sentry, OpenTelemetry
Cloud logfx-cloudwatch, logfx-google-cloud, logfx-azure AWS, GCP, Azure
Alerts/SIEM logfx-slack, logfx-loki, logfx-papertrail, logfx-splunk Slack alerts, Grafana Loki, Papertrail, Splunk
Analytics logfx-honeycomb, logfx-logtail Honeycomb, Logtail

Each uses the same Transport interface. Add console, file, or webhook in the same array. Install only what you need. See each package README for full config.

File Transport with Rotation

Write to disk with size-based rotation and compression:

transports.file({
  path: './logs/app.log',
  rotation: {
    maxSize: '10mb',
    maxFiles: 5,
    compress: true
  }
})

Creates `app.log.1`, `app.log.2`, etc. Old files are gzipped. Prevents disk from filling up. Secure rotated files with appropriate permissions (eg: chmod 600 ) and set retention policies for compliance.

Browser Support

Same API in Node and browser. For SPAs, the Beacon transport sends logs reliably even when the user closes the tab:

transports.beacon({
  url: '/api/logs',
  events: { beforeunload: true, visibilitychange: true }
})

Uses the Beacon API (payload size limits apply; batch large logs). Falls back to fetch if unavailable. Non-blocking so it doesn't delay page unload. Works in Edge runtimes (Cloudflare workers, Vercel Edge) without Node polyfills.

When It Helps

  • Node.js APIs - Express/Fastify middleware, structured JSON to Datadog/Elasticsearch, request tracing

  • SPAs - Beacon transport for analytics or error reporting on page close

  • Microservices - Context and request IDs for correlating logs across services

  • Sensitive data - PII redaction before logs hit aggregation

  • High volume - Async buffering, sampling, circuit breaker when the log sink is down

What It Doesn't Do

It's a logger, not a full observability platform. No distributed tracing (though OpenTelemetry integration injects trace IDs). No metrics. No APM. It logs. That's the scope.

Compatibility

Node 18+, ESM and CJS, Edge runtimes (Cloudflare, Vercel), and browsers. ~3KB gzipped. Stress tested: 10k logs in under 1s, 1k with redaction in under 500ms.

FAQ

Why am I still seeing pretty logs in production? Check `NODE_ENV`. logfx auto-detects; if it's not `production`, you get pretty output.

How do I add custom redaction rules? Use `customPatterns` with a regex, or `keys` for specific field names. For partial masking (e.g. last 4 digits), use `maskEmail`, `maskCreditCard`, or `maskPhone` from the redact helpers.

Does this work in serverless cold starts? Yes. Same API in Node, Edge, and browser. No Node-only deps like `fs` or `os` that break Edge.

Try It

npm install logfx
import { log } from 'logfx'

log.info('Hello', { version: '1.0.0' })
log.error('Something failed', new Error('oops'))

Upgrade from v0.5.0

  • Breaking: Redaction API changed. Replace `fields` with `keys`, `replacement` with `censor`. Add `patterns` for built-in PII (email, SSN, credit card, etc.).

  • New: 13 integrations, circuit breaker and DLQ in webhook transport, Beacon transport, Edge runtime support.

  • Compatible: Core API unchanged. Upgrade and add what you need.

I also maintain handlejson (GitHub) for safe JSON parsing, envconfig-kit (GitHub) for env validation, and upstatus (GitHub) for uptime monitoring. All focused Node/TS packages.

Try logfx: npm i logfx and check the examples and full docs on GitHub. If you run into anything, report issues here Github . If you found this helpful, star the repo and share your feedback, it really helps.