Skip to content

docs(adr): ADR-048 structured logging - #652

Merged
d-klotz merged 2 commits into
nextfrom
claude/frigg-logging-adr-15a03a
Sep 24, 2026
Merged

d-klotz merged 2 commits into
nextfrom
claude/frigg-logging-adr-15a03a

Conversation

@d-klotz

@d-klotz d-klotz commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Frigg has no logging standard. Core logs through 309 direct console.* calls (plus 29 in admin-scripts), with no JSON, no levels and no shared correlation ids. ADR-011 names a "telemetry/logger abstraction" for logs, but the code never built the logs leg.

The current logging also leaks secrets:

  • Every 5xx and every failed testAuth dumps the raw Lambda event: Authorization, x-frigg-* keys, cookies, the OAuth code and the login password (create-handler.js:167).
  • FetchError puts the full URL with its query string in every stage. On STAGE=dev it also adds request headers and bodies (Bearer tokens, client_secret).
  • span.recordException sends the same raw text to the trace backend.
  • Devtools sets no log retention, so every Frigg log group never expires.

This ADR defines one logger for the framework.

What's in this PR

Documentation only. No code changes.

  • docs/architecture-decisions/048-structured-logging.md (new)
  • docs/architecture-decisions/README.md (one row in Current ADRs)
  • website/roadmap/data/adrs.json (one entry, count 30 → 31)

The decision, in brief

One Frigg-owned logger port in core writes one redacted JSON record per line to stdout. Every record carries the same field set and the ADR-011 correlation ids.

Topic Decision
API getLogger(name), trace…fatal(message, fields), child(), isLevelEnabled(). this.logger on IntegrationBase and Requester
Format JSON only, every stage. Written with fs.writeSync to fd 1, not console.*
Fields timestamp, level, message, logger, requestId, ADR-011 ids, eventName, trace_id/span_id, error
Levels Six levels as a threshold. FRIGG_LOG_LEVEL (default INFO, DEBUG on local runs)
Redaction Inside the logger for every record. Allow-list serializers, a key denylist, string scrubbing. FetchError fixed at the source; no stage writes secrets
Correlation Framework scopes only (invocation, message, handler). No scope API for developers
OTel No Logs SDK yet (JS still 0.x). Trace ids from the stable @opentelemetry/api. Amends ADR-011 for the logs signal
Deployment Opt-in appDefinition.logging → Lambda LoggingConfig + retention. New apps get 30 days
Enforcement no-console: error in runtime packages, a redaction regression suite, require-graph guards

The ADR also has a Usage examples section with integration, API module, core, test and log-query examples.

Needs a decision from the maintainers

The upgrade changes behavior, so it is an exception to the ADR-027 "no behavior change on upgrade" rule. The ADR lists 7 changes in the Negative consequences table (JSON replaces text, FetchError loses query and dev detail, the debug replay stops, and others). A maintainer ratifies the whole list before PR A merges, as ADR-031 did.

Sequencing

PR 0 (a dev-stack spike) → PR A (foundation, no call-site changes) → PR B (security-critical call sites) → devtools → area-by-area migration → no-console: error.

This PR supersedes #540: its OAuth-callback steps become redacted DEBUG records. 048 is the first number no open PR claims (#644 claims 028–041, #646 claims 043, #648 claims 044–047).

Test plan

Documentation only, so there is nothing to run.

  • website/roadmap/data/adrs.json passes python3 -m json.tool
  • 111 path:line citations checked against next @ 99f6534
  • Decider review of the Decision and the upgrade-change list

🤖 Generated with Claude Code

📦 Published PR as canary version: 2.0.0--canary.652.b14f3b5.0

✨ Test out this PR locally via:

