npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hiprax/logger

v0.7.0

Published

Fully typed Winston-based logger with rotation, timezone support, and Express middleware

Readme

@hiprax/logger

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.

License: MIT npm version TypeScript Node.js


Table of Contents


Features

  • Multi-target logging (console, per-module files, shared global file, custom transports). Console output is colorized and omits timestamps so only rotating files capture the full timeline.
  • Daily rotation with independent retention rules for module and global files
  • Guaranteed UTC timestamps plus optional verified IANA timezone mirrors
  • Automatic log directory creation (including nested module scopes like security/failedLogins)
  • Batteries-included Express middleware with structured HTTP payloads, body redaction, and header filtering
  • Graceful fallback for unknown logger methods (warns once, re-routes to info())
  • Environment-aware request logging with built-in presets (dev-only, prod-only, test-only) and custom rules
  • Zero-config ESM & CommonJS dual builds with rich IntelliSense documentation
  • Comprehensive Jest suite with 100% coverage enforcement

Installation

npm install @hiprax/logger

The package ships with precompiled dual builds (.mjs for ESM, .cjs for CommonJS). No transpilation is required in consuming projects.

Quick Start

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:

const { createLogger } = require("@hiprax/logger");

API

createLogger(options?: LoggerOptions)

Creates a fully configured Winston logger with safe defaults, rotating files, UTC timestamps, and optional timezone mirrors. Instances are cached by moduleName + logDirectory — calling createLogger() with the same configuration from multiple files returns the same logger, preventing duplicate transports, file handles, and console output.

| 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 override. | | includeConsole | boolean | true | Enables console logging. Console lines are colorized and omit timestamps. | | 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 rendered beside UTC. Validity is enforced; invalid zones throw InvalidTimezoneError. | | rotation | RotationStrategy | 20 MB / 14 days / daily | Rotation config for the module file. | | globalRotation | RotationStrategy | rotation | Override rotation for the shared file. Falls back to rotation when omitted. | | additionalTransports | winston.transport[] | [] | Appends custom transports (e.g., HTTP, Kafka, Stream). |

RotationStrategy

interface RotationStrategy {
  maxSize?: string; // e.g., '20m', '200k'
  maxFiles?: string; // e.g., '14d', '30'
  datePattern?: string; // default: 'YYYY-MM-DD'
  zippedArchive?: boolean;
}

Unknown method fallback: If you call a method that does not exist on the logger (e.g., logger.success("done")), it will log a warning once and route the message through info() instead of throwing.

Instance caching: Logger instances are cached by moduleName + logDirectory. The same call from different files returns the same instance. Use resetLoggerRegistry() to clear the cache (useful for testing or hot-reload scenarios):

import { resetLoggerRegistry } from "@hiprax/logger";

resetLoggerRegistry(); // clears all cached instances

Request Logging Middleware

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) with a guard against double-logging, and does not depend on on-finished.

| Option | Type | Default | Description | | ------------------------ | --------------------------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------- | | logger | winston.Logger | Scoped http logger | Provide your own logger or use the auto-created scoped one. | | level | LogLevel \| (status: number) => LogLevel | Auto (info/warn/error) | Override severity. The default maps 5xx to error, 4xx to warn, rest to info. | | label | string | — | 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 | — | Return true to skip logging for specific requests. | | enrich | (req, res, durationMs) => Record<string, unknown> | — | Inject extra context (e.g., tenant, user). Attached under entry.context. | | includeRequestHeaders | boolean \| string[] | false | true for all headers, or an array of allowed header names. | | includeResponseHeaders | boolean \| string[] | false | Same as above for response headers. | | includeRequestBody | boolean | false | Logs parsed request body (with redaction support). | | maskBodyKeys | string[] | [] | Keys replaced with [REDACTED]. Applies deeply and case-insensitively, including arrays. | | maxBodyLength | number | 3000 | Caps serialized body size to prevent log floods. Truncated bodies end with .... | | includeHttpContext | boolean | false | Attaches the structured RequestLogEntry payload under info.http. | | loggingEnabled | boolean | true | Hard enable/disable switch. | | loggingMode | RequestLoggingMode | 'always' | Environment-aware control. See below. |


