@storyous/logger
v1.3.3
Published
``` typescript import { initLogger } from '@storyous/logger';
Maintainers
Keywords
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/node6 → 10: no config changes needed for the documented options.- Loggly: unchanged (
winston-loggly-bulk), see the note above aboutnpm 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,addressare replaced with[REDACTED]without walking inside, so a renamed field cannot leak; - redacted values - keys like
email,phone,firstName,vatId,bankAccount,responseBodyare replaced with[REDACTED]; - masked secrets - keys like
token,password,clientSecret,sessionIdand theauthorization/x-authorization/x-api-key/x-api-router-secretrequest 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-cookieand thecookiesobject 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
CDATAcontent; 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=...andlogin=x&password=yare matched by parameter name, percent-encoding included, so theurlthatbasicLogMiddlewarelogs on every request is covered; - errors -
Errorinstances (incl.@storyous/fetcherrors withmeta.request.body/meta.response.body) are redacted on a clone;message/stackare kept.nameis 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 astackand itsnamereads 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
},
});