Fully typed, production-grade logging toolkit for Node.js applications. Built on top of Winston with first-class TypeScript support, rotating file transports, timezone mirroring, and an HTTP middleware that outperforms traditional solutions.
- ✅ Multi-target logging (console, per-module files, shared files, custom transports). Console output stays enabled but omits timestamps so only files capture the full timeline.
- ✅ Daily rotation with independent retention rules
- ✅ Guaranteed UTC timestamps plus optional verified IANA timezones
- ✅ Automatic log directory creation (including nested module scopes)
- ✅ Batteries-included Express middleware with structured payloads
- ✅ Zero-config ESM & CommonJS builds, rich IntelliSense docs
- ✅ Comprehensive Jest suite with 100% coverage
npm install @hiprax/loggerThe package ships with precompiled dual builds. No transpilation is required in consuming projects.
import { createLogger } from "@hiprax/logger";
const securityLogger = createLogger({
moduleName: "security/failedLogins",
extraTimezones: ["Europe/London"],
});
securityLogger.warn(`Failed login attempt\nEmail: ${email}\nIP: ${req.realIp}`);CommonJS usage is equally simple:
const { createLogger } = require("@hiprax/logger");| Option | Type | Default | Description |
|---|---|---|---|
moduleName |
string |
'global' |
Label used in log lines and for module-specific files. Supports nested scopes (security/failedLogins). |
logDirectory |
string |
<process.cwd()>/logs |
Target directory (auto-created). |
level |
LogLevel |
'info' |
Default level for all transports. |
consoleLevel |
LogLevel |
level |
Console-specific level. |
includeConsole |
boolean |
true |
Enables console logging. Console lines omit timestamps; files keep the full multi-timezone output. |
includeFile |
boolean |
true |
Enables module-specific rotating file logging. |
includeGlobalFile |
boolean |
true |
Enables shared rotating file logging. |
globalModuleName |
string |
'all-logs' |
Label for the shared log file. |
extraTimezones |
string | string[] |
[] |
Additional IANA zones to render beside UTC. Validity is enforced. |
rotation |
RotationStrategy |
20 MB / 14 days / daily | Rotation config for the module file. |
globalRotation |
RotationStrategy |
rotation |
Override rotation for the shared file. |
additionalTransports |
winston.transport[] |
[] |
Appends custom transports (e.g., HTTP, Kafka). |
RotationStrategy
interface RotationStrategy {
maxSize?: string; // e.g., '20m', '200k'
maxFiles?: string; // e.g., '14d', '30'
datePattern?: string; // default: 'YYYY-MM-DD'
zippedArchive?: boolean;
}import express from "express";
import { createRequestLogger } from "@hiprax/logger";
const app = express();
app.use(express.json());
app.use(
createRequestLogger({
includeRequestBody: true,
includeRequestHeaders: ["authorization"],
includeResponseHeaders: true,
maskBodyKeys: ["password", "token"],
enrich: (req) => ({ tenantId: req.headers["x-tenant-id"] }),
}),
);When includeHttpContext is enabled, the middleware attaches rich structured metadata via info.http while emitting a concise human-readable message. It relies on plain Node events (finish/close) and does not depend on on-finished, keeping the surface secure and modern.
| Option | Type | Default | Description |
|---|---|---|---|
logger |
winston.Logger |
Scoped http logger |
Provide your own logger or reuse the scoped one. |
level |
LogLevel | (status: number) => LogLevel |
Auto (info/warn/error) |
Override severity per response. |
label |
string |
'http' |
Included in the auto-generated logger name (http/<label>). |
messageBuilder |
(entry) => string |
"METHOD URL status latency (event)" |
Customize the final message string. |
skip |
(req, res) => boolean |
false |
Allowlist/denylist support. |
enrich |
(req, res, durationMs) => Record<string, unknown> |
undefined |
Inject extra context (e.g., tenant, user). |
includeRequestHeaders |
boolean | string[] |
false |
Toggle or limit header emission. |
includeResponseHeaders |
boolean | string[] |
false |
Same as above for responses. |
includeRequestBody |
boolean |
false |
Logs parsed body (with redaction). |
maskBodyKeys |
string[] |
[] |
Keys replaced with [REDACTED]. Applies deeply, including arrays. |
maxBodyLength |
number |
3000 |
Caps serialized body size to prevent log floods. |
includeHttpContext |
boolean |
false |
Adds the structured payload under info.http. |
loggingEnabled |
boolean |
true |
Hard enable/disable switch. |
loggingMode |
RequestLoggingMode |
'always' |
Env-aware control. Supports 'dev-only', 'prod-only', 'test-only', or custom rules. |
- Use
loggingMode: 'dev-only'to automatically log whenNODE_ENV,APP_ENV, orENVequalsdev,development, orlocal(case-insensitive). - Use
loggingMode: 'prod-only'to emit logs exclusively when the env matchesprod,production, orlive. - Use
loggingMode: 'test-only'to limit logging totest,testing,qa, orstaging. - Provide a custom configuration, e.g.:
loggingMode: { sources: ["DEPLOYMENT_STAGE"], allow: ["staging", "qa"], fallback: false }
- Pair
loggingEnabledwith boolean expressions (loggingEnabled: process.env.FEATURE_LOGS === "on") for one-line toggles. - Keep log output lightweight unless needed by turning
includeHttpContexton only when you want the structured payload.
- UTC is always logged.
- Additional zones must be valid IANA identifiers (
moment-timezonevalidation). Invalid entries throwInvalidTimezoneError. - Provide a single string or an array:
extraTimezones: ['Europe/London', 'America/New_York'].
import { PassThrough } from "node:stream";
import winston from "winston";
const capture = new PassThrough();
const logger = createLogger({
moduleName: "audit",
includeConsole: false,
includeFile: false,
includeGlobalFile: false,
additionalTransports: [new winston.transports.Stream({ stream: capture })],
});| Command | Description |
|---|---|
npm run build |
Generates dual ESM/CJS bundles plus type declarations. |
npm test |
Runs Jest with 100% coverage enforcement. |
npm testts-jestcompiles TypeScript on the fly.- Coverage thresholds are locked at 100% global branches/functions/lines/statements.
- All filesystem interactions are sandboxed to the configured log directory.
- Timezones are validated against the Moment timezone database before use.
- Sensitive request payload fields can be masked recursively via
maskBodyKeys.
- Clone the repo and install dependencies.
- Run
npm testto ensure the suite passes before submitting changes. - Follow the existing TypeScript, linting, and documentation patterns.
MIT License - see LICENSE file for details.