@nowarajs/logger
Version:
Type-safe logging library for Bun with advanced TypeScript body intersection, modular sink pattern, transform streams, and immutable API design.
304 lines (236 loc) • 9.65 kB
Markdown

- [🎯 NowaraJS - Logger](
- [📌 Table of Contents](
- [📝 Description](
- [✨ Features](
- [🔧 Installation](
- [⚙️ Usage](
- [Basic Setup](
- [Multiple Sinks](
- [Custom Sinks with Advanced Type Safety](
- [Typed Sink Implementation](
- [Body Intersection with Multiple Sinks](
- [Mixed Sink Types](
- [Error Handling](
- [Sink Management](
- [Available Log Levels](
- [Configuration Options](
- [📚 API Reference](
- [⚖️ License](
- [📧 Contact](
> A TypeScript library that provides a modular, type-safe, and sink-based logging system designed specifically for Bun.
**@nowarajs/logger** provides a flexible and high-performance logging system with advanced TypeScript support. It uses a sink pattern architecture that allows multiple logging destinations (console, file, custom) to be used individually or combined, with automatic type inference and body intersection for type-safe logging operations.
## ✨ Features
- 🔒 **Type Safety**: Full TypeScript support with advanced body intersection for type-safe logging
- 🎯 **Sink Pattern**: Multiple logging sinks (console, file, custom) that can be used individually or combined
- 🔄 **Non-blocking Architecture**: Uses transform streams and async processing for optimal performance
- ⚡ **High Performance**: Queue-based system with configurable buffer limits (default: 10,000 logs)
- 🎨 **Flexible Logging Levels**: Support for ERROR, WARN, INFO, DEBUG, and LOG levels
- 🔗 **Event-Driven**: Emits typed events for error handling and lifecycle management
- 🔧 **Immutable API**: Each operation returns a new logger instance for better state management
- 📦 **Built-in Sinks**: Console logger with colorization and file logger included
- 🛠️ **Custom Sink Support**: Easily create and register custom logging sinks with advanced type safety
- 📜 **Body Intersection**: Automatically infers and enforces correct types based on selected sinks
## 🔧 Installation
```bash
bun add @nowarajs/logger
```
### Peer Dependencies
#### Required :
```bash
bun add @nowarajs/error @nowarajs/typed-event-emitter
```
## ⚙️ Usage
### Basic Setup
```typescript
import { Logger } from '@nowarajs/logger';
import { ConsoleLoggerSink, FileLoggerSink } from '@nowarajs/logger/sinks';
// Create a logger with console strategy
const logger = new Logger()
.registerStrategy('console', new ConsoleLoggerSink(true)); // with colors
// Log messages
logger.info('Application started successfully');
logger.error('An error occurred');
logger.debug('Debug information', ['console']); // specific strategy
```
```typescript
// Combine multiple sinks
const logger = new Logger()
.registerStrategy('console', new ConsoleLoggerSink(true))
.registerStrategy('file', new FileLoggerSink('./app.log'));
// Logs to both console and file
logger.info('This goes to both sinks');
// Log to specific sinks only
logger.error('Critical error', ['file']); // only to file
logger.warn('Warning message', ['console']); // only to console
```
The most powerful feature of @nowarajs/logger is its **advanced type safety system**. You can create custom logging sinks with typed objects, and TypeScript will automatically infer and enforce the correct types based on your selected sinks through **body intersection**.
When you implement `LoggerSink<TLogObject>`, you specify the exact type of object that sink expects:
```typescript
import { Logger } from '@nowarajs/logger';
import type { LoggerSink, LogLevels } from '@nowarajs/logger/types';
// Define specific interfaces for different logging contexts
interface DatabaseLog {
userId: number;
action: string;
metadata?: Record<string, unknown>;
}
interface ApiLog {
endpoint: string;
method: string;
statusCode: number;
responseTime: number;
}
// Create typed sinks
class DatabaseLoggerStrategy implements LoggerSink<DatabaseLog> {
public async log(level: LogLevels, date: Date, object: DatabaseLog): Promise<void> {
// object is strictly typed as DatabaseLog
await saveToDatabase({
level,
date,
userId: object.userId,
action: object.action,
metadata: object.metadata
});
}
}
// You can just put the type directly in the log method and it will be automatically inferred
class ApiLoggerStrategy implements LoggerSink {
public async log(level: LogLevels, date: Date, object: ApiLog): Promise<void> {
// object is strictly typed as ApiLog
await sendToMonitoring(`${object.method} ${object.endpoint} - ${object.statusCode} (${object.responseTime}ms)`);
}
}
// Register typed sinks
const logger = new Logger()
.registerStrategy('database', new DatabaseLoggerStrategy())
.registerStrategy('api', new ApiLoggerStrategy())
.registerStrategy('console', new ConsoleLoggerSink()); // ConsoleLoggerSink<unknown>
// ✅ TypeScript enforces the correct types based on selected sinks
logger.info({
userId: 123,
action: 'login',
metadata: { ip: '192.168.1.1' }
}, ['database']); // Only DatabaseLog type required
logger.error({
endpoint: '/api/users',
method: 'POST',
statusCode: 500,
responseTime: 1250
}, ['api']); // Only ApiLog type required
// ❌ TypeScript error: Missing required properties
logger.info({
userId: 123,
action: 'login'
// Error: object doesn't match ApiLog interface
}, ['api']);
```
When using multiple sinks simultaneously, @nowarajs/logger creates a **type intersection** of all selected sink types using the `BodiesIntersection` utility type:
```typescript
// ✅ TypeScript requires intersection of both types when using multiple sinks
logger.warn({
userId: 123,
action: 'failed_request',
endpoint: '/api/users',
method: 'POST',
statusCode: 400,
responseTime: 200
}, ['database', 'api']); // Both DatabaseLog & ApiLog types required
// ❌ TypeScript error: Missing ApiLog properties
logger.error({
userId: 123,
action: 'error'
// Error: Missing endpoint, method, statusCode, responseTime
}, ['database', 'api']);
// ✅ When no sinks specified, uses all sinks (intersection of all types)
logger.log({
userId: 123,
action: 'system_event',
endpoint: '/health',
method: 'GET',
statusCode: 200,
responseTime: 50
}); // DatabaseLog & ApiLog & unknown (console) intersection required
```
You can mix typed and untyped sinks. The intersection will include `unknown` for untyped sinks:
```typescript
// Using typed + untyped sinks
logger.info({
userId: 123,
action: 'mixed_log',
additionalData: 'any value' // ✅ Allowed due to intersection with unknown
}, ['database', 'console']); // Type: DatabaseLog & unknown
// TypeScript allows additional properties when unknown is in the intersection
logger.debug({
userId: 123,
action: 'debug_info',
debugLevel: 3,
stackTrace: ['frame1', 'frame2'],
customField: { nested: 'data' }
}, ['database', 'console']); // ✅ Extra properties allowed due to unknown intersection
```
```typescript
const logger = new Logger()
.registerStrategy('console', new ConsoleLoggerSink());
// Listen for errors
logger.on('error', (error) => {
console.error('Logger error:', error);
});
// Listen for completion
logger.on('end', () => {
console.log('All pending logs processed');
});
```
```typescript
let logger = new Logger();
// Add sinks
logger = logger
.registerStrategy('console', new ConsoleLoggerSink())
.registerStrategy('file', new FileLoggerSink('./app.log'));
// Add multiple sinks at once
logger = logger.registerStrategies([
['database', new DatabaseLoggerStrategy()],
['remote', new RemoteLoggerStrategy()]
]);
// Remove sinks
logger = logger.unregisterStrategy('database');
logger = logger.unregisterStrategies(['file', 'remote']);
// Clear all sinks
logger = logger.clearStrategies();
```
```typescript
logger.error('Error message'); // ERROR level
logger.warn('Warning message'); // WARN level
logger.info('Info message'); // INFO level
logger.debug('Debug message'); // DEBUG level
logger.log('Generic message'); // LOG level
```
```typescript
// Custom queue size (default: 10,000)
const logger = new Logger({}, 5000);
// With initial sinks
const logger = new Logger({
console: new ConsoleLoggerSink(true),
file: new FileLoggerSink('./app.log')
});
```
You can find the complete API reference documentation for `@nowarajs/logger` at:
- [Reference Documentation](https://nowarajs.github.io/logger)
Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
- GitHub: [NowaraJS](https://github.com/NowaraJS)
- Package: [@nowarajs/logger](https://www.npmjs.com/package/@nowarajs/logger)