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, and providerApiKey. 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 returns Error values untouched so Logger can still expand them.

    It lives in security/ rather than utils/ on the same intent test as timingSafeEqual: 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. service answers "which service" and is stamped from the environment; component answers "where inside it". Before this, call sites passed a class name as service, so the field meant "which service" on some lines and "which class" on others.

🔧 Enhancements

  • Logger runs every payload through redact before serialising. The order is load-bearing in one direction: redaction returns Error values 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 event where they carried message. The first argument to debug, info, warn, and error is now an event name — a stable, lowercase, dotted identifier such as notification.sent — and it is emitted under the event key.

    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 event into a column, which makes event = notification.failed a 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_NAME in the environment, before logging.service on the configuration surface. The order is the point: a name in the environment sits beside the deployment's own name — in wrangler.json, two lines below "name" — where a rename that misses it shows up in the same diff.

    LoggingSettings.service is now optional and systemDefaults.logging supplies none, so code reading settings.service reads string | 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 forgets SERVICE_NAME is 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 = unknown is 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 that Error values reach the logger intact.
  • test/core/Logger.spec.ts — the event key, the SERVICE_NAME precedence over a stale configured name, the unknown fallback and that the key is never simply dropped, and that the redaction pass actually runs.

results matching ""

    No results matching ""