better-logger

console.log is broken. better.log fixes it.

npm install @better-logger/core
0
Dependencies
3.8KB
Gzipped
280
Tests
96%
Coverage
7
Integrations

โŒ console.log

console.log('start')
console.log('user', user)
console.log('after db')
console.log('payment', payment)
console.log('done')

Flat noise. No timing. No context. No structure.

โœ… better.log

better.log('start')
better.log('user', user)
better.log('after db')
better.log('payment', payment)
better.log('done')

Same code. Structured output with automatic timing.

๐Ÿ“ค What You Get

๐Ÿš€ [flow:default] (tid: abc123)
  โ†’ start
  โœ“ start (12ms)
  โ†’ user
    data: { email: "test@example.com" }
  โœ“ user (45ms)
  โ†’ after db
  โœ“ after db (210ms)
  โ†’ payment
    data: { amount: 99.99 }
  โœ“ payment (183ms)
  โ†’ done
  โœ“ done (0ms)
๐Ÿ [flow:default] success (450ms)

๐ŸŽฏ Features

โšก Drop-in Replacement

Replace console.log with better.log. Zero config. Works immediately. No breaking changes.

๐Ÿ• Automatic Timing

Every operation timed automatically. See exactly how long each step and flow took. No manual Date.now() calls.

๐ŸŒณ Hierarchical Output

Rapid calls auto-group into flows. Nested structures show parent-child relationships with visual indentation.

๐ŸŽฏ Context Propagation

Shared context propagates across all steps and child flows. Set once, access everywhere. No manual binding.

๐Ÿ“ฆ Zero Dependencies

3.8KB gzipped. Nothing to install. Nothing to break. Works in Node.js 18+, modern browsers, and Edge runtimes.

๐Ÿ”’ Production-ready

File, stream, HTTP transports. PII redaction. Async mode. 280 tests. 96% coverage. Framework integrations.

๐Ÿท Severity Tagging

better.log.warn() and better.log.error() auto-tag flows with severity for filtering.

๐Ÿ”Œ Extensible

Custom renderers, custom clocks, custom ID generators. Express, Fastify, Next.js, React integrations included.

๐Ÿš€ Getting Started

1. Install

npm install @better-logger/core

2. Import

import { better } from '@better-logger/core'
// or
const { better } = require('@better-logger/core')

3. Use

// Drop-in for console.log
better.log('Server started on port 3000')
better.log('User created', { email: 'test@example.com' })
better.log.warn('Slow query detected', { duration: 250 })
better.log.error('Database connection failed', error)

4. That's It

No configuration needed. Your logs are now structured, timed, and grouped automatically.

๐Ÿ“– API Reference

Core API

Method Description Example
better.log(msg, data) Drop-in for console.log better.log('User created', { email })
better.log.info(msg, data) Info-level (auto-tags) better.log.info('Server started')
better.log.warn(msg, data) Warning (auto-tags [warn]) better.log.warn('Slow query', { duration })
better.log.error(msg, error) Error (fails flow, tags [error]) better.log.error('Failed', error)
better.flow(name, opts) Create explicit flow better.flow('checkout', { tags: ['payment'] })
better.setEnabled(bool) Global toggle better.setEnabled(false)
better.subscribe(fn) Flow completion listener better.subscribe(flow => console.log(flow.name))
better.toJSON(flow) Export flow as JSON const json = better.toJSON(flow)

Flow Methods

Method Description
flow.step(name, data) Create a step with optional data
step.success() Mark step as successful
step.fail(error) Mark step as failed
flow.run(name, fn) Async step helper โ€” auto-completes on resolve/reject
flow.setContext(ctx) Set shared context (merged)
flow.getContext() Get current context copy
flow.child(name) Create nested child flow
flow.success() Complete flow successfully
flow.fail(error) Fail flow with error

Configuration

Method Default Description
better.setIdleTimeout(ms) 100 Auto-complete timeout for implicit flows
better.setFlowName(name) 'default' Default flow name for implicit flows
better.setDefaultTags(tags) [] Tags applied to all implicit flows
better.log.flush() โ€” Force complete current implicit flow

Explicit Flow Example

Complex multi-step operation
const flow = better.flow('user-checkout', {
  tags: ['ecommerce', 'payment'],
  initialContext: { userId: 'user-123', cartId: 'cart-456' }
})

flow.setContext({ orderId: 'order-789' })

// Async step with auto-complete
const result = await flow.run('charge-card', async () => {
  return await paymentGateway.charge({ amount: 99.99 })
})

// Manual step
const notifyStep = flow.step('send-confirmation')
await sendEmail(user.email, 'Order confirmed')
notifyStep.success()

