> ## 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.

# Logger

> A powerful, flexible logging utility for Orion.js applications

The Orion.js Logger is a powerful, flexible logging utility built on top of [Winston](https://github.com/winstonjs/winston). It provides structured logging capabilities with customizable formats, transports, and context tracking.

## Installation

```bash theme={null}
pnpm add @orion-js/logger
```

## Basic Usage

```typescript theme={null}
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.

```typescript theme={null}
// 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**:

```typescript theme={null}
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)

### Formatting

The logger supports two main output formats:

1. **Text Format** (for development):
   * Colorized output
   * Human-readable formatting
   * Activated when `ORION_DEV=1` environment variable is set

2. **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

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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

1. **Use Static Message Strings**: The first argument must be a static string for proper log aggregation:
   ```typescript theme={null}
   // Good
   logger.info('User created', {userId: '123', email: 'user@example.com'})

   // Avoid
   logger.info(`User ${userId} created with email ${email}`)
   ```

2. **Pass Error Objects Correctly**: Use the `error` property name:
   ```typescript theme={null}
   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})
   }
   ```

3. **Add Context**: Use `logger.addContext(module)` at the top of your files to automatically include file information.

4. **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

5. **Include Relevant Data**: Pass structured objects with relevant context:
   ```typescript theme={null}
   logger.info('Payment processed', {
     orderId: order._id,
     amount: payment.amount,
     currency: payment.currency,
     method: payment.method
   })
   ```

## Common Patterns

### Service Logging

```typescript theme={null}
@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

```typescript theme={null}
@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'})
  }
}
```
