Skip to content

Redaction ​

Set redact on the logger and logixia masks sensitive data in the payload, in nested errors and in the message string before any transport or plugin sees the entry.

ts
import { createLogger } from 'logixia';

const logger = createLogger({
  appName: 'api',
  redact: {
    paths: ['req.headers.authorization', '*.password', 'user.creditCard'],
    patterns: [/sk-[a-z0-9]{32,}/gi],
    censor: '[REDACTED]',
    autoDetect: 'conservative',
  },
});

await logger.info('login', { user: { name: 'ada', creditCard: '4111 1111 1111 1111' } });
// user.creditCard is replaced with [REDACTED]

Options ​

OptionTypeDefaultDescription
pathsstring[]noneDot-notation paths whose values are replaced.
excludePathsstring[]nonePaths exempt from pattern and autoDetect value scanning.
patternsRegExp[]noneRegexes applied to string values; each match is replaced.
censorstring'[REDACTED]'The replacement text.
autoDetectboolean | 'conservative' | 'aggressive'offBuilt-in secret and PII rules, added to your own.

Path syntax ​

Paths are dot-separated and matched case-insensitively against the full path of each value.

PatternMatches
passwordA top-level password.
user.passwordpassword directly under user.
*.tokentoken one level down, under any key.
req.headers.*Every direct child of req.headers.
**.passwordpassword at any depth, including the top level.
req.**req and everything below it.

* matches exactly one segment; ** matches any number, including zero.

Patterns ​

patterns run over string values (and over the log message), so they catch secrets that sit in free text:

ts
import { createLogger } from 'logixia';

const logger = createLogger({
  appName: 'api',
  redact: { patterns: [/Bearer\s+\S+/gi] },
});

await logger.error('upstream rejected Bearer eyJhbGciOi.payload.sig');
// message: upstream rejected [REDACTED]

Only patterns apply to the message string; path rules need an object to walk.

autoDetect ​

autoDetect: true (or 'conservative') adds:

  • patterns for JWTs, Bearer values, sk- / pk- / api- / key- / secret- / token- prefixed keys, AWS access key IDs and AWS secret access keys;
  • name-based rules (**.<name>) for common secret field names: password, passwd, secret, token, apiKey, api_key, accessToken, access_token, refreshToken, refresh_token, authorization, credentials, privateKey, private_key, clientSecret, client_secret, cookie, set-cookie, x-api-key, session, proxy-authorization, x-auth-token, x-access-token and the AWS secret key names.

'aggressive' adds patterns for email addresses, US Social Security numbers, card numbers, US phone numbers and IPv4 addresses, plus field names such as email, phone, ssn, dob, creditCard, cardNumber, cvv and ipAddress. Aggressive matching is blunt: a long run of digits can look like a card or phone number. Use excludePaths for identifiers that trip it:

ts
import { createLogger } from 'logixia';

const logger = createLogger({
  appName: 'api',
  redact: { autoDetect: 'aggressive', excludePaths: ['**.vendorRefId', '**.traceId'] },
});

await logger.info('synced', { vendorRefId: '1234567890123', email: 'ada@example.com' });
// vendorRefId is kept, email is redacted

If a field matches both paths and excludePaths, paths wins.

What is covered ​

  • Nested objects and arrays, class instances, toJSON() results and nested Error objects (name, message, stack and cause chain are serialized and then scanned).
  • Circular references become [Circular]. The original object is never modified.
  • The log message string, for patterns and autoDetect patterns.
  • HTTP request headers and query parameters have their own lists in the HTTP logging middleware. Set redact.queryParams (for example ['invite']) to add query parameter names that the NestJS exception filter and trace middleware redact in logged URLs and error messages.

Limits and failure behaviour ​

Redaction is bounded so a hostile payload cannot stall the process:

  • nesting deeper than 32 levels becomes [MaxDepth]; more than 10,000 objects in one pass becomes [MaxNodes];
  • strings longer than 16,384 characters are cut before pattern scanning and the tail is dropped, never logged unscanned;
  • secret patterns use bounded quantifiers so scanning stays linear.

If redaction itself throws, logixia fails closed: the payload is replaced with { redaction: '[RedactionFailed]' } and the message with [RedactionFailed], and the raw data is not emitted.

Use it outside the logger ​

ts
import { applyRedaction, redactObject } from 'logixia';

const safe = applyRedaction({ user: { password: 'x' } }, { paths: ['**.password'] });
console.log(safe); // { user: { password: '[REDACTED]' } }

const copy = redactObject({ token: 't' }, { paths: ['token'] });
console.log(copy);

Both return new objects and never mutate their input.

Redact at the logger, not in each transport

Because redaction runs before transports and plugins, one redact block covers file, database, cloud and analytics outputs together. See Security for the other protections around secrets.

Released under the MIT License.