npm install @friggframework/admin-scripts@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/core@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/devtools@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/eslint-config@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/prettier-config@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/schemas@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/serverless-plugin@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/test@2.0.0--canary.652.b14f3b5.0
npm install @friggframework/ui@2.0.0--canary.652.b14f3b5.0
# or 
yarn add @friggframework/admin-scripts@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/core@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/devtools@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/eslint-config@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/prettier-config@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/schemas@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/serverless-plugin@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/test@2.0.0--canary.652.b14f3b5.0
yarn add @friggframework/ui@2.0.0--canary.652.b14f3b5.0

Version

Published prerelease version: v2.0.0-next.111

Changelog

🚀 Enhancement

  • @friggframework/admin-scripts, @friggframework/core, @friggframework/devtools, @friggframework/eslint-config, @friggframework/prettier-config, @friggframework/schemas, @friggframework/serverless-plugin, @friggframework/test, @friggframework/ui
    • fix(core): single-flight the 401 token refresh in Requester #636 (@d-klotz)
    • fix(requester): grace-retry non-refreshable 401s and surface diagnostic detail on invalid-credential errors #623 (@d-klotz)

🐛 Bug Fix

Authors: 3

One Frigg-owned logger port in core writes one redacted JSON record per
line to stdout, with a fixed field set, six threshold levels, ADR-011
correlation ids and redaction by construction. Adds the register row and
the roadmap entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@d-klotz d-klotz added release Create a release when this pr is merged prerelease This change is available in a prerelease. labels Sep 23, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, comment @claude review on this pull request, or push a new commit, to trigger a review.

@netlify

netlify Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for friggframework-org ready!

Name Link
🔨 Latest commit b14f3b5
🔍 Latest deploy log https://app.netlify.com/projects/friggframework-org/deploys/6ab513ceed1bf80008b0abf3
😎 Deploy Preview https://deploy-preview-652--friggframework-org.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T20:02:37.634679Z 60de836 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60de83654a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +279 to +280
The DLQ processor logs an unparsed body as its length and SHA-256
(`dlq-processor.js:25,50`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the DLQ digest through string scrubbing

The proposed DLQ fingerprint will normally be a 64-character hexadecimal SHA-256 string (or a 44-character base64 string), both of which satisfy the preceding rule that scrubs base64-like runs of 40 or more characters in every string. Consequently malformed DLQ messages will record only [REDACTED:<len>] instead of the promised digest, preventing operators from grouping repeated payloads. Exempt the digest field from this pattern or use an encoding that the scrubber explicitly preserves.

Useful? React with 👍 / 👎.

@seanspeaks seanspeaks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm approving since it looks thorough as a plan and covers a lot of the detailed basics.

The thing I want to call out for rationale:

  1. It enables standardized logging in all Frigg implementations with default logging tooled up into the various components such that anyone debugging and monitoring a Frigg application can do so cleanly and from central docs without needing to explore how each repo or project may implement.

  2. With that structured output, much easier to programmatically trace through and connect messages to db objects etc. making debugging much simpler.

  3. Also lets us add outbound log destinations with a simple core extension that tackles the translation to the corresponding service with.

I think that last point deserves some notes/treatment here.

Answers the maintainer review on #652. Adds §11 log destinations: a
destination sink supplies only translate and send, and core owns the
buffer, the shared flush deadline and the failure rules. Makes the record
contract additive-only from PR A, adds entityId and credentialId, and adds
a central docs/guides/LOGGING.md to the sequencing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@d-klotz

d-klotz commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

@seanspeaks added §11 (log destinations) for your point 3: a sink only translates + sends, core owns buffer, flush and failures. Also entityId/credentialId for point 2 and a central docs/guides/LOGGING.md for point 1.
Merging as Proposed. Have a look at §11 when you can and I'll flip it to Accepted.

@d-klotz
d-klotz merged commit 93c9557 into next Sep 24, 2026
13 of 14 checks passed
@d-klotz
d-klotz deleted the claude/frigg-logging-adr-15a03a branch September 24, 2026 12:53
@seanspeaks

Copy link
Copy Markdown
Contributor

🚀 PR was released in v2.0.0-next.113 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prerelease This change is available in a prerelease. release Create a release when this pr is merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants