console.log is broken. better.log fixes it.
npm install @better-logger/core
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('start')
better.log('user', user)
better.log('after db')
better.log('payment', payment)
better.log('done')
Same code. Structured output with automatic timing.
Replace console.log with better.log. Zero config. Works immediately. No breaking
changes.
Every operation timed automatically. See exactly how long each step and flow took. No manual Date.now() calls.
Rapid calls auto-group into flows. Nested structures show parent-child relationships with visual indentation.
Shared context propagates across all steps and child flows. Set once, access everywhere. No manual binding.
3.8KB gzipped. Nothing to install. Nothing to break. Works in Node.js 18+, modern browsers, and Edge runtimes.
File, stream, HTTP transports. PII redaction. Async mode. 280 tests. 96% coverage. Framework integrations.
better.log.warn() and better.log.error() auto-tag flows with severity for
filtering.
Custom renderers, custom clocks, custom ID generators. Express, Fastify, Next.js, React integrations included.
npm install @better-logger/core
import { better } from '@better-logger/core'
// or
const { better } = require('@better-logger/core')
// 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)
No configuration needed. Your logs are now structured, timed, and grouped automatically.
| 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) |
| 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 |
| 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 |
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()
Official integrations for popular frameworks. Auto-instrument your app with zero configuration.
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)
})
const fastify = require('fastify')()
await fastify.register(require('@better-logger/fastify'))
// 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
})
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))
}
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...')
})
Write logs anywhere โ files, streams, HTTP endpoints. Zero external dependencies.
better.log.toFile('app.log', { append: true })
better.log.toFile('error.log', { append: true })
better.log.toStream(process.stdout)
better.log.toStream(fs.createWriteStream('logs.json'))
better.log.toHttp('https://api.example.com/logs', {
method: 'POST',
headers: { 'Authorization': 'Bearer token' }
})
better.log.toFile('app.log')
better.log.toHttp('https://logs.example.com')
// All logs go to both destinations
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
// 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
// Globally disable all logging
better.setEnabled(false)
// Per-flow silent mode
const flow = better.flow('health-check', { enabled: false })
// No output, near-zero overhead
// 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)
// 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 })
// 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
| 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 | โ | โ | โ | โ |