npm package: @lyleunderwood/isomorphic-logger
Description: An isomorphic logger with pino-inspired API for browser and Node.js
Latest version: 1.0.3

# @lyleunderwood/isomorphic-logger

An isomorphic logger with a pino-inspired API for browser and Node.js environments. Features automatic environment detection, structured logging, and comprehensive error handling with full stack traces.

## Features

- **🌐 Isomorphic**: Works seamlessly in both Node.js and browser environments
- **📝 Pino-inspired API**: Familiar interface with fixed log levels (trace, debug, info, warn, error, fatal)
- **🔧 Smart Formatting**: Pretty-printing in development, JSON in production
- **🎨 Color Support**: Automatic terminal color detection with level-specific colors
- **🚨 Advanced Error Handling**: Full error serialization with stack traces and error cause chains
- **👶 Child Loggers**: Create contextual loggers with bound data
- **📦 Zero Dependencies**: Lightweight with no external dependencies
- **🔌 Bundler Friendly**: Works with webpack, Vite, and other modern bundlers
- **📘 TypeScript**: Fully typed with comprehensive type definitions

## Installation

```bash
npm install @lyleunderwood/isomorphic-logger
```

## Quick Start

### Node.js

```javascript
import createLogger from '@lyleunderwood/isomorphic-logger'

const logger = createLogger({ level: 'debug' })

logger.info('Hello from Node.js!')
logger.debug({ userId: '123', action: 'login' }, 'User logged in')
logger.error(new Error('Something went wrong'), 'Operation failed')
```

### Browser

```javascript
import createLogger from '@lyleunderwood/isomorphic-logger'

const logger = createLogger({ level: 'debug' })

logger.info('Hello from the browser!')
logger.warn({ componentId: 'UserList' }, 'Component rendered with warnings')
```

### CommonJS

```javascript
const createLogger = require('@lyleunderwood/isomorphic-logger')

const logger = createLogger()
logger.info('Hello from CommonJS!')
```

## API Reference

### Creating a Logger

```javascript
const logger = createLogger(options)
```

#### Options

- `level?: LogLevel` - Minimum log level (default: 'info')
- `prettyPrint?: boolean` - Force pretty-printing on/off (default: auto-detect development)
- `useColors?: boolean` - Force colors on/off (default: auto-detect terminal support)
- `bindings?: Record<string, any>` - Default properties to include in all log entries

#### Log Levels

- `trace` (10) - Detailed debug information
- `debug` (20) - Debug information
- `info` (30) - General information
- `warn` (40) - Warning messages
- `error` (50) - Error messages
- `fatal` (60) - Fatal error messages

### Logging Methods

Each method supports multiple call signatures:

```javascript
// String message
logger.info('Simple message')

// String message with additional arguments
logger.info('User action', 'arg1', 'arg2')

// Object context
logger.info({ userId: '123', action: 'login' })

// Object context with message
logger.info({ userId: '123' }, 'User logged in')

// Error objects (automatically serialized)
logger.error(new Error('Something failed'))
logger.error(error, 'Operation failed')
logger.error({ error, userId: '123' }, 'User operation failed')
```

### Child Loggers

Create child loggers with persistent context:

```javascript
const requestLogger = logger.child({ 
  requestId: 'req-123',
  userId: '456' 
})

requestLogger.info('Processing request') 
// Output includes requestId and userId in every log

const serviceLogger = requestLogger.child({ 
  service: 'user-service' 
})

serviceLogger.debug('Database query executed')
// Output includes requestId, userId, and service
```

### Environment Detection

The logger automatically detects the environment and adjusts formatting:

**Development Mode** (Pretty-printed with colors):
```
[2024-01-15T10:30:45.123Z] INFO: User logged in {
  "userId": "123",
  "action": "login"
}
```

**Production Mode** (JSON):
```json
{"level":30,"time":1642249845123,"msg":"User logged in","userId":"123","action":"login"}
```

Development is detected when:
- `NODE_ENV === 'development'` (Node.js)
- `localhost`, `127.0.0.1`, or `file:` protocol (Browser)

### Color Support

The logger automatically detects terminal color support and adds colors to improve readability:

**Color Detection**:
- **Enabled**: TTY terminals, `FORCE_COLOR=1`, GitHub Actions
- **Disabled**: `NO_COLOR` set, `NODE_DISABLE_COLORS` set, non-TTY environments, CI (except GitHub Actions)

**Level Colors**:
- `TRACE`: Dim gray
- `DEBUG`: Cyan  
- `INFO`: Green
- `WARN`: Yellow
- `ERROR`: Red
- `FATAL`: Bright red

```javascript
// Force colors on/off
const colorLogger = createLogger({ useColors: true })
const noColorLogger = createLogger({ useColors: false })

// Auto-detect (default)
const autoLogger = createLogger()
```

