npm package: @periodic/iridium
Description: Production-grade, framework-agnostic structured logging library for Node.js with TypeScript support
Author: Uday Thakur
Latest version: 1.0.0

# 🌟 Periodic Iridium

[![npm version](https://img.shields.io/npm/v/@periodic/iridium.svg)](https://www.npmjs.com/package/@periodic/iridium)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue)](https://www.typescriptlang.org/)

**Production-grade, framework-agnostic structured logging library for Node.js with TypeScript support**

Part of the **Periodic** series of Node.js packages by Uday Thakur.

---

## 💡 Why Iridium?

**Iridium** gets its name from the rare, dense metallic element known for its extreme durability and resistance to corrosion — just like how this library provides **durable, reliable logging** that won't fail under pressure.

In chemistry, iridium is one of the most corrosion-resistant metals known, found in the Earth's crust and meteorites. Similarly, **@periodic/iridium** was forged through real-world production experience to create logging infrastructure that's both resilient and elegant.

The name represents:
- **Durability**: Never throws, never fails, production-tested
- **Clarity**: Clean, structured logs with consistent formatting
- **Precision**: Type-safe with strict TypeScript support
- **Rarity**: A truly dependency-free, first-class logging core

Just as iridium is a fundamental building block in high-precision instruments, **@periodic/iridium** serves as the foundational logging layer for production-grade Node.js applications.

---

## 🎯 Why Choose Iridium?

Building robust applications requires reliable, structured logging, but most solutions come with significant challenges:

- **Existing loggers** (Winston, Pino, Bunyan) are heavy with many dependencies
- **Framework-coupled solutions** make library reuse difficult
- **Global state** in loggers causes problems in multi-tenant apps
- **Configuration complexity** leads to maintenance nightmares
- **Wrapper libraries** add layers without solving core issues

**Periodic Iridium** provides the perfect solution:

✅ **Zero dependencies** - Pure TypeScript logging core  
✅ **Framework-agnostic** - Safe for use in both libraries and applications  
✅ **Five log levels** - debug, info, warn, error, fatal  
✅ **Structured logging** with first-class metadata support  
✅ **Child loggers** with immutable context inheritance  
✅ **Custom transports** - Console, Memory, and extensible  
✅ **Pretty & JSON formatters** with TTY-aware colorization  
✅ **Sensitive data redaction** built-in  
✅ **Type-safe** with strict TypeScript from the ground up  
✅ **No global state** - No side effects on import  
✅ **Production-ready** - Never throws, graceful error handling

---

## 📦 Installation

```bash
npm install @periodic/iridium
```

Or with yarn:

```bash
yarn add @periodic/iridium
```

**No other dependencies required!** Iridium is completely dependency-free.

---

## 🚀 Quick Start

```typescript
import { createLogger, ConsoleTransport } from '@periodic/iridium';

const logger = createLogger({
  level: 'info',
  transports: [new ConsoleTransport()],
});

// Start logging
logger.info('Application started');
logger.warn('Low memory detected', { available: '512MB' });
logger.error('Database connection failed', { host: 'db.example.com' });

// With metadata
logger.info('User logged in', {
  userId: 'user_123',
  email: 'john@example.com',
  ipAddress: '192.168.1.1',
});
```

**Console Output:**
```
[2024-02-13T10:30:00.000Z] INFO  Application started
[2024-02-13T10:30:01.000Z] WARN  Low memory detected {"available":"512MB"}
[2024-02-13T10:30:02.000Z] ERROR Database connection failed {"host":"db.example.com"}
```

---

## 🧠 Core Concepts

### The `createLogger` Function

- **`createLogger` is the primary factory function**
- Returns a configured `Logger` instance
- Accepts flexible configuration options
- **This is the main entry point for all applications**
- No global state, safe for multi-tenant apps

**Typical usage:**
- Application code creates loggers with `createLogger()`
- Libraries export logger instances for consumers
- Tests create isolated loggers with MemoryTransport

```typescript
const logger = createLogger({
  level: 'info',
  transports: [new ConsoleTransport()],
  processors: [],
  redact: { paths: ['password'] }
});
```

### The `Logger` Class

- **`Logger` is the core logging class**
- Provides five logging methods: debug, info, warn, error, fatal
- Supports child logger creation with context
- **Never throws** - all errors routed to `onError` handler
- Immutable context merging for safety

**Design principle:**
> Applications create loggers, libraries use loggers, frameworks extend loggers.

```typescript
// Logging
logger.info('User registered', { userId: '123' });

// Child logger with context
const requestLogger = logger.child({ requestId: 'req_abc' });
requestLogger.info('Processing request'); // Includes requestId

// Error handling
try {
  await riskyOperation();
} catch (error) {
  logger.error('Operation failed', { error });
}
```

---

## ✨ Features

### 🎚️ Five Log Levels

Complete control over logging verbosity:

```typescript
logger.debug('Detailed debugging info');
logger.info('Informational message');
logger.warn('Warning message');
logger.error('Error occurred');
logger.fatal('System failure');
```

**Level Hierarchy:**
```
debug < info < warn < error < fatal
```

Set minimum level to filter logs:
```typescript
const logger = createLogger({
  level: 'warn', // Only warn, error, fatal will be logged
  transports: [new ConsoleTransport()],
});
```

### 🎯 Structured Logging

First-class support for structured metadata:

```typescript
logger.info('Payment processed', {
  userId: 'user_123',
  amount: 99.99,
  currency: 'USD',
  paymentMethod: 'card',
  transactionId: 'txn_abc123',
});
```

**JSON Output:**
```json
{
  "timestamp": "2024-02-13T10:30:00.000Z",
  "level": "info",
  "message": "Payment processed",
  "meta": {
    "userId": "user_123",
    "amount": 99.99,
    "currency": "USD",
    "paymentMethod": "card",
    "transactionId": "txn_abc123"
  }
}
```

### 🧒 Child Loggers

Create child loggers with inherited context:

```typescript
// Parent logger
const logger = createLogger({
  transports: [new ConsoleTransport()],
});

// Child logger for a specific request
const requestLogger = logger.child({
  requestId: 'req_abc123',
  userId: 'user_456',
  method: 'POST',
  path: '/api/users',
});

// All logs include the context
requestLogger.info('Request started');
requestLogger.info('Validating input');
requestLogger.info('Creating user');
requestLogger.info('Request completed');

// Output includes: requestId, userId, method, path in every log
```

**Context is immutable:**
```typescript
const parent = logger.child({ service: 'api' });
const child = parent.child({ requestId: '123' });

child.info('test');  // Context: { service: 'api', requestId: '123' }
parent.info('test'); // Context: { service: 'api' }
```

### 🛡️ Error Serialization

Automatic, safe error handling:

```typescript
try {
  await connectToDatabase();
} catch (error) {
  logger.error('Database connection failed', { error });
}
```

**Output:**
```json
{
  "timestamp": "2024-02-13T10:30:00.000Z",
  "level": "error",
  "message": "Database connection failed",
  "error": {
    "name": "Error",
    "message": "Connection refused",
    "stack": "Error: Connection refused\n    at ..."
  }
}
```

### 🎨 Formatters

#### Pretty Formatter (Human-Readable)

```typescript
import { PrettyFormatter } from '@periodic/iridium';

const logger = createLogger({
  transports: [
    new ConsoleTransport({
      formatter: new PrettyFormatter({ colorize: true }),
    }),
  ],
});
```

**Output:**
```
[2024-02-13T10:30:00.000Z] INFO  User logged in {"userId":"123"}
[2024-02-13T10:30:01.000Z] WARN  Low memory {"available":"512MB"}
[2024-02-13T10:30:02.000Z] ERROR Connection failed {"host":"db.example.com"}
```

#### JSON Formatter (Machine-Readable)

```typescript
import { JsonFormatter } from '@periodic/iridium';

const logger = createLogger({
  transports: [
    new ConsoleTransport({
      formatter: new JsonFormatter(),
    }),
  ],
});
```

**Output:**
```json
{"timestamp":"2024-02-13T10:30:00.000Z","level":"info","message":"User logged in","meta":{"userId":"123"}}
```

### 🔒 Sensitive Data Redaction

Built-in support for redacting sensitive fields:

```typescript
const logger = createLogger({
  level: 'info',
  transports: [new ConsoleTransport()],
  redact: {
    paths: ['password', 'apiKey', 'user.ssn', 'creditCard.number'],
    censor: '[REDACTED]',
  },
});

logger.info('User registered', {
  username: 'john_doe',
  password: 'super_secret_123',  // Will be redacted
  email: 'john@example.com',
  apiKey: 'sk_live_1234567890',  // Will be redacted
  user: {
    name: 'John Doe',
    ssn: '123-45-6789',           // Will be redacted
  },
});
```

**Output:**
```json
{
  "username": "john_doe",
  "password": "[REDACTED]",
  "email": "john@example.com",
  "apiKey": "[REDACTED]",
  "user": {
    "name": "John Doe",
    "ssn": "[REDACTED]"
  }
}
```

### 🔌 Custom Processors

Transform log records before output:

```typescript
import { addFieldsProcessor, addCorrelationIdProcessor } from '@periodic/iridium';

const logger = createLogger({
  level: 'info',
  transports: [new ConsoleTransport()],
  processors: [
    // Add static fields to every log
    addFieldsProcessor({
      environment: process.env.NODE_ENV,
      version: process.env.APP_VERSION,
      hostname: require('os').hostname(),
    }),
    
    // Add correlation ID
    addCorrelationIdProcessor(() => 
      Math.random().toString(36).substring(7)
    ),
  ],
});

logger.info('Application started');
// Output includes: environment, version, hostname, correlationId
```

**Custom Processor:**
```typescript
import type { LogProcessor } from '@periodic/iridium';

const timestampProcessor: LogProcessor = (record) => ({
  ...record,
  meta: {
    ...record.meta,
    timestampMs: Date.now(),
  },
});

const logger = createLogger({
  processors: [timestampProcessor],
  transports: [new ConsoleTransport()],
});
```

### 📊 Multiple Transports

Send logs to multiple destinations:

```typescript
import { ConsoleTransport, MemoryTransport } from '@periodic/iridium';

const memory = new MemoryTransport();

const logger = createLogger({
  level: 'info',
  transports: [
    new ConsoleTransport(),  // Output to console
    memory,                   // Store in memory
  ],
});

logger.info('test');

// Later: check memory logs
console.log(`Logged ${memory.count()} records`);
console.log(memory.getRecords());
```

---

## 📚 Common Patterns

### 1. Express.js Integration

```typescript
import express from 'express';
import { createLogger, ConsoleTransport } from '@periodic/iridium';

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  transports: [new ConsoleTransport()],
});

const app = express();

// Request logging middleware
app.use((req, res, next) => {
  req.logger = logger.child({
    requestId: req.id || Math.random().toString(36).substring(7),
    method: req.method,
    path: req.path,
    userAgent: req.get('user-agent'),
  });
  
  req.logger.info('Request started');
  next();
});

// Use request-specific logger
app.get('/api/users', async (req, res) => {
  req.logger.info('Fetching users');
  const users = await db.users.findAll();
  res.json(users);
});

// Error handling
app.use((err, req, res, next) => {
  req.logger.error('Request failed', { error: err });
  res.status(500).json({ error: 'Internal Server Error' });
});
```

### 2. Service Layer Logging

```typescript
class UserService {
  constructor(private logger: Logger) {}
  
  async createUser(data: UserData) {
    const log = this.logger.child({ operation: 'createUser' });
    
    log.info('Creating user', { email: data.email });
    
    try {
      const user = await db.users.create(data);
      log.info('User created successfully', { userId: user.id });
      return user;
    } catch (error) {
      log.error('Failed to create user', { error });
      throw error;
    }
  }
}

const userService = new UserService(logger);
```

### 3. Background Jobs

```typescript
import { createLogger, ConsoleTransport, JsonFormatter } from '@periodic/iridium';

const logger = createLogger({
  level: 'info',
  transports: [
    new ConsoleTransport({
      formatter: new JsonFormatter(), // JSON for log aggregation
    }),
  ],
});

async function processJob(job: Job) {
  const log = logger.child({
    jobId: job.id,
    jobType: job.type,
  });
  
  log.info('Job started');
  
  try {
    await job.process();
    log.info('Job completed successfully');
  } catch (error) {
    log.error('Job failed', { error, attempts: job.attempts });
    throw error;
  }
}
```

### 4. Testing with Memory Transport

```typescript
import { createLogger, MemoryTransport } from '@periodic/iridium';

describe('UserService', () => {
  let logger: Logger;
  let memory: MemoryTransport;
  let userService: UserService;

  beforeEach(() => {
    memory = new MemoryTransport();
    logger = createLogger({
      level: 'debug',
      transports: [memory],
    });
    userService = new UserService(logger);
  });

  it('should log user creation', async () => {
    await userService.createUser({ email: 'test@example.com' });
    
    const logs = memory.getRecordsByLevel('info');
    expect(logs).toHaveLength(2);
    expect(logs[0].message).toBe('Creating user');
    expect(logs[1].message).toBe('User created successfully');
  });
});
```

### 5. Production Configuration

```typescript
import winston from 'winston';
import { createLogger, ConsoleTransport, JsonFormatter } from '@periodic/iridium';

const isDevelopment = process.env.NODE_ENV === 'development';

const logger = createLogger({
  level: process.env.LOG_LEVEL || (isDevelopment ? 'debug' : 'info'),
  
  transports: [
    new ConsoleTransport({
      formatter: isDevelopment
        ? new PrettyFormatter({ colorize: true })
        : new JsonFormatter(),
      stderr: true, // error/fatal go to stderr
    }),
  ],
  
  processors: [
    addFieldsProcessor({
      service: 'api',
      environment: process.env.NODE_ENV,
      version: process.env.npm_package_version,
      hostname: require('os').hostname(),
    }),
  ],
  
  redact: {
    paths: [
      'password',
      'token',
      'apiKey',
      'secret',
      'creditCard',
      'ssn',
    ],
  },
  
  onError: (error) => {
    // Log logger errors to external service
    console.error('Logger error:', error);
  },
});

export default logger;
```

### 6. Graceful Shutdown

```typescript
const logger = createLogger({
  level: 'info',
  transports: [new ConsoleTransport()],
});

async function shutdown(signal: string) {
  logger.info('Shutdown initiated', { signal });
  
  // Flush all async transports
  await logger.flush();
  
  logger.info('Shutdown complete');
  process.exit(0);
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
```

---

## 🎛️ Configuration Options

### Logger Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `level` | `LogLevel` | `'info'` | Minimum level to log |
| `transports` | `Transport[]` | `[]` | Output destinations |
| `processors` | `LogProcessor[]` | `[]` | Record transformers |
| `redact` | `RedactionConfig` | - | Sensitive data redaction |
| `clock` | `ClockFunction` | - | Custom timestamp function |
| `onError` | `(error) => void` | - | Error handler |

```typescript
const logger = createLogger({
  level: 'info',
  transports: [new ConsoleTransport()],
  processors: [addFieldsProcessor({ service: 'api' })],
  redact: { paths: ['password'], censor: '[REDACTED]' },
  clock: () => new Date().toISOString(),
  onError: (error) => console.error('Logger error:', error),
});
```

### Console Transport Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `formatter` | `Formatter` | `PrettyFormatter` | Output formatter |
| `colorize` | `boolean` | Auto-detect | Enable colors |
| `stderr` | `boolean` | `true` | Route error/fatal to stderr |

```typescript
new ConsoleTransport({
  formatter: new JsonFormatter(),
  colorize: false,
  stderr: true,
});
```

### Redaction Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `paths` | `string[]` | - | Fields to redact (supports nested) |
| `censor` | `string` | `'[REDACTED]'` | Replacement value |

```typescript
redact: {
  paths: ['password', 'apiKey', 'user.ssn'],
  censor: '***',
}
```

---

## 📋 API Reference

### `createLogger(config?)`

Create a new logger instance.

```typescript
function createLogger(config?: LoggerConfig): Logger
```

### `Logger` Class

Main logger interface.

```typescript
class Logger {
  debug(message: string, meta?: Record<string, unknown>): void
  info(message: string, meta?: Record<string, unknown>): void
  warn(message: string, meta?: Record<string, unknown>): void
  error(message: string, meta?: Record<string, unknown>): void
  fatal(message: string, meta?: Record<string, unknown>): void
  
  child(context: LogContext): Logger
  flush(): Promise<void>
}
```

### Transports

#### `ConsoleTransport`

```typescript
new ConsoleTransport(options?: {
  formatter?: Formatter;
  colorize?: boolean;
  stderr?: boolean;
})
```

#### `MemoryTransport`

```typescript
const memory = new MemoryTransport();

memory.log(record: LogRecord): void
memory.getRecords(): LogRecord[]
memory.getRecordsByLevel(level: string): LogRecord[]
memory.clear(): void
memory.count(): number
```

### Formatters

#### `PrettyFormatter`

```typescript
new PrettyFormatter(options?: {
  colorize?: boolean;
})
```

#### `JsonFormatter`

```typescript
new JsonFormatter(options?: {
  pretty?: boolean;
})
```

### Processors

```typescript
addFieldsProcessor(fields: Record<string, unknown>): LogProcessor
addCorrelationIdProcessor(getId: () => string): LogProcessor
composeProcessors(...processors: LogProcessor[]): LogProcessor
```

---

## 🧩 Architecture

```
@periodic/iridium/
├── src/
│   ├── core/                  # Framework-agnostic core
│   │   ├── types.ts          # TypeScript interfaces
│   │   ├── levels.ts         # Log level utilities
│   │   ├── logger.ts         # Main Logger class
│   │   ├── record.ts         # Record building
│   │   └── processors.ts     # Processor utilities
│   ├── transports/            # Transport implementations
│   │   ├── console.ts        # Console transport
│   │   └── memory.ts         # Memory transport
│   ├── formatters/            # Formatter implementations
│   │   ├── pretty.ts         # Pretty formatter
│   │   └── json.ts           # JSON formatter
│   ├── utils/                 # Utilities
│   │   ├── serializeError.ts # Error serialization
│   │   └── redact.ts         # Redaction
│   └── index.ts               # Public API
```

**Design Philosophy:**
- **Core** is pure TypeScript with no dependencies
- **Transports** are pluggable output destinations
- **Formatters** control output format
- **Processors** enable extensibility
- Easy to extend for custom transports and formatters

---

## 📈 Performance

Iridium is optimized for production workloads:

- **Early level filtering** - Disabled logs have near-zero overhead
- **No unnecessary allocations** - Minimal object creation
- **Numeric comparisons** - Fast level checks
- **Deferred stringification** - JSON.stringify only in transports
- **Zero dependencies** - No external overhead

**Benchmark Results** (Node.js 20):

```
Disabled logs (filtered):     ~2,000,000 ops/sec
Active logs (memory):         ~500,000 ops/sec
Active logs with metadata:    ~450,000 ops/sec
Child logger:                 ~400,000 ops/sec
Multi-transport (2x):         ~300,000 ops/sec
With processor:               ~400,000 ops/sec
With redaction:               ~350,000 ops/sec
```

Run benchmarks:
```bash
npm run benchmark
```

---

## 🚫 Explicit Non-Goals

This package **intentionally does not** include:

❌ File transport (use external packages)  
❌ HTTP transport (use external packages)  
❌ Log rotation (use external tools)  
❌ Log aggregation (use Elasticsearch, Datadog, etc.)  
❌ Metrics collection (use Prometheus, StatsD, etc.)  
❌ Tracing (use OpenTelemetry)  
❌ Configuration files (configure in code)  
❌ Automatic framework detection (explicit integration)

Focus on doing one thing well: **structured logging core**.

---

## 🤝 Related Packages

Part of the **Periodic** series by Uday Thakur:

- [**@periodic/obsidian**](https://www.npmjs.com/package/@periodic/obsidian) - HTTP error handling
- [**@periodic/titanium**](https://www.npmjs.com/package/@periodic/titanium) - Rate limiting
- [**@periodic/osmium**](https://www.npmjs.com/package/@periodic/osmium) - Redis caching

Build complete, production-ready APIs with the Periodic series!

---

## 📖 Documentation

- [Quick Start Guide](QUICKSTART.md)
- [Setup & Development](SETUP.md)
- [Contributing Guide](CONTRIBUTING.md)
- [Changelog](CHANGELOG.md)
- [Folder Structure](FOLDER_STRUCTURE.md)

---

## 🧪 Testing

```bash
# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests in watch mode
npm run test:watch

# Run benchmarks
npm run benchmark
```

**Note:** All tests achieve >80% code coverage.

---

## 🛠️ Production Recommendations

### Environment Variables

```bash
LOG_LEVEL=info
NODE_ENV=production
APP_VERSION=1.0.0
```

### Log Aggregation

Use JSON formatter with log aggregation services:

```typescript
const logger = createLogger({
  transports: [
    new ConsoleTransport({
      formatter: new JsonFormatter(),
    }),
  ],
});

// Pipe to Elasticsearch, Datadog, CloudWatch, etc.
```

### Error Monitoring

Integrate with error tracking:

```typescript
const logger = createLogger({
  onError: (error) => {
    Sentry.captureException(error);
  },
});
```

### Performance Monitoring

```typescript
const logger = createLogger({
  processors: [
    (record) => ({
      ...record,
      meta: {
        ...record.meta,
        duration: performance.now(),
      },
    }),
  ],
});
```

---

## 🎨 TypeScript Support

Full TypeScript support with complete type safety:

```typescript
import type {
  Logger,
  LogLevel,
  LogRecord,
  LogContext,
  Transport,
  Formatter,
  LogProcessor,
  RedactionConfig,
} from '@periodic/iridium';

const logger: Logger = createLogger({
  level: 'info' as LogLevel,
  transports: [new ConsoleTransport()],
});

const context: LogContext = { requestId: '123' };
const child: Logger = logger.child(context);
```

---

## 📝 License

MIT © [Uday Thakur](LICENSE)

---

## 🙏 Contributing

Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on:

- Code of conduct
- Development setup
- Pull request process
- Coding standards
- Architecture principles

---

## 📞 Support

- 📧 **Email:** udaythakurwork@gmail.com
- 🐛 **Issues:** [GitHub Issues](https://github.com/udaythakur7469/periodic-iridium/issues)
- 💬 **Discussions:** [GitHub Discussions](https://github.com/udaythakur7469/periodic-iridium/discussions)

---

## 🌟 Show Your Support

Give a ⭐️ if this project helped you build better applications!

---

**Built with ❤️ by Uday Thakur for production-grade Node.js applications**