Environment-Aware Request Logging

The loggingMode option controls when request logging is active, based on environment variables.

Built-in presets:

| Mode | Matched env values (case-insensitive) | Env sources checked (in order) | | ------------- | ------------------------------------- | ------------------------------ | | 'always' | Always enabled | — | | 'never' | Always disabled | — | | 'dev-only' | dev, development, local | NODE_ENV, APP_ENV, ENV | | 'prod-only' | prod, production, live | NODE_ENV, APP_ENV, ENV | | 'test-only' | test, testing, qa, staging | NODE_ENV, APP_ENV, ENV |

Custom configuration:

createRequestLogger({
  loggingMode: {
    sources: ["DEPLOYMENT_STAGE"], // env vars to check (priority order)
    allow: ["staging", "qa"], // values that enable logging
    fallback: false, // behavior when no env source is found
  },
});

Tips:

  • Pair loggingEnabled with boolean expressions for one-line toggles: loggingEnabled: process.env.FEATURE_LOGS === "on"
  • The loggingMode decision is evaluated once at middleware creation time, not per-request
  • Keep log output lightweight by enabling includeHttpContext only when you need the full structured payload

Timezone Handling

  • UTC is always logged in file output.
  • Additional zones must be valid IANA identifiers (validated by moment-timezone). Invalid entries throw InvalidTimezoneError.
  • Provide a single string or an array:
createLogger({
  extraTimezones: ["Europe/London", "America/New_York"],
});
  • Duplicate timezone entries are automatically deduplicated.

Custom Transports

You can disable all built-in transports and use only custom ones:

import { PassThrough } from "node:stream";
import winston from "winston";
import { createLogger } from "@hiprax/logger";

const capture = new PassThrough();
const logger = createLogger({
  moduleName: "audit",
  includeConsole: false,
  includeFile: false,
  includeGlobalFile: false,
  additionalTransports: [new winston.transports.Stream({ stream: capture })],
});

Log Output Format

File output (with timestamps and extra timezones):

UTC: 2025-06-15 14:30:22
Europe/London: 2025-06-15 15:30:22
[WARN] (security/failedLogins)
Failed login attempt
Email: [email protected]
IP: 192.168.1.1

Console output (colorized, no timestamps):

[WARN] (security/failedLogins)
Failed login attempt
Email: [email protected]
IP: 192.168.1.1

Request middleware default message:

POST /auth/login 201 12.45ms (completed)

When includeHttpContext is enabled, the structured RequestLogEntry is attached under info.http:

{
  "event": "completed",
  "method": "POST",
  "url": "/auth/login",
  "statusCode": 201,
  "responseTimeMs": 12.45,
  "contentLength": 256,
  "ip": "127.0.0.1",
  "userAgent": "Mozilla/5.0...",
  "requestId": "req-abc-123"
}

Scripts

| Command | Description | | -------------------- | ------------------------------------------------------ | | npm run build | Generates dual ESM/CJS bundles plus type declarations. | | npm test | Runs Jest with 100% coverage enforcement. | | npm run lint | Runs ESLint across source and test files. | | npm run type-check | Runs the TypeScript compiler in check-only mode. |

Testing & Coverage

npm test
  • ts-jest compiles TypeScript on the fly with ESM support.
  • Coverage thresholds are locked at 100% for branches, functions, lines, and statements.
  • Tests run in band (--runInBand) for deterministic behavior.

Security Notes

  • All filesystem interactions are sandboxed to the configured log directory.
  • Module names are sanitized to prevent path traversal (dangerous characters are replaced with hyphens).
  • Timezones are validated against the Moment timezone database before use.
  • Sensitive request payload fields can be masked recursively via maskBodyKeys (case-insensitive, handles nested objects and arrays).
  • Circular references in request bodies are safely handled (replaced with [Circular]).

Contributing

  1. Clone the repo and install dependencies.
  2. Run npm test to ensure the suite passes before submitting changes.
  3. Follow the existing TypeScript, linting, and documentation patterns.

License

MIT License - see LICENSE file for details.

Links