No-op fast path
When level: 'none', masking is never executed — the method returns before reaching the masking step.
Accidentally logging secrets is one of the most common and costly security mistakes in application development. log-wiz solves this natively — no extra code, no middleware, no wrappers.
Every metadata object passed to any log call is deep-cloned and scanned before being written to any transport.
Keys matching the masked-key list have their values replaced with the string [MASKED].
wiz.info("event", { meta: { password: "secret", userId: 42 } }) ↓ maskSensitiveData(meta, maskedKeys) ↓ { password: "[MASKED]", userId: 42 } ↓ written to transportThe original object is never mutated.
These keys are masked out of the box. Matching is case-insensitive and ignores -, _, and spaces.
| Key | Also matches |
|---|---|
password | Password, PASSWORD |
passwd | Passwd |
token | Token, TOKEN, accessToken, access_token |
refreshtoken | refreshToken, refresh_token |
secret | Secret, SECRET |
authorization | Authorization, AUTHORIZATION |
cookie | Cookie |
card_number | cardNumber, card-number |
cvv | CVV |
ssn | SSN |
apikey | apiKey, api_key, API_KEY |
privatekey | privateKey, private_key |
Masking is fully recursive — it descends into nested objects and arrays at any depth:
wiz.info("checkout", { meta: { user: { name: "Alice", payment: { card_number: "4111-1111-1111-1111", // -> [MASKED] cvv: "123", // -> [MASKED] expiry: "12/26", // visible }, }, items: [ { sku: "ABC", secret: "x" }, // secret -> [MASKED] { sku: "DEF", price: 9.99 }, // visible ], },});log-wiz uses a WeakSet to track visited objects and safely handles circular references:
const req: Record<string, unknown> = { id: "r-1", method: "POST" };req["self"] = req; // circular reference!
wiz.info("request", { meta: req });// -> { id: "r-1", method: "POST", self: "[Circular]" }// Never throws RangeError, never hangsExtra keys are merged with the built-in defaults — defaults stay active:
import { Wiz } from "@gouranga_samrat/log-wiz";
const logger = new Wiz({ maskedKeys: ["nationalId", "medicalRecordNumber", "driverLicense"],});
logger.info("patient record accessed", { meta: { patientName: "Jane Doe", // visible nationalId: "123-45-6789", // -> [MASKED] (custom) medicalRecordNumber: "MRN-00042", // -> [MASKED] (custom) token: "session-token", // -> [MASKED] (built-in default still active) },});Set replaceDefaultMaskedKeys: true to use only your custom list:
const logger = new Wiz({ maskedKeys: ["internalRef"], replaceDefaultMaskedKeys: true,});
logger.info("audit", { meta: { internalRef: "REF-999", // -> [MASKED] token: "still-visible", // visible — defaults were replaced action: "EXPORT", },});Masked keys can be updated at any time via setConfig():
logger.setConfig({ maskedKeys: ["newSecret", "anotherField"],});No-op fast path
When level: 'none', masking is never executed — the method returns before reaching the masking step.
Level-filtered
If an entry is below the configured level threshold, masking is skipped entirely. Only entries that will be written incur the clone cost.
Non-mutating
Deep clone ensures the original object is never modified. Safe to log request bodies without side effects.
WeakSet lifecycle
The WeakSet is allocated per call and garbage-collected immediately — no global state, no memory leaks.