Performance
@sylphx/cat is optimized for extreme performance with 25M+ operations per second. This guide covers benchmarks, optimizations, and best practices.
Benchmarks
Tested on Apple M1 Pro, Node.js 20:
| Operation | ops/sec | Description |
|---|---|---|
| Baseline (empty function) | 1,234M | Control baseline |
| Filtered log (below level) | 234M | Fast-path level check |
| Basic log (console) | 21M | Simple message only |
| Structured log (console) | 18M | Message + data object |
| JSON formatter | 19M | JSON serialization |
| Pretty formatter | 15M | Colored output |
| File transport | 12M | Write to file |
| OTLP transport (batch) | 8M | Network + batching |
vs. Competitors
| Logger | Basic Log | Structured Log | Bundle Size |
|---|---|---|---|
| @sylphx/cat | 21M ops/s | 18M ops/s | 8.93 KB |
| Pino | 15M ops/s | 12M ops/s | 11 KB |
| Winston | 5M ops/s | 4M ops/s | 80 KB |
| console.log | 25M ops/s | 20M ops/s | 0 KB (built-in) |
Optimizations
1. Fast-Path Level Filtering
Logs below the minimum level are filtered before any processing:
const logger = createLogger({ level: 'info' })
// Very fast - ~234M ops/sec (just a comparison)
logger.debug('This is filtered') // No formatter, transport, or allocation
// Slower - ~21M ops/sec (full processing)
logger.info('This is processed')Tip: Set appropriate log levels in production:
const logger = createLogger({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug'
})2. Minimal Object Allocation
Avoid creating unnecessary objects:
// ✅ Good - no intermediate objects
logger.info('User login', { userId: '123', action: 'login' })
// ❌ Bad - creates temporary string
logger.info('User login: ' + JSON.stringify({ userId: '123' }))
// ❌ Bad - creates intermediate object
const data = { userId: '123' }
const enriched = { ...data, timestamp: Date.now() }
logger.info('User login', enriched)3. Batching
Buffer logs for high-throughput scenarios:
const logger = createLogger({
batch: true,
batchSize: 100, // Flush after 100 logs
batchInterval: 1000 // Or every 1 second
})
// Fast - logs are buffered
for (let i = 0; i < 100000; i++) {
logger.info(`Event ${i}`)
}
await logger.flush() // Single writePerformance gain: 2-5x faster for high volume
4. Avoid Expensive Computations
Only compute when necessary:
// ❌ Bad - always computes
logger.debug('Users: ' + JSON.stringify(users))
// ✅ Good - only computes if debug is enabled
if (logger.isLevelEnabled('debug')) {
logger.debug('Users: ' + JSON.stringify(users))
}
// ✅ Better - lazy evaluation (if supported)
logger.debug(() => `Users: ${JSON.stringify(users)}`)5. Use JSON Formatter in Production
JSON formatter is faster than Pretty formatter:
// ✅ Fast - minimal processing
formatter: jsonFormatter()
// ❌ Slower - color codes, timestamp formatting
formatter: prettyFormatter({ colors: true })Performance difference: ~20-30%
6. Disable Features You Don't Need
// ✅ Minimal - just logging
const logger = createLogger({
formatter: jsonFormatter(),
transports: [consoleTransport()]
})
// ❌ Heavy - unnecessary features
const logger = createLogger({
formatter: prettyFormatter({ colors: true }),
transports: [consoleTransport(), fileTransport(), otlpTransport()],
plugins: [contextPlugin(), tracingPlugin(), redactionPlugin()]
})Memory Usage
Logger Instance
Single logger instance:
- ~100 bytes
With context:
- ~100 bytes + context size
Child loggers share parent context (no duplication).
Batching
Batch mode buffers logs in memory:
batch: true,
batchSize: 100 // ~10 KB (100 logs × ~100 bytes each)Tip: Adjust batch size based on memory constraints
Tail-Based Sampling
Buffers traces in memory:
maxBufferSize: 1000 // ~10 MB (1000 traces × ~10 KB each)Tip: Limit buffer size on memory-constrained environments
CPU Usage
Logging overhead under load:
| Scenario | CPU Overhead |
|---|---|
| Filtered logs | <0.1% |
| Basic logging | ~1-2% |
| Structured logging | ~2-3% |
| With plugins | ~3-5% |
| OTLP transport | ~4-6% |
| Tail-based sampling | ~5-7% |
Tip: Use batching and sampling to reduce overhead
Benchmark Your App
import { performance } from 'node:perf_hooks'
const logger = createLogger()
const iterations = 1000000
const start = performance.now()
for (let i = 0; i < iterations; i++) {
logger.info('Benchmark', { index: i })
}
await logger.flush()
const end = performance.now()
const duration = end - start
const opsPerSec = iterations / (duration / 1000)
console.log(`Duration: ${duration.toFixed(2)}ms`)
console.log(`Throughput: ${(opsPerSec / 1000000).toFixed(2)}M ops/sec`)Production Optimization
Environment-Based Configuration
const isProd = process.env.NODE_ENV === 'production'
const logger = createLogger({
level: isProd ? 'info' : 'debug',
formatter: isProd ? jsonFormatter() : prettyFormatter({ colors: true }),
transports: [
consoleTransport(),
...(isProd ? [
otlpTransport({
endpoint: process.env.OTLP_ENDPOINT,
batch: true,
batchSize: 100,
compression: 'gzip'
})
] : [])
],
plugins: [
...(isProd ? [
tracingPlugin(),
redactionPlugin(),
tailSamplingPlugin({ adaptive: true })
] : [])
]
})High-Throughput Setup
const logger = createLogger({
level: 'info', // Filter debug logs
formatter: jsonFormatter(), // Fast formatter
transports: [
otlpTransport({
batch: true,
batchSize: 500, // Large batches
batchInterval: 5000, // Less frequent flushes
compression: 'gzip' // Reduce network
})
],
plugins: [
tailSamplingPlugin({ // Reduce volume
adaptive: true,
monthlyBudget: 50 * 1024 * 1024 * 1024
})
]
})Best Practices
1. Set Appropriate Log Levels
// ✅ Production
level: 'info'
// ✅ Development
level: 'debug'
// ❌ Too verbose
level: 'trace'2. Use Sampling
// ✅ High traffic
tailSamplingPlugin({ sampleRate: 0.01 }) // 1%
// ❌ No sampling
// Every log processed and sent3. Batch Writes
// ✅ High throughput
batch: true,
batchSize: 100
// ❌ Immediate writes
batch: false4. Avoid String Concatenation
// ✅ Good
logger.info('User login', { userId, username })
// ❌ Bad
logger.info(`User ${userId} (${username}) logged in`)5. Minimize Plugin Chain
// ✅ Only what you need
plugins: [tracingPlugin(), redactionPlugin()]
// ❌ Excessive plugins
plugins: [plugin1(), plugin2(), plugin3(), plugin4(), plugin5()]Profiling
Use Node.js profiler to identify bottlenecks:
node --prof app.js
node --prof-process isolate-*.log > profile.txtOr use Chrome DevTools:
node --inspect app.js
# Open chrome://inspectSee Also
- Best Practices - Production patterns
- Tail-Based Sampling - Cost optimization
- API Reference - Complete API