Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[5.1.3]
Added
Transport error observability — failures are no longer silent
HttpTransportonError(err, droppedEntries)callback fires when a batch is permanently dropped (retries exhausted or evicted bymaxQueueSize); receives the underlyingErrorand the dropped entriesHttpTransportmaxQueueSizecaps the pending-batch queue (defaultInfinity); previously the retry queue was unboundedHttpTransportoverflowStrategy: 'drop-oldest' | 'drop-newest'controls eviction (default'drop-oldest'— favours fresh data)StreamTransport(andFileTransportby inheritance) now respectsstream.write()returningfalse: pauses, queues entries in memory, and resumes on'drain'instead of dropping logs on the floorStreamTransport/FileTransportacceptmaxQueueSize,overflowStrategy, andonDrop(entries, reason)to bound the backpressure queue and surface drops- Across
FileTransportrotations the new file stream gets its own'drain'listener and the paused state is reset, so backpressure handling carries over
Input validation
setLevel(level)and thelogger.level = …setter now throwTypeErroron a name that isn't one oftrace | debug | info | warn | error | fatal. PreviouslyLEVELS[bad] === undefinedleft the threshold in an undefined state — silently breaking every level comparisonchild(bindings)runs astructuredCloneprobe overbindingswhen the parent was constructed withuseWorker: trueand throwsTypeErrorif they aren't structured-cloneable (functions, symbols, class instances with private fields). Converts a cryptic workerpostMessagecrash on the first log call into a clear error at the call site. Circular references remain supported
Fixed
WritableLikeinterface now declareson('drain', listener)overload so streams that emit'drain'are typed correctly
[5.1.0] - 2026-04-22
Added
- AsyncLocalStorage context propagation (Node.js)
Konsole.enableContext()lazily initializesnode:async_hooks— apps that never use context pay zero overheadKonsole.runWithContext(store, fn)binds a context store to an async scope; every log entry produced inside it auto-mergesstoreinto its fieldsKonsole.getContext()reads the current store (useful for debugging)- Nested
runWithContextcalls merge into the parent store (outer spread into inner) — middleware stacking produces the union of all active contexts - Merge precedence at log time: ALS context < child bindings < call-site fields
- Browser is a no-op —
runWithContextjust invokesfn()directly - Enables automatic
requestIdpropagation in Express/Fastify middleware without threading child loggers through every call
[5.0.1] - 2026-04-17
Added
DEBUG=*namespace filtering — drop-in replacement for thedebugnpm package- Reads the
DEBUGenv var at logger construction time (Node.js only) - Glob/wildcard matching:
DEBUG=konsole:*,DEBUG=konsole:http,konsole:db,DEBUG=* - Negation support:
DEBUG=*,-App:verboseenables everything exceptApp:verbose - Loggers whose namespace doesn't match are fully short-circuited (
_debugDisabledflag forces noop methods, no entry construction)
- Reads the
Pino API compatibility — Phase 1
logger.levelgetter/setter property — read or write the current threshold (delegates tosetLevel())logger.isLevelEnabled(level)— cheap predicate to gate expensive log constructionlogger.bindings()— returns a shallow copy of the merged child bindingslogger.flush()— alias forflushTransports()(Pino naming)
[5.0.0] - 2026-04-15
Added
Serializers — pluggable per-field transforms on
KonsoleOptionsandKonsoleChildOptions- New
serializersoption accepts aRecord<string, (value) => unknown>map - Built-in
stdSerializersforerr,req, andres(Pino-compatible shapes) serializeErrorflattens anErrorto{ type, message, stack, ...customProps, cause? }— fixes the long-standingJSON.stringify(err) === "{}"foot-gunserializeRequest/serializeResponsecover Nodehttp.IncomingMessage, Express, and FetchRequest/Response- Child loggers inherit parent serializers and can override per key
- New exports:
stdSerializers,serializeError,serializeRequest,serializeResponse,applySerializers,Serializer,Serializers
- New
Auto Error flattening —
Konsole.addLognow expands any field whose value is anErroreven when no explicit serializer is configured, so logs never silently emit"err":{}
Fixed
parseArgsSlowmulti-arg join no longer renders Errors as{}; they're expanded viaserializeErrorJsonFormatter,transports/base.toJsonLine, and the pretty/text field renderer all expand nestedErrorvalues inside fields via a shared JSON replacer- Cycle safety:
err.self = err, mutualcausechains, and deeply nested cyclic graphs inside custom error props all serialize without throwing — replaced with"[Circular]"only on the current walk path - Repeated non-cyclic references across sibling branches are preserved as full copies (not collapsed to
[Circular]) toJSONpassthrough —URL,Buffer,Date, Decimal, Moment, Temporal, etc. round-trip through their canonical form instead of becoming{}- Own
__proto__keys on parsed JSON payloads (e.g.JSON.parse('{"__proto__":…}')) survive serialization as own data properties without pollutingObject.prototype RegExpvalues on custom error props serialize to/pattern/flags(or expand withsource/flagsplus custom props), with full cycle detectionapplySerializersonly honors own keys on the serializer map, so a field literally namedhasOwnPropertyortoStringno longer picks upObject.prototypemethods- Fetch
Headers, subclasses, polyfills, andMap-like header containers are flattened by interface (forEach/entries) instead of byconstructor.name, so redaction paths likereq.headers.authorizationsee the actual values - Header normalization preserves a header literally named
__proto__as an own data property via null-prototype output objects
[4.4.0] - 2026-04-05
Added
- File rotation — New
rotationoption onFileTransportOptions- Size-based rotation:
maxSizein bytes (e.g.10 * 1024 * 1024for 10 MB) - Time-based rotation:
intervalaccepts'daily','hourly', or a number (ms) - Retention:
maxFilescaps the number of rotated files (default: 5); oldest are deleted - Compression:
compress: truegzip-compresses rotated files asynchronously (.gzsuffix) - Naming: numeric suffix —
app.log→app.log.1→app.log.2; current file stays at configured path - No entry loss: writes during rotation are buffered and flushed after the new file opens
- Byte counter seeded from existing file size on append mode — rotation triggers correctly on restart
- New export:
RotationOptionstype
- Size-based rotation:
[4.3.0] - 2026-03-30
Added
Platform worker adapter —
useWorker: truenow works on both browser and Node.js- Browser: Web Worker via Blob + Object URL (unchanged)
- Node.js:
worker_threadsvia dynamic import withparentPortshim - Unified
KonsoleWorkerinterface insrc/workerAdapter.ts - Messages sent before the Node.js worker is ready are buffered and flushed automatically
- Falls back gracefully to main-thread processing if no worker API is available
- New exports:
createPlatformWorker(),KonsoleWorkertype
Conditional
node/browserexports inpackage.jsonfor better bundler support
Changed
useWorker: trueis no longer browser-only — it is now supported in Node.js viaworker_threads- Removed the "Web Worker is not available" console warning in Node.js (worker is now created instead)
[4.2.0] - 2026-03-28
Added
Field redaction — New
redactoption onKonsoleOptionsandKonsoleChildOptions- Accepts dot-notation field paths:
redact: ['password', 'req.headers.authorization'] - Values replaced with
'[REDACTED]'before any output, transport, or buffer - Children inherit parent redact paths (security invariant — cannot opt out) and can add more
- Applied after bindings merge, before all consumers
- Original caller objects are never mutated
- New exports:
compileRedactPaths(),applyRedaction(),REDACTED
- Accepts dot-notation field paths:
Graceful shutdown — New static methods for clean process exit
Konsole.shutdown()— flushes and destroys all registered loggersKonsole.enableShutdownHook()— registersSIGTERM,SIGINT, andbeforeExithandlers (Node.js only, no-op in browser)
Browser runtime redaction toggle —
__Konsole.disableRedaction(true/false)viaexposeToWindow()- Temporarily bypass redaction in DevTools for debugging
- Not available in Node.js — server-side redaction is always enforced
[4.0.0] - 2026-03-19
Added
Configurable timestamps — New
timestampoption onKonsoleOptionsandKonsoleChildOptions- Preset formats:
'datetime'(default),'iso','time','date','unix','unixMs','none' - Custom function:
(date: Date, hrTime?: number) => string - Full options object:
{ format: TimestampFormat, highResolution: boolean } - New
TimestampFormatandTimestampOptionstypes exported
- Preset formats:
Full date in timestamps — Pretty, text, and browser formatters now show
YYYY-MM-DD HH:MM:SS.mmmby default (was time-onlyHH:MM:SS.mmm)Browser formatter timestamps —
BrowserFormatternow renders timestamps in DevTools output (previously omitted, relying on DevTools built-in timestamps)High-resolution timestamps —
{ highResolution: true }captures nanosecond-precision monotonic timingLogEntry.hrTimefield (nanoseconds viaprocess.hrtime.bigint()in Node.js,performance.now()in browsers)- Included in JSON output when present
- Custom timestamp functions receive
hrTimeas the second argument
Runtime timestamp control — New
setTimestamp()instance method- Accepts preset strings, custom functions, or
TimestampOptionsobjects - Recreates the internal formatter with the new format
- Accepts preset strings, custom functions, or
Browser runtime timestamp control —
exposeToWindow()now exposes:__Konsole.setTimestamp(format)— change all loggers__Konsole.getLogger(ns).setTimestamp(format)— change a specific logger__Konsole.getLogger(ns).setLevel(level)— change level from DevTools
Child logger timestamp override —
child(bindings, { timestamp })can override the parent's timestamp formatNew exports —
resolveTimestampConfig(),formatTimestamp(),getHrTime(),FormatterOptionsPerformance optimizations — on par with Pino on per-call overhead, faster on JSON serialization
bufferoption: defaults tofalsein Node.js (no circular buffer overhead),truein browsers- Disabled log levels add zero overhead
- Optimized argument parsing and field merging on the hot path
- Removed deprecated
logtypefield from entry construction
Benchmark suite —
npm run benchmarkcompares against Pino, Winston, and Bunyan- Throughput (ops/sec), latency (p50/p95/p99), bundle size, and memory usage
- Daily CI benchmark via GitHub Actions (
.github/workflows/benchmark.yml)
Changed
- Default timestamp format changed from time-only (
HH:MM:SS.mmm) todatetime(YYYY-MM-DD HH:MM:SS.mmm) for pretty, text, and browser formatters toTextLine()in file/stream transports now outputsYYYY-MM-DD HH:MM:SS.mmm(was time-only)createFormatter()andcreateAutoFormatter()accept an optionaltsFormatparameter- All formatter classes now accept
FormatterOptionswith an optionaltimestampFormatfield - Child loggers get their own formatter instance when overriding
timestamp(previously always shared with parent) - In Node.js,
bufferdefaults tofalse—getLogs()/viewLogs()return empty unlessbuffer: trueis set explicitly - Disabled log levels add zero per-call overhead in Node.js
Breaking Changes
- Default timestamp format changed — Pretty, Text, and Browser formatters now output
YYYY-MM-DD HH:MM:SS.mmm(wasHH:MM:SS.mmm). Restore old behavior withtimestamp: 'time'. - BrowserFormatter now renders timestamps — DevTools output includes a timestamp prefix. Use
timestamp: 'none'to omit. bufferdefaults tofalsein Node.js —getLogs(),viewLogs(), andgetStats()return empty unlessbuffer: trueis set. Browser default is unchanged (true).logtypefield removed from entries — The deprecatedLogEntry.logtypeis no longer set. Useentry.levelinstead (available since v3.0.0).toTextLine()output changed — File and stream transport text format now includes the full date (YYYY-MM-DD HH:MM:SS.mmm), matching the formatter default.
Migration from v3
// Restore v3 timestamp format
new Konsole({ timestamp: 'time' });
// Restore v3 buffer behavior in Node.js
new Konsole({ buffer: true });
// Replace logtype usage
entry.logtype // ❌ undefined in v4
entry.level // ✅ 'info', 'error', etc.[3.0.0] - 2025-03-14
Added
Node.js compatibility — Works in both browser and Node.js ≥ 18
- Replaced
windowusage withglobalThisthroughout fetchis detected viaglobalThis.fetch; passfetchImplfor Node.js < 18- Graceful warning when
useWorker: trueis set in Node.js (removed in v4.3.0 — now supported natively)
- Replaced
Numeric log levels — Six-level numeric system
- New methods:
trace()(10),debug()(20),fatal()(60) log()kept as an alias forinfo()(30)- New
levelconstructor option — minimum threshold; entries below are discarded entirely - New
setLevel()instance method for runtime changes LogEntrynow includeslevelandlevelValuefields
- New methods:
Structured logging — Consistent JSON schema
- Log entries now carry
msgandfields(structured key-value pairs) in addition tomessages - Fields spread into the top level of JSON output:
{ level, levelName, time, namespace, msg, ...fields } - Object-first calling convention:
logger.info({ msg: '...', key: val }) - String + fields object:
logger.info('msg', { key: val }) Erroras first argument:msg=err.message,fields.err= the Error
- Log entries now carry
Output formatters —
formatconstructor option ('auto' | 'pretty' | 'json' | 'text' | 'browser' | 'silent')PrettyFormatter— ANSI-colored human-readable output for TTY terminals; respectsNO_COLOR/FORCE_COLORJsonFormatter— Newline-delimited JSON for pipes, CI, and log aggregatorsTextFormatter— Plain text, no ANSI, for CI environmentsBrowserFormatter—%cCSS badge styling in browser DevToolsSilentFormatter— No output; entries still stored in buffer and forwarded to transportsautoselects the best formatter for the current environment automatically- Errors and fatals always route to
stderr; all other levels tostdout
Child loggers —
logger.child(bindings, options?)- Bindings automatically merged into every entry the child produces
- Bindings accumulate through nested children; call-site fields override on collision
- Optional
{ namespace, level }overrides per child - Children share parent's circular buffer and formatter
- Children are not registered in
Konsole.instances(ephemeral) child.addTransport()does not affect parent
Transport abstraction — New
Transportinterface withwrite,flush?,destroyHttpTransport— renamed fromTransport; updated payload to structured JSON schemaConsoleTransport— wraps a Formatter; useful withformat: 'silent'FileTransport— appends NDJSON/text to a file; Node.js only; buffers entries during async openStreamTransport— writes to anyWritableLike; duck-typed for compatibilityaddTransport()andtransportsoption now accept bothTransportinstances andTransportConfigobjects
Vitest test suite — 92 tests across 8 files covering all core components and transports
Changed
criteriais now deprecated — uselevelfor threshold filtering andformat: 'silent'to suppress outputTransportConfigno longer the only type accepted bytransports/addTransport()— bothTransportinstances andTransportConfigplain objects are acceptedLogEntry.logtypeis deprecated — useLogEntry.level- Rollup now externalizes all
node:built-ins to prevent browser stubs
Breaking Changes
LogEntryshape changed: addedmsg,fields,level,levelValue;logtypeis now@deprecated- Old
Transportclass is nowHttpTransport;src/Transport.tsis a deprecated re-export criteria: false(the old default) is superseded byformat: 'silent'
[2.0.0] - 2024-12-27
Added
Circular Buffer Storage — Memory-efficient log storage with configurable
maxLogslimit- Automatically evicts oldest logs when capacity is reached
- New
CircularBufferclass exported for custom use
Web Worker Support — Offload log processing to background thread
- Enable with
useWorker: trueoption - New
getLogsAsync()method for async log retrieval
- Enable with
Transport System — Send logs to external services
- Configurable
batchSize,flushInterval, retry with exponential backoff - Filter and transform per entry
- New
addTransport()andflushTransports()methods
- Configurable
Memory Statistics —
getStats()returns log count and buffer usageGlobal Transport —
Konsole.addGlobalTransport()static method
Changed
destroy()method is now async and returns a Promise- Log storage uses
CircularBufferinstead of a plain array
Fixed
viewLogs()no longer shows "Array(1)" — messages are correctly formatted
[1.0.0] - 2024-01-01
Added
- Initial release
- Namespaced logging with
Konsoleclass - Log levels:
log,error,warn,info - In-memory log storage with automatic cleanup
- Conditional logging with boolean and function criteria
viewLogs()for batch viewing of stored logsgetLogs()for programmatic accessclearLogs()to remove all stored logsKonsole.getLogger(),exposeToWindow(),enableGlobalPrint()- Configurable retention period and cleanup interval
- Full TypeScript support with exported types
- Zero dependencies
Security
- Window exposure is opt-in and can be conditionally enabled