Discover komi-logger: My First Logging Library
Hello! I'm Komiriko and this is my very first post here on dev.to! π
I'm going to present my komi-logger package that I recently developed. π
I'd really love to get some feedback on it βΊοΈ, it's a project that's close to my heart and will hopefully help me progress and learn, so without further ado, let me present the project!
π Table of Contents
- π― What is komi-logger?
- β¨ Why I created this library
- π₯ Key features
- π Usage examples
- π€ Your feedback matters!
π― What is komi-logger?
komi-logger is a modular, type-safe logging library based on the Strategy pattern.
It provides a flexible and non-blocking logging system with advanced type safety. Indeed, I put a strong emphasis on Developer Experience (DX) and type safety.
I really loved Elysia (a framework I'm particularly fond of) and that's why I wanted to be inspired by its DX to create this library.
Works with Bun and Node.js!
bun add komi-logger
β¨ Why I created this library
To be honest, I created komi-logger simply because I wanted to succeed in creating a logger with good DX and the ability to create custom logging strategies with type safety, that I could use in my projects.
π₯ Key features
π Non-blocking architecture
Uses transform streams and asynchronous processing.
π― Strategy pattern
Multiple logging strategies (console, file, custom) usable individually or combined.
π‘οΈ Advanced type safety
Complete TypeScript support with strict typing and automatic type intersection based on selected strategies.
β‘ High performance
Queue-based system with configurable buffer (default: 10,000 logs).
The logger uses a transform stream to process log entries asynchronously. Each log is queued and processed through the configured strategies. The system automatically handles backpressure and provides error isolation between strategies.
π Event-driven
Emits typed events for error handling (You can throw in your strategy and it will be caught and transmitted with the error event) and lifecycle management.
π Usage examples
Let's see some usage examples of komi-logger to illustrate its features!
But before we start, you should know that komi-logger only exports the logger at the root. If you want to use types for example, you need to import them from komi-logger/types.
Here's a list of exports that will be useful to you:
-
komi-logger: Logger -
komi-logger/types: exports types and interfaces -
komi-logger/strategies: exports already implemented strategies (console and file)
Strategy management
import { Logger } from 'komi-logger';
import { ConsoleLoggerStrategy, FileLoggerStrategy } from 'komi-logger/strategies';
// You can register strategies individually
const logger = new Logger()
.registerStrategy('console', new ConsoleLoggerStrategy())
.registerStrategy('file', new FileLoggerStrategy('./logs/app.log'));
// Or register multiple strategies at once
const logger = new Logger()
.registerStrategies([
['console', new ConsoleLoggerStrategy()],
['file', new FileLoggerStrategy('./logs/app.log')]
]);
// Or just use the constructor
const logger = new Logger({
console: new ConsoleLoggerStrategy(),
file: new FileLoggerStrategy('./logs/app.log')
});
// Remove strategies
logger = logger.unregisterStrategies(['file', 'remote']);
// Clear all strategies
logger = logger.clearStrategies();
Typed strategies with automatic intersection
import type { LoggerStrategy, LogLevels } from 'komi-logger/types';
import { Logger } from 'komi-logger';
// Define specific interfaces
interface DatabaseLog {
userId: number;
action: string;
metadata?: Record<string, unknown>;
}
interface ApiLog {
endpoint: string;
method: string;
statusCode: number;
responseTime: number;
}
// Create typed strategies
class DatabaseLoggerStrategy implements LoggerStrategy<DatabaseLog> {
public async log(level: LogLevels, date: Date, object: DatabaseLog): Promise<void> {
// object is strictly typed as DatabaseLog
await saveToDatabase(...);
}
}
// You can omit the LoggerStrategy type if you want, it will be automatically inferred from the log method
class ApiLoggerStrategy implements LoggerStrategy {
public async log(level: LogLevels, date: Date, object: ApiLog): Promise<void> {
// object is strictly typed as ApiLog
await sendToMonitoring(...);
}
}
// TypeScript automatically enforces the correct types
const logger = new Logger({
database: new DatabaseLoggerStrategy(),
api: new ApiLoggerStrategy()
});
/**
* Tip: for example with VSCode, you can use Ctrl + Space to see available strategies
*
* logger.log(..., [*here*]); // You'll see available strategies
* Based on selected strategies, the first parameter of log will be automatically typed. (by default, all strategies are selected)
*/
// β
Correct type for one strategy
logger.info({
userId: 123,
action: 'login',
metadata: { ip: '192.168.1.1' }
}, ['database']); // Only DatabaseLog is required
// β
Automatic intersection for multiple strategies
logger.warn({
userId: 123,
action: 'failed_request',
endpoint: '/api/users',
method: 'POST',
statusCode: 400,
responseTime: 200
}, ['database', 'api']); // DatabaseLog & ApiLog are required!
// β TypeScript error: missing properties
logger.error({
userId: 123,
action: 'error'
// Error: endpoint, method, statusCode, responseTime missing
}, ['database', 'api']);
TypeScript calculates the intersection of required types based on the strategies you use!
Error handling and events
import type { LoggerStrategy, LogLevels } from 'komi-logger/types';
import { Logger } from 'komi-logger';
class DatabaseLoggerStrategy implements LoggerStrategy {
public log(): void {
throw new Error('Database connection failed'); // Simulate an error
}
}
const logger = new Logger({
database: new DatabaseLoggerStrategy()
});
logger.log('database');
// Listen for errors
logger.on('error', (error) => {
console.error(error.uuid); // Unique error UUID
console.error(error.date); // Error date
console.error(error.message); // Error message
console.error(error.key); // Error key (If you want to use i18n)
console.error(error.cause?.strategyName); // Name of the strategy that caused the error
console.error(error.cause?.object); // Object that caused the error
console.error(error.cause?.error); // The error that was thrown
});
// Listen for processing end
logger.on('end', () => {
console.log('All pending logs have been processed');
});
π€ Your feedback matters!
This is my first npm package that I'm sharing and I'd really love to get your feedback!
What I'm particularly interested in:
- π€ What do you think about the API and DX (Developer Experience)?
- π§ Are there any features you're missing?
- π Have you found any bugs or strange behaviors?
- π Is the documentation clear and complete?
- β‘ How do you find the performance in your use cases?
- π‘ Do you have any ideas for improvements or new features?
Useful links:
- π¦ NPM Package
- π GitHub Repository
- π Complete Documentation
Feel free to:
- β Star the repo if you like it
- π Open issues for bugs
- π‘ Suggest improvements
- π Contribute to the code
Thanks for reading this far! π
I'm really excited to see what you think and how you might use it in your projects.
PS: feel free to contact me: X or even by email komiriko@pm.me
Top comments (0)