- Updated colorize.ts so values now follow the color of their associated key, using a slightly different shade instead of the exact same color to make key/value pairs easier to visually distinguish while still clearly belonging together.
IColorConfigurationkey slots removed. The interface no longer includeskey,levelKey,messageKey,fileNameKey,logCallStackKey,packageNameKey,timestampKey, orerrCallStackKey. These slots were used to assign a single fixed color to all keys of each type. They are replaced by hash-based coloring (see below). If you were reading or writing these slots on a customIColorConfiguration, remove those references. Value-coloring slots (errorLevel,warnLevel,nonErrorMessage,fileName,timestamp,errCallStack, etc.) are unchanged.
- Unique per-property key colors derived from property name. Every JSON key now gets its own distinct ANSI truecolor (24-bit RGB) derived from a djb2 hash of the key name. The same key name always produces the same color across all log lines, regardless of call site, log level, or log order. This makes it easy to visually track specific fields like
userIdorrequestIdacross many log entries — each field consistently appears in its own color. - Terminal background auto-detection. When
CONSOLE_LOG_COLORIZE=true, the colorizer now detects whether the terminal background is dark or light and adjusts key colors accordingly. Dark backgrounds (the most common developer setup) get bright, high-lightness colors. Light backgrounds get darker, more readable tones. Detection checks, in order:CONSOLE_LOG_COLORIZE_BACKGROUNDenv var override,COLORFGBG(xterm/rxvt/Konsole),TERM_PROGRAM(Apple_Terminal→ light,vscode→ dark),WT_SESSION(Windows Terminal → dark),VTE_VERSION(GNOME Terminal → dark). Falls back to dark-background behavior when no signal is found. CONSOLE_LOG_COLORIZE_BACKGROUNDoverride. Set todarkorlightto force a specific color palette regardless of terminal detection. Useful in CI environments, scripts, or when auto-detection is wrong.
- Replaced the regex-based
colorJson(...)token matcher with a linear JSON scanner, removing the polynomial-time escaped-string path flagged by CodeQL while preserving existing ANSI color behavior.
- Unified the logger configuration model so settings can be supplied either as direct
LoggerAdaptToConsole({...})options or as environment variables. This adds top-level programmatic aliases for the existing env-backed flags and adds env-var forms for log level, debug-string capture, static custom fields, redaction, hook timeout, and advanced hook references. - Key colors are cached per name and theme across log calls, so repeated keys (e.g.
level,message,userId) pay the hash cost only once per process lifetime.
- Added regression coverage for long escaped-quote payloads in
colorJson(...), ensuring colorized output still round-trips without content changes. - Added regression coverage for the unified configuration surface, including top-level flag aliases, env-driven log level/debug/custom-options/redaction behavior, and env-resolved hook callbacks.
- Added end-to-end regression coverage for colorized logger output through the real
LoggerAdaptToConsole(...)path, including direct top-levelcolorizeusage and thetransformOutput + colorizeandredact + colorizecombinations. The new tests assert both ANSI emission and that the JSON payload remains correct after stripping ANSI codes. - Added hash-based key color tests: same key name produces same color across calls, different key names produce different colors, truecolor codes are emitted, light and dark themes produce visually distinct colors (verified by RGB sum thresholds).
- Hardened browser-like host detection so the logger treats
globalThis.documentandwindow.documentas DOM signals when deciding whetherprocess.stdout.write(...)is trustworthy. This closes anotheresm.sh-style recursion path where a browser host exposes a fake Nodestdoutshim throughtty.mjs.
- Added regression coverage for browser-like hosts that expose DOM only through
window.document, ensuring the logger still avoids fakestdoutwrites in that shape.
- Added a second defensive safeguard for browser-style hosts where even the saved original
console.log(...)feeds back into the patched console path. Re-entrant feedback during the logger's own write/fallback emission is now dropped instead of recursively re-logging logger output.
- Upgraded dev-dependency np package version to 11.1.0
- Upgraded dev-dependency jest package version to 30.3.0
- Narrowed browser-like output handling to ignore fake
process.stdout.write(...)shims in hosts likecode.esm.sh, preventing recursiveconsole.log(...)loops andMaximum call stack size exceededwhile preserving direct stdout writes in real Node.js. See details in docs/code-esm-sh-stdout-recursion.md
- Made the Jest contributor/test stack ESM-aware without changing library runtime behavior. Jest now transpiles the ESM-only
chaipackage viats-jest, uses a dedicatedtsconfig.jest.json, and keeps the test environment on the existing CommonJS semantics so logger startup behavior stays stable under test. - Refreshed the contributor toolchain around the Jest migration by upgrading
ts-jest,chai,@types/jest,@types/chai,sinon,@types/sinon,@types/node,prettier, andtypescript, while pruning stale unused test/dev dependencies frompackage.json. - Added TypeScript 6 build compatibility by setting
ignoreDeprecations: "6.0"intsconfig.json, keeping the existing emit target and broader library compatibility intact.
- Added regression coverage for the browser-like fake-stdout recursion case in both the Node-simulated compatibility tests and the real browser bundle tests.
- Added regression coverage for browser-style saved-console feedback loops, both in the Node-simulated compatibility tests and in the real browser bundle tests.
- Updated the Jest test suite to use ESM-safe imports where needed so the upgraded Jest/Chai toolchain runs cleanly end to end.
- Logging a bare
Errorno longer prefixes the message with a dangling hyphen. Calls likeconsole.error(new Error('bang'))andconsole.log(new Error('bang'))now emitmessage: "bang"instead ofmessage: " - bang".
- Added regression coverage for bare-error logging without extra context. Updated error-only expectations and added an explicit end-to-end console-path test to ensure the emitted
messagestays clean when no leading string message is provided.
- Buffered startup logs now preserve original caller metadata. When an early log is held briefly for async package-name lookup, its
@filenameand@logCallStackare captured at the original call site instead of being recomputed later during flush. - Node ESM caller detection now skips packaged library frames more reliably.
@filenameno longer falls back tonode_modules/console-log-json/dist/esm/index.mjsin the reported startup case when the first real external caller is the consumer's app entry file. - Bare
Errorstack headers are now stripped from non-error call stacks.@logCallStackno longer degrades to a useless literal"Error"when the runtime emits a header line without a trailing colon. - Internal helper frames are omitted from
@logCallStack. Non-error call stacks now start at the consumer-visible caller instead of internal helpers likecaptureFileInfo(...),emitConsoleJsonLog(...), orlogUsingConsoleJson(...).
- Added end-to-end regression coverage for the reported Node ESM startup case. The test suite now verifies
@packageName,@filename, and@logCallStackbehavior with a temporary ESM consumer setup that mirrors the installed package layout undernode_modules/console-log-json/dist/esm/index.mjs. - Added stack-parser regressions for packaged ESM frames and internal helper trimming. Browser/stack compatibility tests now cover installed-package frame skipping, bare
Errorheaders, and removal of leading internal logger helper frames from@logCallStack.
- Automatic
.envloading has been removed. The library no longer reads.envfiles or depends ondotenv, even optionally. Configuration now comes only from existingprocess.envvalues orLoggerAdaptToConsole({ envOptions }). logUsingWinstonhas been renamed tologUsingConsoleJson. If you were importing or referencing the old helper directly, update to the new name.
- Built-in structured redaction.
LoggerAdaptToConsole()now accepts a Pino-styleredactoption:- shorthand array form:
redact: ['password', 'headers.authorization', 'items[*].token'] - object form with custom censor:
redact: { paths: [...], censor: 'MASKED' }
- shorthand array form:
- Redaction runs on the final output object. It applies after
transformOutput, before serialization/write, andonLogreceives the redacted result. - Minification-resistant caller detection.
@filenamenow prefers V8 callsites, skips internal logger frames by function identity where possible, and falls back to stack parsing only when needed.
- Jest replaced Mocha. The test runner is now Jest via
ts-jest, with a newjest.config.cjsand TypeScript configured for Jest globals. - Safer browser/bundled filename behavior. Documentation now explicitly treats
@filenameas best-effort when a consumer bundles the library and their app into a single browser file. - No more manual
.envscanning logic. The legacyEnv.loadDotEnv()implementation is now a no-op compatibility shim instead of touching the filesystem. - Logging pipeline cleanup. Internal naming and structure were updated to match the current architecture:
logUsingConsoleJsonreplaces stale Winston naming- final output shaping, transform hooks, redaction, and serialization are now separated more clearly
- Test suite cleanup. Removed confusing no-op
await console.*(...)usage, converted the helper stack-format test to assertions instead of console output, and added targeted coverage for:- reserved
messagehandling through the realconsole.log(...)path - fallback-pattern skipping in browser-style stack parsing
- callsite-based filename detection
- structured redaction behavior
- reserved
- Stronger configuration coverage. Added an audit test to ensure every documented
CONSOLE_LOG_*setting inREADME.mdis referenced by the test suite. - More deterministic permutation coverage. Added a table-driven test that exercises permutations of string messages, errors, one or more context objects, and explicit
{ level: ... }overrides and asserts a normalized final payload.
- Reserved
messagehandling is now consistent.console.log('dude', { message: 'hi there' })now producesmessage: "dude - hi there"- object-valued
messageis preserved under@messageObjectinstead of being flattened into top-level keys - when only
message: { ... }is provided, the canonicalmessagefield remains present with the usual placeholder text
@packageNamenow works in Node ESM startup paths. The logger no longer relies solely onrequire(...)to discoverpackage.json, soLoggerAdaptToConsole()in"type": "module"projects no longer emits the misleading"<not-yet-set> Please await..."placeholder.- Early logs are buffered while async package-name lookup completes. Startup logs emitted immediately after
LoggerAdaptToConsole()are now replayed once package-name resolution finishes, instead of racing initialization. - Redaction no longer mutates caller-owned input objects. Sensitive fields are still redacted in the final emitted log object, but the original application objects passed to
console.log(...)remain unchanged.
ARCHITECTURE.mdwas rewritten and updated to reflect the real implementation instead of the older Winston-based design.README.mdnow documents:- Jest-based contributor/test setup
- no automatic
.envloading - best-effort
@filenamesemantics in bundled browsers - the new
redactAPI with examples and guidance
- Removed
winstondependency. The logging engine is now a lightweight native implementation. If you were importing Winston-specific types or relying on Winston transport behavior, this will affect you. The public API (LoggerAdaptToConsole,console.log, etc.) is unchanged. - Removed
app-root-pathdependency. Replaced with a built-in project root detector. The@filenameand stack trace path stripping behavior is identical. dotenvandsource-map-supportare now optional dependencies. They will still be installed by default, but if installation fails (e.g. in restricted environments), the library works without them.- Compilation target lowered from
es2019toes2017. This broadens browser and Node.js compatibility but should not affect consumers. - Barrel exports restricted to public API only. Internal helpers like
getAppRoot,getEnv,sortObject,ToOneLine,safeObjectAssign,FormatStackTrace,NewLineCharacter,jsonStringifySafe,colorJson,CaptureNestedStackTrace, andcallsitesare no longer exported from the package root. If you were importing these directly fromconsole-log-json, import them from their specific file paths instead (e.g.console-log-json/dist/src/safe-object-assign).
- Zero required runtime dependencies. The library is fully self-contained.
- Browser compatible. Works in Chrome, Firefox, Safari, and Edge via webpack, vite, esbuild, or any modern bundler. Node-specific features (
@filename,.envloading) degrade gracefully. envOptionsparameter. Configure the logger programmatically withoutprocess.env. Accepts the same variable names as the environment variables. Ideal for browser environments.LoggerAdaptToConsole({ envOptions: { CONSOLE_LOG_JSON_NO_FILE_NAME: 'true', CONSOLE_LOG_JSON_NO_STACK_FOR_NON_ERROR: 'true', } });
onLoginterceptor. Receive every log entry asynchronously after it's written. Useful for forwarding logs to a backend, analytics service, or error tracker. Crash-safe and non-blocking.LoggerAdaptToConsole({ onLog: (jsonString, parsedObject) => { navigator.sendBeacon('/api/logs', jsonString); } });
transformOutputhook. Modify the log object before it's written. Rename fields, add properties, or reshape the output to match your log aggregator's schema. Falls back to original output if the callback throws.LoggerAdaptToConsole({ transformOutput: (obj) => { obj.status = obj.level; delete obj.level; return obj; } });
CONSOLE_LOG_JSON_CONTEXT_KEYoption. Nest all user-provided context properties under a single key instead of flattening them to the top level. Keeps the top-level JSON schema predictable for DataDog filters, OpenSearch index mappings, etc.LoggerAdaptToConsole({ envOptions: { CONSOLE_LOG_JSON_CONTEXT_KEY: 'context' } });
- ESM build. Ships both CommonJS (
dist/index.js) and ESM (dist/esm/index.mjs) entry points. Modern bundlers automatically pick the ESM version for tree-shaking. - Boolean parameter support.
console.log('active', false)now correctly includesfalsein the message instead of silently dropping it.
filterNullOrUndefinedParametersskipped adjacent nulls.forEach+spliceshifted indices; fixed by iterating backwards.FormatErrorObjectcrashed whenmessagewas an object. Fixed by deletingmessagebefore merge and addingtypeofguards.ErrorWithContext(null, 'string')crashed.typeof null === 'object'causednull.messageaccess; fixed by adding null guard.Env.loadDotEnv()had a dead branch..length < 0is always false; fixed to.length > 0.ifEverythingFailsLoggercould throw into the caller's code. Now silently catches all errors so logging failures never crash the application.
- Single Error object per log call (down from two).
getCallingFilenamenow parses the filename from the shared stack string instead of creating a separate Error via the V8 callsites API. - No more
Error.prepareStackTraceglobal mutation. Eliminated the race condition hazard in the logging hot path. - Environment variables cached at init time. Reduces ~10
getEnv()calls per log to zero. - Pre-compiled regex for stack message extraction.
safeObjectAssignusesdeepClonewith a visited-object Map instead ofJSON.parse(jsonStringifySafe(...)). Eliminates double serialization.sortObjectoptimized to sort keys directly without intermediate arrays.- JSON auto-parse skipped for non-JSON messages. A quick first-character check avoids the
try { JSON.parse() } catchoverhead on every log. NewLineCharactercached after first call.- Stack trace capture skipped entirely when both
CONSOLE_LOG_JSON_NO_FILE_NAMEandCONSOLE_LOG_JSON_NO_STACK_FOR_NON_ERRORare enabled. Eliminates the most expensive operation for high-throughput logging. - Depth limits added to
safeObjectAssign(50 levels) andfindNonConflictingKeyInTarget(20 iterations) to prevent stack overflow on deeply nested objects.
-
Install the new version:
npm install console-log-json@4
-
No code changes needed if you only use the public API (
LoggerAdaptToConsole,console.log,ErrorWithContext, etc.). The drop-in behavior is identical. -
If you imported internal helpers from
console-log-json(e.g.safeObjectAssign,sortObject,FormatStackTrace), update your imports to use the deep path:// Before (v3) import { safeObjectAssign } from 'console-log-json'; // After (v4) import { safeObjectAssign } from 'console-log-json/dist/src/safe-object-assign';
-
If you depended on Winston types or transports, those are gone. The library no longer uses Winston internally.
-
dotenvandsource-map-supportare now optional. If you need.envfile loading, ensuredotenvis installed in your project. If you need TypeScript source map support in stack traces, ensuresource-map-supportis installed.