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

@storyous/logger

v1.3.3

Published

``` typescript import { initLogger } from '@storyous/logger';

Readme

logger

Usage

import { initLogger } from '@storyous/logger';

const config = {
    env: process.env.NODE_ENV,
    logging: {
        console: {
            colorize: true,
            
            // Use for localhost development only, it will output nicely readable logs
            // and if error appears the stack will be clickable in webstorm
            prettyOutput: true,
        },
        loggly: {
            silent: true,
            token: process.env.LOGGLY_TOKEN || '',
            subdomain: 'storyous',
            tags: ['serviceName', process.env.NODE_ENV],
            json: true
        },
        sentry: {
            dsn: process.env.SENTRY_DSN || '',
            level: 'error',
            silent: true
        }
    }
}

const logger = initLogger(config.env, config.logging);

// define new logger for specific module
const logModule = logger.module('extraLogger');

export default logger;

Requirements

Node.js >= 22.

Transports

Loggly

Uses winston-loggly-bulk (the package named in Loggly's Node.js docs). Its dependency node-loggly-bulk pins [email protected], which npm audit flags — see ADR 0001 for why this is accepted (the only request is a POST of log data to a fixed Loggly URL; none of the advisories are reachable that way). If you want a clean audit in your project, add to your root package.json (scoped, so your own axios is not affected):

"overrides": { "node-loggly-bulk": { "axios": "^1.20.0" } }

Sentry

Uses @sentry/node 10. Only dsn (from sentry.dsn) and environment (from the env argument) are passed to Sentry.init. Note that @sentry/node >= 8 instruments http/https via a global require hook when initialised; database and framework instrumentation is only enabled when tracing is turned on (it is not).

Upgrading to 1.2.6

  • Minimum Node.js version is 22.
  • @sentry/node 6 → 10: no config changes needed for the documented options.
  • Loggly: unchanged (winston-loggly-bulk), see the note above about npm audit.

PII redaction (since 1.3.0)

Every log record is passed through a PII/secret redaction format before it reaches any transport (console, Loggly, and the Sentry transport including scope.setExtras(info.meta)). Sentry events that never pass through winston at all - the ones its own integrations build, such as HTTP breadcrumbs, request data and the global uncaught-exception handler - are covered separately by beforeSend/beforeBreadcrumb. It is on by default and covers:

  • dropped subtrees - keys like customer, contact, address are replaced with [REDACTED] without walking inside, so a renamed field cannot leak;
  • redacted values - keys like email, phone, firstName, vatId, bankAccount, responseBody are replaced with [REDACTED];
  • masked secrets - keys like token, password, clientSecret, sessionId and the authorization / x-authorization / x-api-key / x-api-router-secret request headers keep only their last 4 characters (********abcd) so log lines stay correlatable. On the authorization headers the authentication scheme is kept as well (Bearer ********abcd) - it is not a secret and it says which credential the caller used;
  • cookies - cookie, set-cookie and the cookies object are replaced with [REDACTED] whole: a cookie header is a jar of independent values, so a masked tail would hide the last one only. Sentry's request handler attaches all three to every event;
  • free text - email addresses are redacted inside any string value, including JSON.stringify(...)-ed payloads (they are parsed, redacted and stringified back);
  • XML/SOAP payloads - the same key sets are applied to element names, attribute names and CDATA content; namespace prefixes are ignored (<ns2:password> = <password>), and a dropped-subtree element loses its children and all of its attributes;
  • query strings and urlencoded bodies - ?email=...&access_token=... and login=x&password=y are matched by parameter name, percent-encoding included, so the url that basicLogMiddleware logs on every request is covered;
  • errors - Error instances (incl. @storyous/fetch errors with meta.request.body / meta.response.body) are redacted on a clone; message/stack are kept. name is a personal key everywhere else, but on an error it is the error class, so it is kept too - both for an error nested in the payload (log.error('msg', { err })) and for one passed as the meta argument (log.error('msg', err)), where winston flattens it into the record. A record only counts as an error when it carries a stack and its name reads like a class identifier, so a real person's name (Jan Novak) stays [REDACTED] even next to a stack.

Key matching ignores case and separators (phoneNumber = phone_number = Phone-Number). Call sites should still log ids instead of documents - the redaction is the last line of defense, not a licence.

Per-service tuning via the redaction key of the log config:

initLogger(config.env, {
    ...config.logging,
    redaction: {
        disabled: false,             // turn the whole format off (not recommended)
        allowedKeys: ['name'],       // keys excluded from every redaction list
        extraDroppedKeys: [],        // additional whole-subtree drops
        extraRedactedKeys: [],       // additional [REDACTED] keys
        extraMaskedKeys: [],         // additional tail-masked keys
    },
});