Skip to content

Sentry Improvements (WIP) - #4829

Draft
AlexAscherson wants to merge 15 commits into
mainfrom
maintainance/sentry-updates
Draft

Sentry Improvements (WIP)#4829
AlexAscherson wants to merge 15 commits into
mainfrom
maintainance/sentry-updates

Conversation

@AlexAscherson

@AlexAscherson AlexAscherson commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What does this change?

  • Added frontend release tagging support by injecting build commit id into the page in main.scala and reading it during Sentry init in sentry.js
  • Added optional sourcemap upload workflow for Kahuna
  • Implemented shared Scala backend Sentry coverage at the Play infrastructure layer in GridComponents.scala so unhandled server errors can be captured consistently across services using GridComponents.
  • Added backend Sentry config fields in CommonConfig.scala for activation, DSN, and environment control.

Note: New env vars required for reporting - no failure if not present

For sourcemaps:
SENTRY_AUTH_TOKEN
SENTRY_ORG
SENTRY_PROJECT

SENTRY_RELEASE currently set to github.sha in ci.yml

For scala sentry coverage:

sentry.enabled
sentry.dsn
sentry.environment

are required in CommonConfig.scala / defaults in application.conf

Further deployment notes - NB the following is AI generated and needs to be edited / reviewed before we merge

Sentry Setup & Deployment Guide

This document describes the actions required to enable and deploy the Sentry error
monitoring added on the maintainance/sentry-updates branch. It covers both the
backend (Scala/Play services) and the frontend (Kahuna browser app).


1. What this branch enables

Tier What is reported How the release is identified
Backend (all Play services) Unhandled 5xx server errors via the HTTP error handler BuildInfo.gitCommitId (baked in at build time)
Frontend (Kahuna) Uncaught AngularJS exceptions (excluding failed HTTP requests) + console.warn / console.error window.SENTRY_RELEASE.id = BuildInfo.gitCommitId

Both tiers are off by default (sentry.enabled = false) and only activate when
explicitly enabled and a DSN is provided.


2. One-time setup (Sentry + GitHub)

2.1 Create Sentry project(s)

  1. In Sentry, create a project (or two, if you want to separate backend and
    frontend events — see note in §4.3).
  2. Note the DSN for each project.
  3. Note your Sentry org slug and project slug.

2.2 Create a Sentry auth token

Create an internal integration / auth token with permissions to create releases
and upload files:

  • project:releases
  • project:read

2.3 Add GitHub Actions secrets

In the repository settings (Settings → Secrets and variables → Actions), add:

Secret Value Used by
SENTRY_AUTH_TOKEN The auth token from §2.2 Sourcemap upload
SENTRY_ORG Your Sentry org slug Sourcemap upload
SENTRY_PROJECT Your Sentry (frontend) project slug Sourcemap upload

SENTRY_RELEASE is not a stored secret — CI derives it from ${{ github.sha }}
automatically (see .github/workflows/ci.yml).


3. Per-environment configuration (deploy targets)

Backend and frontend are both driven by the Play service configuration for each
service. The defaults live in common-lib/src/main/resources/application.conf:

sentry {
  enabled = false
  # dsn = ""
  # environment = "dev"
}

To enable Sentry in a given environment, override these in that environment's config
(e.g. the deployed application.conf / config bucket for the stage):

sentry {
  enabled = true
  dsn = "https://<key>@<org>.ingest.sentry.io/<project-id>"
  # environment defaults to the lower-cased STAGE (e.g. "prod", "test") if omitted
  # environment = "prod"
}

Config keys (from CommonConfig)

Key Type Default Notes
sentry.enabled Boolean false Master on/off switch for both tiers
sentry.dsn String (optional) unset Empty/unset ⇒ Sentry stays off even if enabled = true
sentry.environment String lower-cased STAGE Sets the Sentry "environment" tag

Important behaviour

  • sentry.enabled gates both tiers. The frontend DSN is only rendered into the
    page (<link rel="sentry-dsn"> in main.scala.html) when sentry.enabled is true,
    so setting enabled = false reliably silences the browser app as well as the server.
  • No runtime env var is required for the backend release. The release is baked in
    at build time from BuildInfo.gitCommitId; the deployed process does not need
    SENTRY_RELEASE or BUILD_VCS_NUMBER set.

4. Releases & sourcemaps (CI)

4.1 What CI does

The Kahuna (client-side) step in .github/workflows/ci.yml builds the production
bundle and uploads sourcemaps to Sentry:

env:
  SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
  SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
  SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
  SENTRY_RELEASE: ${{ github.sha }}
run: |
  npm install-clean
  npm run undist
  npm test
  npm run dist
  npm run sentry:upload-sourcemaps

The sentry:upload-sourcemaps npm script (in kahuna/package.json) uses the
@sentry/cli dev dependency to:

  1. create the release (releases new),
  2. upload sourcemaps from public/dist with url-prefix ~/assets/dist (--validate),
  3. finalize the release (releases finalize).

The script is a no-op (prints a skip message) if any of the four SENTRY_* env vars
are missing, so forks / environments without Sentry secrets are unaffected.

4.2 The release-name consistency requirement

For uploaded sourcemaps to resolve against browser events, the release name used at
upload time must equal the release the app reports at runtime:

  • Upload release = SENTRY_RELEASE = ${{ github.sha }}
  • Runtime release = window.SENTRY_RELEASE.id = BuildInfo.gitCommitId

gitCommitId is resolved in build.sbt in this order:
BUILD_VCS_NUMBER (TeamCity, deprecated — not set in GitHub Actions) →
GITHUB_SHA (set by GitHub Actions, identical to github.sha) →
local git rev-parse HEAD (dev only).

