Tag Datadog logs and traces with X-App-Version / X-App-Platform from mobile
Problem
When a leader reports a bug ("I can't log in", "scanner won't open"), support has no fast way to find out which app build they're on. Today the only signal is the user's word — which is often wrong, sometimes missing, and never queryable.
This shows up most painfully during release windows. After the 1.8.4 launch we coordinated a backend deploy that depended on the mobile being on a specific build. With no version data in the BE logs, we couldn't tell during the rollout which traffic was on which client — we had to gate the backend release on store-rollout instead of on actual adoption.
Proposal
Leaders-app 1.8.5 already sends X-App-Version and X-App-Platform headers on every outgoing request (shipped in leaders-app PR #20, task CU-86ca02ybf). The BE doesn't read them yet.
Add a small NestJS interceptor in apps/external-gateway/src/interceptors/ that reads both headers and attaches them as Datadog tags on every log line, trace, and span produced by the request. After this lands, support / on-call can filter Datadog by app.version:1.8.4+42 or group by app.platform to see version-bucketed error rates.
Why now / Why us
- The mobile side is already shipping the headers — the cost on the BE is one interceptor, ~30 lines.
- No other team owns this. Whoever owns the mobile release pipeline (= me) is the right person to close the loop.
- It unlocks a wider pattern (
X-App-Version-aware behavior — version-gated enum responses, force-update prompts, A/B routing) without committing to that pattern up front. Tagging is the smallest possible first step.
Sketch of approach
- New interceptor
AppInfoTaggingInterceptorinapps/external-gateway/src/interceptors/app-info-tagging.interceptor.ts. - Reads
x-app-versionandx-app-platformfromreq.headers(lowercased by Node). - Uses
dd-trace'stracer.scope().active().setTag(...)to addapp.version/app.platformto the active span, which Datadog propagates to logs and downstream spans via the standard trace context. - Registered globally on the gateway module so every request gets it for free.
- Defensive: if the headers are absent (e.g. old leaders-app build, or non-mobile caller), set the tag to
unknownrather than skip — keeps the tag dimension consistent so dashboards don't have to handle nulls.
Open questions
- Should we also add
app.git_shalater? Mobile would need to send it; not part of this proposal. - Where does the interceptor live for
feeds(the public customer API)? That gateway sees external customer traffic, not mobile. Probably skip it there.
Related
- leaders-app PR #20 — mobile side, already in master.
- CU-86ca02ybf — original ClickUp task for the mobile headers + BE consumption.
Triage notes
To be filled in at the next triage session.