Release Notes
Version 1.2.0
📅 Release Date
August 23, 2026
📖 Overview
Three changes to Logger, all of them about the same thing: a log line has to mean something on its own, once it has
left the console it was written to.
Logger emitted a prose sentence under a message key, which no collector can filter, count, or chart. It read the
service name from a constant compiled in from config/ — a file a rename does not touch, which is how a deployed
service came to spend production calling itself cloudflare-boilerplate. And it handed whatever payload it was given
straight to JSON.stringify.
So: an event name where the sentence was, a service name that comes from the deployment and is always present, and a
redaction pass before anything is serialised.
🚀 Features
redact— scrubs credential-named fields out of a structure bound for a log stream.redact({ provider: 'messaging', apiKey: 'live-key' }) // { provider: 'messaging', apiKey: '[redacted]' }Matching is by field name, separator- and case-insensitively, so one entry covers
apiKey,API_KEY,x-api-key, andproviderApiKey. It walks arrays and nested objects to a bounded depth — which is also what makes a cyclic structure terminate rather than exhaust the stack — and returnsErrorvalues untouched soLoggercan still expand them.It lives in
security/rather thanutils/on the same intent test astimingSafeEqual: it exists because the obvious alternative is unsafe, not because it happens to be useful.LogContext.component— names which part of an application wrote a line.serviceanswers "which service" and is stamped from the environment;componentanswers "where inside it". Before this, call sites passed a class name asservice, so the field meant "which service" on some lines and "which class" on others.
🔧 Enhancements
Loggerruns every payload throughredactbefore serialising. The order is load-bearing in one direction: redaction returnsErrorvalues untouched precisely so the existing expansion pass can still recognise them.Treat it as the last line of defence. It matches on names, so a secret under an innocent one gets through, and a whole request body is logged in full apart from the fields it happens to recognise. The rule at the call site is what actually protects you.
⚠️ Breaking Changes
Lines carry
eventwhere they carriedmessage. The first argument todebug,info,warn, anderroris now an event name — a stable, lowercase, dotted identifier such asnotification.sent— and it is emitted under theeventkey.log.info('article.created', { id }) // → { level, event: 'article.created', time, data: { id } }The signature is unchanged, so nothing stops compiling; what changes is the field a query filters on, and the shape of the value in it. Anything reading
message— a saved log view, a Logpush consumer, an alert — needs updating.A collector that indexes JSON fields turns
eventinto a column, which makesevent = notification.faileda filter rather than a substring search, and the count of one event over time a series. A prose message can be neither:'Failed to send notification for user 7'is a different string on every line it is written.The service name is read from
SERVICE_NAMEin the environment, beforelogging.serviceon the configuration surface. The order is the point: a name in the environment sits beside the deployment's own name — inwrangler.json, two lines below"name"— where a rename that misses it shows up in the same diff.LoggingSettings.serviceis now optional andsystemDefaults.loggingsupplies none, so code readingsettings.servicereadsstring | undefined. It remains the fallback for runtimes with no environment to read.A line with no name available reads
service: "unknown"rather than omitting the field. A deployment that forgetsSERVICE_NAMEis a defect, and an absent key makes it an invisible one — the lines look ordinary and nothing surfaces until two services share a stream and cannot be told apart.service = unknownis a query that finds every misconfigured deployment.
🧪 Tests
test/security/redact.spec.ts— new: what counts as a secret across every spelling, what must survive untouched, the depth cap, cyclic structures, and thatErrorvalues reach the logger intact.test/core/Logger.spec.ts— theeventkey, theSERVICE_NAMEprecedence over a stale configured name, theunknownfallback and that the key is never simply dropped, and that the redaction pass actually runs.