flow.success()

๐Ÿ”Œ Framework Integrations

Official integrations for popular frameworks. Auto-instrument your app with zero configuration.

Express.js

const express = require('express')
const { betterLogger, betterErrorHandler } = require('@better-logger/express')

const app = express()
app.use(betterLogger({ logBody: false, redactFields: ['password'] }))
app.use(betterErrorHandler)

app.post('/api/users', async (req, res) => {
  // Each request is auto-logged with timing
  const user = await createUser(req.body)
  res.json(user)
})

Fastify

const fastify = require('fastify')()
await fastify.register(require('@better-logger/fastify'))

Next.js

// next.config.js
const withBetter = require('@better-logger/nextjs')
module.exports = withBetter({ /* your config */ })

// pages/api/users.js
import { withBetterLoggerAPI } from '@better-logger/nextjs'
export default withBetterLoggerAPI(async (req, res) => {
  // Auto-logged
})

React

import { useComponentLogger, useAsyncLogger, BetterErrorBoundary } from '@better-logger/react'

function UserProfile({ userId }) {
  const logger = useComponentLogger('UserProfile')
  const fetchData = useAsyncLogger('fetch-user')

  useEffect(() => {
    logger.mount({ userId })
    return () => logger.unmount({ userId })
  }, [userId])

  const loadUser = () => fetchData(() => api.getUser(userId))
}

CLI Tools

const { command, step, progress, table } = require('@better-logger/cli')

await command('build', async () => {
  await step('compile', async () => { /* ... */ })
  await step('bundle', async () => { /* ... */ })
  progress(50, 100, 'bundling...')
})

๐Ÿšš Transports

Write logs anywhere โ€” files, streams, HTTP endpoints. Zero external dependencies.

File Transport

better.log.toFile('app.log', { append: true })
better.log.toFile('error.log', { append: true })

Stream Transport

better.log.toStream(process.stdout)
better.log.toStream(fs.createWriteStream('logs.json'))

HTTP Transport

better.log.toHttp('https://api.example.com/logs', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer token' }
})

Multiple Transports

better.log.toFile('app.log')
better.log.toHttp('https://logs.example.com')
// All logs go to both destinations

๐Ÿ”’ Production Features

PII Redaction

better.log.redact(['password', 'ssn', 'creditCard'])

better.log('User login', { username: 'alice', password: 'secret', ssn: '123-45-6789' })
// Output: { username: "alice", password: "[REDACTED]", ssn: "[REDACTED]" }

better.log.redact([]) // Clear redaction

Async Logging

// Non-blocking buffered logging for high-throughput
better.log.async(true)

better.log('msg1')
better.log('msg2')
better.log('msg3')
// All buffered

better.log.flush()
// All processed at once

better.log.async(false) // Disable auto-flushes remaining

Silent Mode

// Globally disable all logging
better.setEnabled(false)

// Per-flow silent mode
const flow = better.flow('health-check', { enabled: false })
// No output, near-zero overhead

๐Ÿ”„ Migration Guides

From console.log

// Before
console.log('User created:', user)
console.error('Payment failed:', error)

// After โ€” just add "better."
better.log('User created:', user)
better.log.error('Payment failed:', error)

From Pino

// Before (pino)
const logger = pino({ transport: { target: 'pino/file', options: { destination: 'app.log' } } })
logger.info('User created', { email })

// After (better-logger)
const { better } = require('@better-logger/core')
better.log.toFile('app.log')
better.log.info('User created', { email })

From Winston

// Before (winston)
const logger = winston.createLogger({
  transports: [
    new winston.transports.File({ filename: 'app.log' }),
    new winston.transports.Http({ url: 'https://logs.example.com' })
  ]
})
logger.info('User created', { email })

// After (better-logger)
better.log.toFile('app.log')
better.log.toHttp('https://logs.example.com')
better.log.info('User created', { email })

See full guides: Migrate from Pino ยท Migrate from Winston

๐Ÿ“Š Why better-logger?

Feature console.log pino winston better.log
Drop-in replacement โ€” โŒ โŒ โœ…
Auto-flow grouping โŒ โŒ โŒ โœ…
Automatic timing โŒ Manual Manual โœ…
Context propagation โŒ Manual Manual โœ…
Hierarchical output โŒ โŒ โŒ โœ…
Zero dependencies โœ… โŒ โŒ โœ…
Bundle size 0KB 30KB 100KB 3.8KB
TypeScript support โŒ โœ… โœ… โœ…
Browser support โœ… โŒ โŒ โœ…
Auto-error tracking โŒ โŒ โŒ โœ