mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 21:13:12 +00:00
Some checks failed
* feat: add API Sentry tracing * feat: activate Sentry deployment environments * fix: allow Sentry-free design-time tooling * fix: trace background jobs in Sentry * feat(observability): identify and scrub Sentry transactions * fix(observability): finish abandoned transactions
4 KiB
4 KiB
ADR 0002 — Sentry observability: release identity and redaction-safe telemetry fields
- Status: Accepted
- Date: 2026-09-03
- Scope:
Api.SeaHavenIndustries(observability layer only; no behavior changes)
Context
The backend reports to Sentry (SH-298). Events arrived without a stable release identity, without service/commit attribution, and background jobs each hand-rolled their own transaction setup. HTTP request metadata risked leaking PII through route values, query strings, or identity claims.
Decision
- Release identity.
SentryObservability.ResolveRelease(Assembly)accepts an informational version shapedshoc-backend@<40 hex commit sha>and otherwise resolves tolocal-development.scripts/package-elastic-beanstalk.shresolves the commit fromAPP_COMMIT_SHA→GITHUB_SHA→git rev-parse HEAD, fails whenCI=truecannot produce a valid 40-hex sha, and publishes with-p:SourceRevisionId,-p:InformationalVersion=shoc-backend@<sha>,-p:DebugType=portable,-p:DebugSymbols=true. - Default tags.
service=shoc-backendandapp.commit=<sha | local-development>onSentryOptions.DefaultTags. All events use the same commit-addressedshoc-backend@<sha>release. - HTTP requests.
SentryRequestMetadataMiddlewareruns after routing/authentication and before authorization. It sets on the current scope only the fields below. Non-controller endpoints omitcode.function. - Background jobs. All six worker transaction blocks (five files) use
SentryObservability.BeginBackgroundTransaction, which pushes a scope, starts the transaction, sets the scope transaction, and returns a handle withFinishOk/FinishCancelled/FinishError(Exception)that cannot double-finish. Existing status/error behavior is preserved. - Last-mile privacy. Request-body extraction is disabled. Global before-send processors clear request data, query strings, cookies, headers, environment values, and miscellaneous request values from both error events and transactions, retain only an opaque user ID, and attach native trace and transaction IDs as searchable transaction tags. Root and child span data use an allowlist for function, operation, actor, route template, method/status, protocol, database operation, and service host; raw URLs, query values, network addresses, and arbitrary extras are removed.
Field / redaction contract
Allowed request fields (template values only — never resolved route values):
| Field | Source | Rule |
|---|---|---|
actor.type |
auth state | authenticated / anonymous / system |
operation.type |
static | http.server / background_job |
code.function |
ControllerActionDescriptor |
ControllerName.ActionName only |
http.method |
request | HTTP verb |
http.route |
RouteEndpoint.RoutePattern.RawText |
route template; omitted when unresolved |
trace_id / transaction_id |
active Sentry span/transaction | native Sentry ids |
scope User.Id |
ClaimTypes.NameIdentifier only |
set only when authenticated |
Never intentionally sent: query strings, headers, bodies, cookies, usernames, emails, IP addresses, route values, credentials, or token values. The application retains stack traces, exception types/messages, safe tags, operation status, and route templates because those are required to identify the failing function and operation.
Consequences
- Release health maps 1:1 to a commit sha; local builds are clearly labeled.
- One redaction contract to audit, enforced by
SentryObservabilityTests/SentryPipelineContractTests. - ASP.NET Core request transactions and the SDK's registered outgoing HTTP instrumentation cover inbound API calls and the platform's typed procurement client; explicit root transactions cover every hosted worker loop.
Alternatives considered
- Per-worker transaction setup (rejected: duplication drifted across five files).
- Sentry's default request payload with PII scrubbing (rejected: default-deny is safer than default-capture-then-scrub).