Documentation Index
Fetch the complete documentation index at: https://www.orionjs.com/llms.txt
Use this file to discover all available pages before exploring further.
The Orion.js Logger is a powerful, flexible logging utility built on top of Winston. It provides structured logging capabilities with customizable formats, transports, and context tracking.
Installation
pnpm add @orion-js/logger
Basic Usage
import {logger} from '@orion-js/logger'
// Basic logging with different levels
logger.debug('Debug message', {customData: 'value'})
logger.info('Info message', {userId: 123})
logger.warn('Warning message', {attemptCount: 3})
logger.error('Error occurred', {error: new Error('Something went wrong')})
// Add metadata to all subsequent logs from this logger instance
const userLogger = logger.addMetadata({userId: 'user-123'})
userLogger.info('User action', {action: 'login'})
// Add context based on the current module
const contextLogger = logger.addContext(module)
contextLogger.info('Operation completed', {result: 'success'})
Critical Logging Pattern
The first argument must always be a static string. All variables should be passed in the object as the second argument.
// CORRECT - Static string first, variables in object
logger.info('User created successfully', {userId: user._id, email: user.email})
logger.warn('Rate limit approaching', {currentRate: rate, limit: maxRate})
// INCORRECT - Variables in the message string
logger.info(`User ${userId} created`) // DON'T DO THIS
logger.info('User ' + userId + ' created') // DON'T DO THIS
This pattern is required because:
- Log aggregation tools can group logs by the static message
- Searching logs by message becomes predictable
- Variable data is properly indexed and queryable
Error Logging Pattern
When logging errors, the error object must be in a parameter named error in the second argument object:
try {
await performOperation()
} catch (error) {
// CORRECT - Error object in the 'error' property
logger.error('Operation failed', {error, userId, operationType})
// INCORRECT - Error as message or wrong property name
logger.error(error.message) // DON'T DO THIS
logger.error('Operation failed', {err: error}) // DON'T DO THIS
logger.error('Operation failed: ' + error.message) // DON'T DO THIS
}
This ensures:
- Stack traces are properly captured
- Error serialization works correctly
- Log analysis tools can extract error details
Features
Log Levels
The logger supports the standard log levels:
error: For error conditions
warn: For warning conditions
info: For informational messages
debug: For debugging information
Automatic Contextual Information
Each log message automatically includes:
- Timestamp
- Log level
- File name where the log was triggered (automatically detected)
- OpenTelemetry trace and span IDs (when available)
The logger supports two main output formats:
-
Text Format (for development):
- Colorized output
- Human-readable formatting
- Activated when
ORION_DEV=1 environment variable is set
-
JSON Format (for production):
- Structured JSON logs
- Ideal for log aggregation and analysis
- Default in production environments
OpenTelemetry Integration
The logger automatically detects and includes OpenTelemetry trace and span IDs when available, enabling correlation between logs and traces.
Advanced Configuration
Setting Log Level
import {setLogLevel} from '@orion-js/logger'
// Set the minimum log level to display
setLogLevel('info') // Will show info, warn, and error logs, but not debug
Adding Custom Transports
import {addTransport} from '@orion-js/logger'
import {transports} from 'winston'
// Add a file transport
const fileTransport = new transports.File({
filename: 'application.log',
level: 'info'
})
addTransport(fileTransport)
Complete Logger Configuration
import {configureLogger} from '@orion-js/logger'
import {format, transports} from 'winston'
configureLogger({
level: 'debug',
format: format.combine(
format.timestamp(),
format.json()
),
transports: [
new transports.Console(),
new transports.File({filename: 'combined.log'})
]
})
Best Practices
-
Use Static Message Strings: The first argument must be a static string for proper log aggregation:
// Good
logger.info('User created', {userId: '123', email: 'user@example.com'})
// Avoid
logger.info(`User ${userId} created with email ${email}`)
-
Pass Error Objects Correctly: Use the
error property name:
try {
await someOperation()
} catch (error) {
// Good - preserves stack trace and uses correct property name
logger.error('Operation failed', {error, context: 'user-service'})
// Avoid
logger.error('Operation failed', {message: error.message})
}
-
Add Context: Use
logger.addContext(module) at the top of your files to automatically include file information.
-
Use Appropriate Levels:
debug: Detailed information useful during development
info: Normal application behavior, milestones
warn: Unexpected but handled issues
error: Errors that prevent proper operation
-
Include Relevant Data: Pass structured objects with relevant context:
logger.info('Payment processed', {
orderId: order._id,
amount: payment.amount,
currency: payment.currency,
method: payment.method
})
Common Patterns
Service Logging
@Service()
export class ProcessPaymentService {
async execute(orderId: OrderId, paymentData: PaymentData) {
logger.info('Processing payment', {orderId, amount: paymentData.amount})
try {
const result = await this.paymentGateway.charge(paymentData)
logger.info('Payment successful', {orderId, transactionId: result.id})
return result
} catch (error) {
logger.error('Payment failed', {error, orderId, amount: paymentData.amount})
throw error
}
}
}
Migration Logging
@MigrationService({name: 'MigrateUsers.v1', useMongoTransactions: false})
export class MigrateUsers {
async runMigration() {
let processed = 0
for await (const user of cursor) {
await this.processUser(user)
processed++
// Log progress every 500 documents
if (processed % 500 === 0) {
logger.info('Migration progress', {processed, migrationName: 'MigrateUsers.v1'})
}
}
logger.info('Migration complete', {processed, migrationName: 'MigrateUsers.v1'})
}
}