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

@akms/logger

v0.2.1

Published

Winston-based logger and console helper with environment-aware configuration, sensitive data masking, daily log rotation, and call-site preserving console bindings.

Readme

@akms/logger

🔒 Internal use only — built for our organization's services.

Winston-based logger for Node.js services with a winston-free browser counterpart. The same $logger import works in SSR loaders and client components; the bundler picks the right entry automatically. On the browser side $logger is backed by a getter + native console bind, so DevTools "source" links point to the caller line. Environment-aware log level, sensitive data masking, and hourly log rotation.

📦 Installation

npm install @akms/logger

🚀 Usage

import { $logger, createLogger } from "@akms/logger";

$logger.info("server started", { port: 3000 });

const logger = createLogger();
logger.debug("debug info", { context: "MyService" });

const scoped = $logger.child({ requestId: "req-123" });
scoped.error("payment failed", { orderId: 42 });

On Node, child(bindings) adds metadata fields to every record via winston's binding mechanism. On the browser, the bindings object is passed as a separate argument to console.* (not stringified), so DevTools renders it as an expandable object you can inspect inline.

🧵 Request-scoped logging

Request scoping is the application's concern, not the logger's — own the AsyncLocalStorage in your server and put a bound child logger inside it. The logger stays generic; child(bindings) merges those fields into every record (call-time fields win on key collision), and you reach the bound logger from any depth without threading it through the call stack.

import { AsyncLocalStorage } from "node:async_hooks";
import { $logger, type Logger } from "@akms/logger";
import { randomUUID } from "node:crypto";

const als = new AsyncLocalStorage<Logger>();
export const reqLogger = (): Logger => als.getStore() ?? $logger;

// at the request entry point (e.g. express middleware)
function requestContext(req, res, next) {
    const logger = $logger.child({
        req_id: randomUUID(),
        ip: req.ip,
        method: req.method,
        url: req.originalUrl,
    });
    als.run(logger, () => next());
}

// later, after JWT verification — re-bind with the enriched context
function onAuth(payload) {
    const enriched = reqLogger().child({ member_id: payload.sub, session_id: payload.sid });
    als.enterWith(enriched);
}

// anywhere downstream in the same async chain
reqLogger().info("payment processed", { orderId: 42 });
// → metadata includes req_id, ip, method, url, member_id, session_id, orderId

🔒 Don't put raw tokens in the bindings (security) — attach a session_id or a token prefix/hash instead. Avoid the context key too; it collides with the child-logger label (e.g. "ServiceName.methodName").

🌐 Environments

The package ships two entries, picked automatically by exports conditions:

| Condition | Entry | Contents | |-----------|-------|----------| | browser / workerd / edge-light | lib/browser.js | Call-site-preserving console adapter — no winston, no Node built-ins. | | import / require | lib/index.js | Full winston logger (file transport, masking, colorized output). |

So a single import { $logger } from "@akms/logger" resolves to winston on the SSR server (React Router loader/action, Next.js route handler, 'use server' module) and to the call-site-preserving console wrapper in the client chunk. A build-time guard (scripts/verify-browser-bundle.mjs) fails the build if winston or any Node built-in leaks into the browser bundle.

The browser entry exposes the same Logger shape (fatal/error/warn/info/debug/verbose/child) so consumer code does not need to branch on environment.

⚠️ Do not add @akms/logger to Next.js transpilePackages or Vite's ssr.noExternal. Doing so can drag the Node entry into client chunks and bypass the browser condition.

⚙️ Environment Variables

Only the Node entry reads these. In the browser entry they are ignored.

| Variable | Description | Default | |----------|-------------|---------| | LOG_LEVEL | fatal / error / warn / info / debug / verbose (trace alias) | info (prod), debug (dev) | | LOG_SENSITIVE_KEYS | Comma-separated keys to mask recursively (e.g., password,token) | - | | LOG_USE_SINGLE_LINE_OBJ | Single-line object output | false | | LOG_USE_FILE | Write to file with hourly rotation (gzip). Recommended true in prod/testprod. | false | | LOG_DIR | Directory log files are written to. Empty → <cwd>/logs. In prod, prefer an OS-standard or absolute path outside the PM2 cwd (e.g. /var/log/<org>/<service>). | <cwd>/logs | | LOG_FILE_PREFIX | Filename prefix to avoid collisions when several services/envs share one directory. Usually <service>_<MODE>. Attached to the filename (not the folder), so a copied file is self-identifying. E.g. auth_api_development2026_06/0601/auth_api_development_2026_0601_15.log. Empty → no prefix. | - | | LOG_FILE_MAX_SIZE | Max size per file; on overflow, splits within the same hour as .1, .2. E.g. 20m, 1g. Empty → unlimited. | unlimited | | LOG_FILE_MAX_DAYS | Retention. 30d (days) or 30 (file count). Empty → unlimited (never auto-deletes). Days recommended — count-based retention (30 files = 30 hours under hourly rotation) may already have purged yesterday's logs during an incident. | unlimited | | MODE | development / production / testprod | - |

Both process.env (Node.js) and import.meta.env (Vite) are supported. process.env takes priority when defined, so an operator-set value wins on Node; Vite client builds (where process is undefined) fall back to import.meta.env.

🔧 Exports

| Name | Purpose | |------|---------| | $logger, createLogger | Logger — singleton / factory. Winston on Node, call-site-preserving console adapter on the browser. | | getLoggerOptions | Inspect resolved winston config (Node entry only — no-op in the browser entry). | | Logger, LoggerOptions | Type exports. Logger is environment-agnostic; LoggerOptions is winston-specific on Node and a no-op Record on the browser. |

🛝 Playground

A React Router 7 SSR sandbox lives in playground/ for verifying the dual-entry split end-to-end. The same $logger import resolves to winston in the SSR loader (terminal output) and to the call-site-preserving console adapter in the client chunk (DevTools).

npm run build      # populate lib/ (or `npm run watch` for HMR alongside)
npm run playground # http://localhost:3000

📒 Changelog

See CHANGELOG.md.