Skip to main content
🗓️ Schedule discussionOpens Google Calendar — add attendees there.

Tag Datadog logs and traces with X-App-Version / X-App-Platform from mobile

ClaimedAuthor: @relbnsOwner: @relbnsEffort: S

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 AppInfoTaggingInterceptor in apps/external-gateway/src/interceptors/app-info-tagging.interceptor.ts.
  • Reads x-app-version and x-app-platform from req.headers (lowercased by Node).
  • Uses dd-trace's tracer.scope().active().setTag(...) to add app.version / app.platform to 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 unknown rather than skip — keeps the tag dimension consistent so dashboards don't have to handle nulls.

Open questions

  • Should we also add app.git_sha later? 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.

Triage notes

To be filled in at the next triage session.