Formatters
Formatters control how log entries are serialized to strings. @sylphx/cat includes JSON and Pretty formatters, plus support for custom formatters.
Built-in Formatters
JSON Formatter
Outputs logs as newline-delimited JSON (NDJSON):
typescript
import { createLogger, jsonFormatter } from '@sylphx/cat'
const logger = createLogger({
formatter: jsonFormatter()
})
logger.info('User login', { userId: '123' })
// {"level":"info","time":1699564800000,"msg":"User login","userId":"123"}Best for:
- Production environments
- Log aggregation systems
- Machine parsing
- Structured log analysis
Options:
typescript
jsonFormatter({
// No options currently
// Pure JSON output for maximum compatibility
})Output format:
json
{
"level": "info",
"time": 1699564800000,
"msg": "Message text",
...data
}Pretty Formatter
Human-readable colored output for development:
typescript
import { createLogger, prettyFormatter } from '@sylphx/cat'
const logger = createLogger({
formatter: prettyFormatter({
colors: true,
timestamp: true,
timestampFormat: 'iso' // 'iso' | 'unix' | 'relative'
})
})
logger.info('Server started', { port: 3000 })
// 2024-01-01T12:00:00.000Z INF Server started {"port":3000}Best for:
- Development environments
- Local debugging
- Console output
- Human readability
Options:
typescript
prettyFormatter({
// Enable ANSI colors
colors?: boolean // default: true
// Show timestamp
timestamp?: boolean // default: true
// Timestamp format
timestampFormat?: 'iso' | 'unix' | 'relative' // default: 'iso'
// Custom colors per level
levelColors?: {
trace?: string
debug?: string
info?: string
warn?: string
error?: string
fatal?: string
}
})Output format:
[timestamp] LEVEL Message {data}Example with colors:
typescript
const logger = createLogger({
formatter: prettyFormatter({
colors: true,
timestampFormat: 'iso'
})
})
logger.trace('Trace message') // Gray
logger.debug('Debug message') // Cyan
logger.info('Info message') // Green
logger.warn('Warning message') // Yellow
logger.error('Error message') // Red
logger.fatal('Fatal message') // MagentaTimestamp formats:
typescript
// ISO 8601
timestampFormat: 'iso'
// Output: 2024-01-01T12:00:00.000Z
// Unix timestamp (milliseconds)
timestampFormat: 'unix'
// Output: 1699564800000
// Relative time (since process start)
timestampFormat: 'relative'
// Output: +1234msCustom Formatters
Create your own formatter by implementing the Formatter interface:
typescript
import type { Formatter, LogEntry } from '@sylphx/cat'
class CustomFormatter implements Formatter {
format(entry: LogEntry): string {
return `[${entry.level.toUpperCase()}] ${entry.message}`
}
}
const logger = createLogger({
formatter: new CustomFormatter()
})
logger.info('Hello')
// [INFO] HelloLogEntry Structure
The LogEntry object passed to formatters:
typescript
interface LogEntry {
level: LogLevel // 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'
timestamp: number // Unix timestamp in milliseconds
message: string // Log message
data?: Record<string, unknown> // Structured data
}Examples
CSV Formatter
typescript
class CSVFormatter implements Formatter {
format(entry: LogEntry): string {
const { timestamp, level, message, data } = entry
const dataStr = data ? JSON.stringify(data) : ''
return `${timestamp},${level},${message},"${dataStr}"`
}
}
const logger = createLogger({
formatter: new CSVFormatter()
})
logger.info('User login', { userId: '123' })
// 1699564800000,info,User login,"{"userId":"123"}"Syslog Formatter
typescript
class SyslogFormatter implements Formatter {
private hostname = require('os').hostname()
private appName = 'myapp'
format(entry: LogEntry): string {
const priority = this.getPriority(entry.level)
const timestamp = new Date(entry.timestamp).toISOString()
const msg = `${entry.message} ${JSON.stringify(entry.data || {})}`
return `<${priority}>1 ${timestamp} ${this.hostname} ${this.appName} - - - ${msg}`
}
private getPriority(level: string): number {
const levelMap = {
fatal: 2, error: 3, warn: 4, info: 6, debug: 7, trace: 7
}
return levelMap[level] || 6
}
}Colored JSON Formatter
typescript
import chalk from 'chalk'
class ColoredJSONFormatter implements Formatter {
format(entry: LogEntry): string {
const json = JSON.stringify({
level: entry.level,
time: entry.timestamp,
msg: entry.message,
...entry.data
}, null, 2)
// Colorize based on level
switch (entry.level) {
case 'error':
case 'fatal':
return chalk.red(json)
case 'warn':
return chalk.yellow(json)
case 'info':
return chalk.green(json)
default:
return chalk.gray(json)
}
}
}Template Formatter
typescript
class TemplateFormatter implements Formatter {
constructor(private template: string) {}
format(entry: LogEntry): string {
return this.template
.replace('{timestamp}', new Date(entry.timestamp).toISOString())
.replace('{level}', entry.level.toUpperCase())
.replace('{message}', entry.message)
.replace('{data}', JSON.stringify(entry.data || {}))
}
}
const logger = createLogger({
formatter: new TemplateFormatter('[{timestamp}] {level}: {message} {data}')
})
logger.info('Test', { foo: 'bar' })
// [2024-01-01T12:00:00.000Z] INFO: Test {"foo":"bar"}Compact Formatter
typescript
class CompactFormatter implements Formatter {
format(entry: LogEntry): string {
const parts = [
entry.level[0].toUpperCase(), // First letter of level
entry.message
]
if (entry.data && Object.keys(entry.data).length > 0) {
parts.push(JSON.stringify(entry.data))
}
return parts.join(' ')
}
}
logger.info('Server started', { port: 3000 })
// I Server started {"port":3000}Environment-Based Formatting
Choose formatter based on environment:
typescript
import { createLogger, jsonFormatter, prettyFormatter } from '@sylphx/cat'
const isDev = process.env.NODE_ENV === 'development'
const logger = createLogger({
formatter: isDev
? prettyFormatter({ colors: true })
: jsonFormatter()
})Performance Considerations
JSON is Fastest
JSON formatter is optimized for performance:
typescript
// Fastest - minimal processing
const logger = createLogger({
formatter: jsonFormatter()
})Pretty Adds Overhead
Pretty formatter adds color codes and formatting:
typescript
// Slower - ANSI color codes, timestamp formatting
const logger = createLogger({
formatter: prettyFormatter({ colors: true })
})Recommendation:
- Use JSON in production
- Use Pretty in development
- Disable colors in CI/CD (
colors: false)
Custom Formatter Optimization
typescript
class OptimizedFormatter implements Formatter {
format(entry: LogEntry): string {
// ✅ Pre-allocate string builder
const parts: string[] = []
parts.push('[', entry.level, '] ', entry.message)
if (entry.data) {
parts.push(' ', JSON.stringify(entry.data))
}
return parts.join('')
}
}
// ❌ Avoid string concatenation in loops
class SlowFormatter implements Formatter {
format(entry: LogEntry): string {
let result = '[' + entry.level + '] ' + entry.message
if (entry.data) {
result += ' ' + JSON.stringify(entry.data)
}
return result
}
}Testing Formatters
typescript
import { jsonFormatter } from '@sylphx/cat'
describe('Formatter', () => {
it('formats log entry', () => {
const formatter = jsonFormatter()
const entry = {
level: 'info' as const,
timestamp: 1699564800000,
message: 'Test',
data: { key: 'value' }
}
const result = formatter.format(entry)
const parsed = JSON.parse(result)
expect(parsed.level).toBe('info')
expect(parsed.msg).toBe('Test')
expect(parsed.key).toBe('value')
})
})See Also
- Loggers - Logger creation and usage
- Transports - Log destinations
- API Reference - Complete formatter API