@forgedock/fd-fastest-logger
v0.1.0
Published
Structured logging utilities for FD-Fastest
Maintainers
Readme
@fd-fastest/logger
Production-grade structured logging for the Fastest toolkit, powered by Pino.
Features
- ✅ Ultra-Fast – Pino is one of the fastest loggers for Node.js
- ✅ Structured Logging – JSON output for easy parsing
- ✅ Multiple Levels – trace, debug, info, warn, error, fatal
- ✅ Pretty Printing – Human-readable output for development
- ✅ Child Loggers – Add context to specific modules
- ✅ Custom Serializers – Handle errors, requests, responses
- ✅ Redaction – Remove sensitive data automatically
- ✅ TypeScript – Full type safety
Installation
pnpm add @fd-fastest/loggerQuick Start
Basic Usage
import { createLogger } from '@fd-fastest/logger';
const logger = createLogger({ level: 'info' });
logger.info('Application started');
logger.error(new Error('Something went wrong'));Development vs Production
// Development (pretty printing)
const devLogger = createLogger({
level: 'debug',
pretty: true,
});
// Production (JSON output)
const prodLogger = createLogger({
level: 'info',
pretty: false,
});Child Loggers with Context
const logger = createLogger();
// Create child with module context
const moduleLogger = logger.child({ module: 'auth' });
moduleLogger.info('User logged in', { userId: '123' });
// Create child with request context
const requestLogger = logger.child({ requestId: 'abc-123' });
requestLogger.info('Processing request');Logging Errors
import { NetworkError } from '@fd-fastest/errors';
try {
await fetchData();
} catch (error) {
// Automatically serializes error details
logger.error(error as Error, 'Failed to fetch data');
}File Output
const logger = createLogger({
destination: {
type: 'file',
path: './logs/app.log',
sync: false,
},
});Multiple Outputs
import {
createLogger,
createProductionMultiTransport,
} from '@fd-fastest/logger';
const logger = createLogger({
pinoOptions: {
transport: createProductionMultiTransport('./logs'),
},
});API Reference
createLogger(options)
Create a new logger instance.
interface LoggerOptions {
name?: string; // Logger name
level?: LogLevel; // Min log level (default: 'info')
pretty?: boolean; // Pretty print (default: dev mode)
base?: Record<string, unknown>; // Base fields
serializers?: Record<string, (value: unknown) => unknown>; // Custom serializers
redact?: string[]; // Fields to redact
destination?:
| { type: 'stdout' | 'stderr'; sync?: boolean }
| { type: 'file'; path?: string; sync?: boolean; mkdir?: boolean }
| {
type: 'multistream';
targets: TransportConfig['targets'];
dedupe?: boolean;
};
pinoOptions?: Partial<PinoLoggerOptions>; // Native pino overrides
}Log Levels
logger.trace('Detailed debugging'); // Level 10
logger.debug('Debug information'); // Level 20
logger.info('General information'); // Level 30
logger.warn('Warning message'); // Level 40
logger.error('Error occurred'); // Level 50
logger.fatal('Fatal error'); // Level 60Logging Patterns
// String message
logger.info('User logged in');
// With context object
logger.info({ userId: '123', action: 'login' }, 'User logged in');
// Error logging
logger.error(error, 'Operation failed');
// Performance timing
logger.info({ timing: { start: t0, end: t1 } }, 'Operation completed');Serializers
Custom serializers transform objects before logging:
import {
errorSerializer,
requestSerializer,
responseSerializer,
} from '@fd-fastest/logger';
const logger = createLogger({
serializers: {
error: errorSerializer, // Errors with full details
req: requestSerializer, // HTTP requests (redacted)
res: responseSerializer, // HTTP responses
custom: (value) => value,
},
});Redaction
Remove sensitive data from logs:
const logger = createLogger({
redact: [
'password',
'apiKey',
'creditCard',
'req.headers.authorization',
'req.headers.cookie',
],
});
logger.info({
username: 'john',
password: 'secret123', // Will be removed
apiKey: 'key456', // Will be removed
});Best Practices
1. Use Appropriate Log Levels
logger.trace('Entering function', { args }); // Very detailed
logger.debug('Processing step', { step }); // Debugging
logger.info('User action', { userId }); // General info
logger.warn('Slow query', { duration }); // Warnings
logger.error(error, 'Operation failed'); // Errors
logger.fatal(error, 'System crash'); // Fatal errors2. Add Context with Child Loggers
// ❌ Bad: Repeating context
logger.info({ module: 'auth', userId: '123' }, 'Started');
logger.info({ module: 'auth', userId: '123' }, 'Completed');
// ✅ Good: Child logger with context
const authLogger = logger.child({ module: 'auth', userId: '123' });
authLogger.info('Started');
authLogger.info('Completed');3. Log Objects, Not Strings
// ❌ Bad: String concatenation
logger.info(`User ${userId} logged in at ${timestamp}`);
// ✅ Good: Structured data
logger.info({ userId, timestamp }, 'User logged in');4. Use Serializers for Complex Objects
// ❌ Bad: Manual serialization
logger.error(
{
errorMessage: error.message,
errorStack: error.stack,
},
'Error occurred'
);
// ✅ Good: Let serializer handle it
logger.error(error, 'Error occurred');5. Performance Logging
const start = Date.now();
try {
await operation();
logger.info(
{ duration: Date.now() - start },
'Operation completed successfully'
);
} catch (error) {
logger.error(
{ err: error as Error, duration: Date.now() - start },
'Operation failed'
);
}Production Configuration
Recommended Setup
import {
createLogger,
createProductionMultiTransport,
type LogLevel,
} from '@fd-fastest/logger';
export const logger = createLogger({
name: 'fastest-cli',
level: (process.env.LOG_LEVEL as LogLevel) || 'info',
pretty: process.env.NODE_ENV === 'development',
base: {
env: process.env.NODE_ENV,
version: process.env.APP_VERSION,
},
redact: [
'password',
'apiKey',
'token',
'*.password',
'*.apiKey',
'req.headers.authorization',
'req.headers.cookie',
],
pinoOptions:
process.env.NODE_ENV === 'production'
? {
transport: createProductionMultiTransport('./logs'),
}
: undefined,
});Environment Variables
# Set log level
export LOG_LEVEL=debug
# Enable pretty printing
export NODE_ENV=developmentPerformance
Pino is designed for performance:
- Minimal overhead: ~10-20% slower than console.log
- Asynchronous: Non-blocking I/O by default
- Worker threads: Pretty printing in separate thread
- JSON.stringify optimization: Custom serialization
// Benchmark results (operations/sec)
console.log: ~200,000
logger.info: ~180,000 // Structured output with minimal overheadLicense
MIT