## Advanced Error Handling

The logger provides comprehensive error handling with full serialization:

### Basic Error Logging

```javascript
const error = new Error('Database connection failed')
logger.error(error)
// Logs with full stack trace and error properties
```

### Error Cause Chains

```javascript
const rootCause = new Error('Network timeout')
const serviceError = new Error('Service unavailable')
serviceError.cause = rootCause

logger.error(serviceError)
// Logs complete error chain with all stack traces
```

### Custom Error Properties

```javascript
const error = new Error('Validation failed')
error.code = 'VALIDATION_ERROR'
error.statusCode = 400
error.details = { field: 'email', value: 'invalid' }

logger.error(error, 'Request validation failed')
// All custom properties are preserved in the log
```

### Nested Errors in Context

```javascript
logger.error({
  operation: 'user-registration',
  userId: '123',
  error: serviceError,
  metadata: { 
    attempt: 3,
    nestedError: anotherError 
  }
}, 'Registration failed after retries')
// All errors in the object tree are properly serialized
```

## Output Examples

### Development Output (Pretty)

```
[2024-01-15T10:30:45.123Z] INFO: User operation completed {
  "userId": "123",
  "operation": "login",
  "duration": 150
}

[2024-01-15T10:30:46.456Z] ERROR: Database operation failed {
  "message": "Connection timeout",
  "name": "Error",
  "stack": "Error: Connection timeout\n    at Database.connect (/app/db.js:15:13)\n    ...",
  "code": "ECONNREFUSED",
  "cause": {
    "message": "Network unreachable",
    "name": "NetworkError",
    "stack": "NetworkError: Network unreachable\n    at TCPSocket.connect (/app/net.js:42:8)\n    ..."
  }
}
```

### Production Output (JSON)

```json
{"level":30,"time":1642249845123,"msg":"User operation completed","userId":"123","operation":"login","duration":150}
{"level":50,"time":1642249846456,"msg":"Database operation failed","message":"Connection timeout","name":"Error","stack":"Error: Connection timeout\n    at Database.connect (/app/db.js:15:13)\n    ...","code":"ECONNREFUSED","cause":{"message":"Network unreachable","name":"NetworkError","stack":"NetworkError: Network unreachable\n    at TCPSocket.connect (/app/net.js:42:8)\n    ..."}}
```

## TypeScript Support

The library is written in TypeScript and provides full type definitions:

```typescript
import createLogger, { Logger, LogLevel, LoggerOptions } from '@lyleunderwood/isomorphic-logger'

const options: LoggerOptions = {
  level: 'debug',
  prettyPrint: false,
  bindings: { service: 'api' }
}

const logger: Logger = createLogger(options)

// All methods are fully typed
logger.info({ userId: string, action: string }, 'User action')
```

## Browser Compatibility

- **Modern Browsers**: ES2020+ (Chrome 80+, Firefox 72+, Safari 13.1+)
- **Build Size**: ~5KB minified
- **Dependencies**: Zero

## Node.js Compatibility

- **Node.js**: 16.0+
- **Build Size**: ~6KB
- **Dependencies**: Zero

## Best Practices

### 1. Use Structured Logging

```javascript
// Good: Structured data
logger.info({ userId, action, duration }, 'User action completed')

// Avoid: String interpolation
logger.info(`User ${userId} completed ${action} in ${duration}ms`)
```

### 2. Use Child Loggers for Context

```javascript
// Good: Child logger with persistent context
const requestLogger = logger.child({ requestId, userId })
requestLogger.info('Request started')
requestLogger.info('Validation completed')
requestLogger.info('Request completed')

// Avoid: Repeating context
logger.info({ requestId, userId }, 'Request started')
logger.info({ requestId, userId }, 'Validation completed')
logger.info({ requestId, userId }, 'Request completed')
```

### 3. Log Errors with Context

```javascript
// Good: Error with operation context
logger.error({ 
  error, 
  operation: 'user-creation',
  userId,
  requestId 
}, 'User creation failed')

// Avoid: Error without context
logger.error(error)
```

### 4. Use Appropriate Log Levels

```javascript
logger.trace('Entering function with params', { params })  // Detailed debugging
logger.debug('Cache hit for key', { key })                 // Debug information
logger.info('User logged in', { userId })                  // General information
logger.warn('Rate limit approaching', { current, limit })  // Warnings
logger.error(error, 'Operation failed')                    // Errors
logger.fatal(error, 'Service shutting down')               // Fatal errors
```

## Development

```bash
# Install dependencies
npm install

# Run tests
npm test

# Build package
npm run build

# Type checking
npm run typecheck
```

## License

MIT © Lyle Underwood