@asaidimu/utils-logger
v1.0.4
Published
A collection of logger utilities.
Readme
@asaidimu/utils-logger
A robust, structured logging library for TypeScript/JavaScript applications. It provides a context-aware logger with support for multiple sinks (console, event bus, custom destinations), hierarchical child loggers, and advanced error formatting—all designed to simplify observability in complex systems.
Features
- Structured Logging – Logs are consistent objects with
level,event,timestamp,data, andcontextfields. - Log Levels –
trace,debug,info,warn,errorwith priority filtering. - Context Propagation – Attach contextual metadata to a logger; child loggers inherit and merge context.
- Pluggable Sinks – Send logs to one or multiple destinations (console, event bus, files, etc.).
- Built‑in Sinks –
ConsoleSink(stdout/stderr) andEventBusSink(integrates with an event bus). - Fuzz Logger – Generate random logs for testing and performance benchmarking.
- Error‑Aware – Automatically extracts structured fields from
SystemErrorand plainErrorobjects. - Type‑Safe – Written in TypeScript with full type definitions included.
Installation
npm install @asaidimu/utils-loggerPeer dependency:
ws(^8.21.0) is required if you use theUnsecureWebSocketServerSinkwith WebSocket‑based buses. Install it separately if needed. Peer dependency:@asaidimu/utils-events(^1.2.0) is required if you use theEventBusSinkInstall it separately if needed.
Quick Start
import { Logger, ConsoleSink } from "@asaidimu/utils-logger";
// Create a logger that writes to the console
const logger = new Logger([new ConsoleSink()], {
app: "my-service",
environment: "development",
});
// Basic logging
logger.info("user_logged_in", { userId: "123", ip: "192.168.1.1" });
// Error logging
try {
// ...
} catch (err) {
logger.error("database_query_failed", err);
}
// Child logger with additional context
const childLogger = logger.child({ requestId: "abc-123" });
childLogger.debug("processing_payment", { amount: 99.99 });Core Concepts
Logger
The main entry point implementing the SystemLogger interface. It holds a set of sinks, a base context, and methods for each log level.
Sinks
A sink is any object implementing the LogSink interface:
interface LogSink {
write(record: SystemLog): void | Promise<void>;
}When a log is emitted, it is passed to all registered sinks concurrently. If a sink returns a Promise, it is awaited (with Promise.all) but errors are swallowed to prevent logging failures from crashing the application.
Context
Context is an arbitrary object attached to every log record. It is useful for adding static metadata like service name, environment, or trace IDs. Child loggers merge new context onto the parent’s context.
Built‑in Sinks
ConsoleSink
Writes formatted log lines to stdout or stderr. It automatically detects the runtime:
- In Node.js, uses
process.stdout.write/process.stderr.write. - In browsers, falls back to
console.log,console.error, etc.
Options
| Option | Type | Description |
| --------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| pipeMap | Partial<Record<LogLevel, ConsolePipe>> | Override the output stream for specific levels (default: warn/error → stderr, others → stdout). |
| format | (record: SystemLog) => string | Custom string formatter. Default produces [ISO timestamp] LEVEL event ctx=... data=.... |
Example
const consoleSink = new ConsoleSink({
pipeMap: { error: "stdout" }, // send errors to stdout
format: (r) => `[${r.level}] ${r.event}`,
});EventBusSink
Routes logs to an event bus, either statically or dynamically. This is useful for cross‑component communication, WebSocket broadcasts, or centralised log aggregation.
Requirements
- An event bus that implements the
EventBus<TEventMap>interface (from@asaidimu/utils-events). The event map defines the allowed event names and their payload shapes.
Static Routing – All logs go to a fixed event name.
import { EventBusSink } from '@asaidimu/utils-logger';
const eventBus = /* your event bus instance */;
const sink = new EventBusSink(eventBus, {
event: 'system:log', // static event name
map: (record) => ({ // optional mapping
level: record.level,
message: record.event,
timestamp: record.timestamp,
extra: record.data
})
});Dynamic Routing – Choose the event name per log record.
const sink = new EventBusSink(eventBus, {
resolve: (record) => {
// Route based on log level or event name
return record.level === "error" ? "error:log" : "info:log";
},
map: (record, eventName) => ({
// eventName is the resolved name
severity: record.level,
details: record.data,
}),
});The sink guards against exceptions so that failures in event emission do not break the logging flow.
Fuzz
A helper class that generates random log entries at random intervals. Useful for load testing, demo environments, or stress‑testing your logging pipeline.
import { Fuzz } from "@asaidimu/utils-logger";
import { Logger, ConsoleSink } from "@asaidimu/utils-logger";
const realLogger = new Logger([new ConsoleSink()]);
const fuzz = new Fuzz(realLogger, {
minIntervalMs: 200,
maxIntervalMs: 1500,
allowedLevels: ["info", "warn", "error"],
eventRegistry: {
info: ["user_login", "order_placed", "payment_success"],
error: ["db_timeout", "stripe_failure"],
},
});
// Start emitting random logs every 200–1500ms
fuzz.start();
// Later, stop
fuzz.stop();Options
| Option | Type | Default | Description |
| --------------- | ------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------- |
| minIntervalMs | number | 100 | Minimum delay between logs (ms). |
| maxIntervalMs | number | 1000 | Maximum delay between logs (ms). |
| allowedLevels | LogLevel[] | ['trace','debug','info','warn','error'] | Which levels may be emitted. |
| eventRegistry | Partial<Record<LogLevel, string[]>> | {} | Custom event name pools per level. If empty, random hacker‑style names are generated. |
API Reference
SystemLogger Interface
| Method | Description |
| -------------------------------------------- | --------------------------------------------------------------------------- |
| trace(event: string, data?: object): void | Log at trace level. |
| debug(event: string, data?: object): void | Log at debug level. |
| info(event: string, data?: object): void | Log at info level. |
| log(event: string, data?: object): void | Alias for info. |
| warn(event: string, data?: object): void | Log at warn level. |
| error(event: string, data?: unknown): void | Log at error level. data can be an Error object (see Error handling). |
| write(record: SystemLog): void | Write a pre‑constructed SystemLog record directly. |
| child(context: object): SystemLogger | Create a child logger with additional context (merged over parent). |
Logger Class
The concrete implementation. Constructor:
new Logger(sinks: LogSink[], context?: object)All methods from SystemLogger are available.
LogSink Interface
interface LogSink {
write(record: SystemLog): void | Promise<void>;
}Implement this to create custom sinks (e.g., file, HTTP, Sentry, etc.).
SystemLog Structure
| Field | Type | Description |
| ----------- | ---------- | ---------------------------------------------------- |
| level | LogLevel | Severity level. |
| event | string | Distinct event name (e.g., "user_login"). |
| data | object | (Optional) Payload specific to the log. |
| context | object | (Optional) Static metadata from the logger instance. |
| timestamp | number | Unix timestamp in milliseconds (auto‑generated). |
LogLevel
type LogLevel = "trace" | "debug" | "info" | "warn" | "error";Priority order (low to high): trace (10), debug (20), info (30), warn (40), error (50). The constant LogLevelPriority provides these numeric values.
Error Handling
When you pass an Error object (or any subclass) to logger.error(), the logger automatically extracts structured information:
- For plain
Error: it capturesname,message, andstack. - For
SystemError(from@asaidimu/utils-error): it additionally extractscode,category,severity,path,operation,issues,httpStatus,action, andcause.
All extracted fields are placed under the error key in the log’s data property, ensuring consistent error reporting across your system.
License
MIT © Saidimu
Contributing & Issues
Found a bug or want to suggest an improvement? Please open an issue on the GitHub repository.
For more details, visit the homepage.