Because CI runs on GitHub Actions, GITHUB_SHA is used, which is the same value as
the SENTRY_RELEASE: ${{ github.sha }} used for the sourcemap upload — so the runtime
and upload releases are guaranteed to match, including for PR merge-commit builds.

Once TeamCity is fully retired, the BUILD_VCS_NUMBER entry in build.sbt can be
removed; it is currently harmless because it is never set in the GitHub Actions
environment.

4.3 Note: Kahuna uses one DSN for both tiers

Kahuna's sentry.dsn is used for both its server-side SentrySupport and the
browser app (via the rendered link tag). If you want frontend and backend events in
separate Sentry projects, that requires a code change (a distinct config key for
the browser DSN); the current design shares one DSN per service.


5. Deployment checklist

  • Sentry project(s) created; DSN(s) recorded.
  • SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT added as GitHub Actions secrets.
  • Target environment config sets sentry.enabled = true and sentry.dsn = "..."
    (and optionally sentry.environment).
  • Deployed and verified (see §6).

6. Verification

  1. Backend: trigger a 5xx (e.g. a known failing endpoint in a test stage) and
    confirm the event appears in Sentry with tags app, stage, method, path,
    and requestId.
  2. Frontend: run console.error('sentry test') in the browser console on Kahuna
    and confirm the event appears, with the correct release and a de-minified stack
    trace (proves sourcemaps resolved).
  3. Confirm the environment tag matches the stage.

7. Disabling / rollback

  • Set sentry.enabled = false in the environment config and redeploy — this stops
    both backend and frontend reporting immediately. No code change or DSN removal
    required.

--- End of AI text

How should a reviewer test this change?

How can success be measured?

Who should look at this?

Tested? Documented?

  • locally by committer
  • locally by Guardian reviewer
  • on the Guardian's TEST environment
  • relevant documentation added or amended (if needed)

@AlexAscherson
AlexAscherson requested a review from a team as a code owner July 27, 2026 14:43
@AlexAscherson AlexAscherson added javascript Pull requests that update Javascript code scala2.13 maintenance Departmental tracking: maintenance work, not a fix or a feature labels Jul 27, 2026
@AlexAscherson AlexAscherson changed the title Sentry Improvements Sentry Improvements (wip) Jul 27, 2026
Fx collision with shared config
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

@AlexAscherson AlexAscherson changed the title Sentry Improvements (wip) Sentry Improvements Jul 27, 2026
Comment on lines +24 to +25
# dsn = ""
# environment = "dev"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I assume these commented variables are ones you would want to set if enabling sentry. Is it worth adding a comment here explaining that, or will it be obvious to everyone?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(Alternatively we could document that possibility in the readme maybe?)

Comment thread kahuna/package.json Outdated
"lint": "eslint public/js && htmllint public/js/**/*.html",
"lint-fix": "eslint public/js --fix && htmllint public/js/**/*.html",
"dist": "webpack --config webpack.config.prod.js",
"sentry:upload-sourcemaps": "if [ -n \"$SENTRY_AUTH_TOKEN\" ] && [ -n \"$SENTRY_ORG\" ] && [ -n \"$SENTRY_PROJECT\" ] && [ -n \"$SENTRY_RELEASE\" ]; then npx -y @sentry/cli@2 releases new \"$SENTRY_RELEASE\" && npx -y @sentry/cli@2 releases files \"$SENTRY_RELEASE\" upload-sourcemaps public/dist --url-prefix '~/assets/dist' --validate && npx -y @sentry/cli@2 releases finalize \"$SENTRY_RELEASE\"; else echo 'Skipping Sentry sourcemap upload (missing Sentry env vars)'; fi",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is it possible to conveniently spin this out into a separate script that we call from here? I know it’s not a very complex script, but it’d be nicer to edit if it weren’t inlined in a string like this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It’s cool to learn how this is done though! I took a look at these docs, which are hopefully the correct ones. A couple of other options are listed there, like creating deploys and setting commits: is it worth trying to do those as well? (Maybe in a later PR?)

@AlexAscherson AlexAscherson changed the title Sentry Improvements Sentry Improvements (WIP) Aug 13, 2026
Comment on lines +13 to +28
def init(config: CommonConfig): Unit = {
if (isEnabled(config)) {
Sentry.init((options: SentryOptions) => {
options.setDsn(config.sentryDsn.get)
options.setEnvironment(config.sentryEnvironment)
options.setServerName(config.appName)
options.setRelease(sys.env.getOrElse("BUILD_VCS_NUMBER", "unknown"))
})
}
}

def shutdown(config: CommonConfig): Future[Unit] = Future.successful {
if (isEnabled(config)) {
Sentry.close()
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is it definitely ok to use Future.successful here (and not use Futures at all for init)? It’s hard to see exactly what work Sentry.close() and Sentry.init() do, but it seems plausible that there might be blocking IO in there, so I’d wonder about erring on the side of caution and choosing to put the calls on a separate execution context.

@AlexAscherson
AlexAscherson marked this pull request as draft August 19, 2026 13:49
<script>
window.SENTRY_RELEASE = { id: "@utils.buildinfo.BuildInfo.gitCommitId" };
</script>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Alternatives to mutating this set as a config var?

// (i.e. after the subclass constructor has completed).
private lazy val sentryInitialised: Unit =
SentrySupport.init(config, buildInfo.gitCommitId)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Follow up on this logic

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

Labels

javascript Pull requests that update Javascript code maintenance Departmental tracking: maintenance work, not a fix or a feature scala2.13

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants