Skip to content

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:

Operationops/secDescription
Baseline (empty function)1,234MControl baseline
Filtered log (below level)234MFast-path level check
Basic log (console)21MSimple message only
Structured log (console)18MMessage + data object
JSON formatter19MJSON serialization
Pretty formatter15MColored output
File transport12MWrite to file
OTLP transport (batch)8MNetwork + batching

vs. Competitors

LoggerBasic LogStructured LogBundle Size
@sylphx/cat21M ops/s18M ops/s8.93 KB
Pino15M ops/s12M ops/s11 KB
Winston5M ops/s4M ops/s80 KB
console.log25M ops/s20M ops/s0 KB (built-in)

Optimizations

1. Fast-Path Level Filtering

Logs below the minimum level are filtered before any processing:

typescript
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:

typescript
const logger = createLogger({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug'
})

2. Minimal Object Allocation

Avoid creating unnecessary objects:

typescript
// ✅ 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:

typescript
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 write

Performance gain: 2-5x faster for high volume

4. Avoid Expensive Computations

Only compute when necessary:

typescript
// ❌ 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:

typescript
// ✅ 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

typescript
// ✅ 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:

typescript
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:

typescript
maxBufferSize: 1000 // ~10 MB (1000 traces × ~10 KB each)

Tip: Limit buffer size on memory-constrained environments

CPU Usage

Logging overhead under load:

ScenarioCPU 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

typescript
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

typescript
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

typescript
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

typescript
// ✅ Production
level: 'info'

// ✅ Development
level: 'debug'

// ❌ Too verbose
level: 'trace'

2. Use Sampling

typescript
// ✅ High traffic
tailSamplingPlugin({ sampleRate: 0.01 }) // 1%

// ❌ No sampling
// Every log processed and sent

3. Batch Writes

typescript
// ✅ High throughput
batch: true,
batchSize: 100

// ❌ Immediate writes
batch: false

4. Avoid String Concatenation

typescript
// ✅ Good
logger.info('User login', { userId, username })

// ❌ Bad
logger.info(`User ${userId} (${username}) logged in`)

5. Minimize Plugin Chain

typescript
// ✅ Only what you need
plugins: [tracingPlugin(), redactionPlugin()]

// ❌ Excessive plugins
plugins: [plugin1(), plugin2(), plugin3(), plugin4(), plugin5()]

Profiling

Use Node.js profiler to identify bottlenecks:

bash
node --prof app.js
node --prof-process isolate-*.log > profile.txt

Or use Chrome DevTools:

bash
node --inspect app.js
# Open chrome://inspect

See Also

Released under the MIT License.