Skip to content

Migrate to Dynatrace New RUM Experience; single loading mechanism - #425

Draft
redbugz wants to merge 37 commits into
frontierMasterfrom
dynatrace-rum-upgrade
Draft

Migrate to Dynatrace New RUM Experience; single loading mechanism#425
redbugz wants to merge 37 commits into
frontierMasterfrom
dynatrace-rum-upgrade

Conversation

@redbugz

@redbugz redbugz commented Jul 29, 2026

Copy link
Copy Markdown

Summary

Migrates Frontier apps to the Dynatrace New RUM Experience (RUM on Grail) and reduces agent loading from three treatments across two feature flags to one flag with three states. Fixes a production 500, removes ~105 KB of dead agent bootstraps, and moves maintainer tooling out of the published package. Ships as @fs/react-scripts 8.17.0.

Full analysis: Dynatrace RUM: Loading Mechanisms & Agent Management (FRDOCS). Decision record in-repo: packages/react-scripts/tools/dynatrace/DYNATRACE_RUM_MECHANISMS.md.

What ships

Treatment Renders Role
off nothing Kill switch. Defined in int, beta and prod
asyncCS-inline versioned OneAgent tag + SRI, sync Retained fallback
anything else _complete.js from the global CDN, async Default

frontier_snow_dynatraceNewRUM is removed. It never controlled New RUM — both branches emitted the same URL and it only toggled async. New RUM enablement is the tenant's enabledOnGrail setting, per application, no deploy.

Reasoned Changes

Single loading mechanism, fail-open default (dynatrace.ejs)

What: Replaced six code paths (3 treatments × old/new RUM) with two. The default is a fallthrough, not an equality check on global-cdn.

Why: _complete.js is the only mechanism that tracks tenant configuration without a react-scripts release. Sampling production during the evaluation found two of the three treatments serving a RUM agent artifact dated 2022-11-04 — refreshing a self-hosted artifact required a fetch-script run, a release, and a redeploy of every consuming app, and that chain had not completed since 2022.

The fallthrough is deliberate: retired treatment names, a renamed flag, and a Split outage returning control must all still load RUM. Failing closed produces a silent monitoring gap, which is the one failure mode invisible in the data itself. There is a comment saying so — please don't tidy it into === 'global-cdn'.

Retained sync SRI arm as the fallback

What: Kept one non-default mechanism: the versioned OneAgent tag + SRI, loaded synchronously, under the existing asyncCS-inline treatment name.

Why: It covers both documented revisit triggers at once — immutably cached for a year (if Dynatrace never adds a cache validator to _complete.js) and synchronous (no blind window if async proves to lose early interactions). One arm rather than two.

The treatment name is historical and describes neither its load mode nor its format; renaming would need a coordinated Split-plus-deploy change.

Deleted all RUM Classic code paths (−219 lines, ~105 KB)

What: Removed edgeUrls, the old cdnUrls, and the three _inline_*.ejs bootstraps.

Why: Inlining is permanently ruled out for New RUM, not just deferred. EJS include() compiles the file as a template and the minified agent contains the EJS open-delimiter sequence inside a character-class string — this caused a production 500 (Could not find matching close tag for "<%"). It also re-shipped 300–460 KB in every HTML response with no caching.

Async readiness helper (layout.ejs)

What: Added dtWhenReady(which, fn) for both RUM APIs; enableManualPageDetection and window.dtinfo.appName no longer sit behind if (window.dtrum).

Why: That guard is always false under async loading, so both were silently inactive on the async treatments — manual page detection quietly fell back to automatic, and dtinfo.appName was never set. Verified fixed on int (see Testing).

Maintainer tooling moved out of the published package

What: fetch-dynatrace-scripts.js, its config artifact, and the RUM docs moved from scripts/ to tools/dynatrace/.

Why: scripts/ is in package.json files, so all of it was being published to consumers — including a script that needs a Dynatrace API token, AWS credentials and keychain access, and that carries internal infrastructure details (S3 bucket, tenant URL, keychain service names, the three APPLICATION-* entity IDs). tools/ is not in files, so exclusion is by construction rather than by remembering a negation pattern.

Published package: 119 files / 392 KB → 104 files / 247 KB. The only Dynatrace file still shipping is dynatrace.ejs, which consumers actually render.

Decision record and measurement harness

What: DYNATRACE_RUM_MECHANISMS.md (option matrix, candidates evaluated and rejected, revisit triggers, revival recipe) and beacon-harness/ with retained raw JSONL.

Why: The rejected candidates — inline bootstrap, self-hosted agent, auto-injection — each had a real case for them and lost for recorded reasons. Without that written down they get re-proposed. The harness measured New RUM's ~2.3× per-session upload increase and is the tool for re-verifying it after prod enablement.

Measurements behind the decision

Agent FCP cost ≈ download time (178 KB @ 205 KB/s → 869 ms predicted, 852 ms measured), stable across a 50× bandwidth range
Loading mechanism moves FCP by tens of ms — inside run-to-run variance
Agent payload moves FCP by hundreds of ms — the actual lever
Session Replay module 29,362 bytes gzipped ≈ +140 ms FCP
Enabling New RUM on prod +59,302 bytes ≈ +290 ms FCP (51% payload increase)
Beacon upload New RUM adds an unsampled ~19.7 KB/session channel alongside Classic — ~2.3× total
_complete.js caching max-age=86400, no ETag/Last-Modified → full re-download every 24 h

Performance was a tie across mechanisms, so the decision was made on maintenance and freshness.

Risk Assessment

Behaviour change on deploy: the tag becomes async. Production currently renders it synchronously in <head>, so every page render blocks on js-cdn.dynatrace.com. Async removes that render-path dependency at no measured FCP cost, but introduces a blind window — measured at 925 ms on int — during which early clicks, XHRs and errors are not captured. CWV and navigation timing are recoverable retroactively via buffered PerformanceObserver, so the cost is early interactions, not page-performance data.

Revert path: flip the flag to asyncCS-inline (sync, no blind window) or off. No deploy required.

Watch after rollout: RUM session volume for a step change across the async transition. Note the healthy sessions-per-render baseline is ~33%, not 100% — costAndTrafficControl: 33 means one session in three is monitored by design.

Ordering constraint for prod: agent 1.341 is missing three of the four Gen3 API methods the RUM JavaScript SDK requires; 1.343 has all four. Prod is on PREVIOUS_STABLE. Move the version channel and re-run the fetch script before enabling enabledOnGrail, or API-reported session properties silently will not land.

Not addressed here: layout.ejs injects only when NODE_ENV === 'production' and only where the layout renders, so custom error pages and 500s get no RUM. Pre-existing and independent of mechanism choice.

Testing

Verified on frontier-app-react int with 8.17.0-alpha.17 deployed:

tag rendered      _complete.js with async          ✓
window.dtrum      object, enableManualPageDetection present   ✓
window.dynatrace  object, all four Gen3 methods present       ✓
dtWhenReady       fires for both classic and grail            ✓
dtinfo.appName    "frontier-app-react" (set unconditionally)  ✓
agentReadyMs      925
removed cleanly   no ruxitagent / __dTCookie / rum_loading_mechanism  ✓

Template rendering asserted for every flag state, including unknown ones:

off             -> no agent
asyncCS-inline  -> SRI, SYNC, +integrity hash
global-cdn      -> complete.js async
asyncCS-script  -> complete.js async   (retired name, fail-open)
control         -> complete.js async   (Split outage, fail-open)
undefined       -> complete.js async

npm pack --dry-run confirms zero files from tools/ in the published package.

Review hotspots

  1. The fallthrough default in dynatrace.ejs — intentional, see the comment above it.
  2. tools/ is not in package.json files — that is what excludes maintainer material. Adding tools to files would silently re-publish it.
  3. cdnConfigNew strings were not retyped — the diff edits around them, so the ~1 KB data-dtconfig values are byte-intact.

Follow-ups (not in this PR)

  • Dynatrace support ticket filed for the missing ETag on _complete.js. If granted, the retained SRI arm can be removed entirely.
  • Re-run fetch-dynatrace-scripts.js immediately after prod moves to LATEST_STABLE — prod's SRI fallback is still 1.341 and the retained arm does not advance on its own.
  • Confirm Snow reads locals.dynatrace.* in production (fs-webdev/snow feat/dynatrace-rum-config-injection, not merged). Until then every value comes from the baked fallback.

redbugz and others added 30 commits May 26, 2026 14:06
- Add new inline scripts for all three environments (int, beta, prod) with agent version 10337260504112723
- Update dynatrace.ejs with frontier_snow_dynatraceNewRUM feature flag for gradual rollout
- When new flag is ON: use 1.329+ version with enhanced data collection (owasp=1, uxrgce=1)
- When new flag is OFF: use existing RUM version (backward compatible)
- All three mechanisms supported: asyncCS-script, asyncCS-inline, global-cdn
- New CDN URLs use SRI integrity hashes for added security
- Existing frontier_snow_dynatraceRUM flag unchanged (still controls mechanism selection)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add fetch-dynatrace-scripts.js helper for automated future RUM updates
- Fetches latest scripts from Dynatrace API
- Auto-writes inline scripts to _inline_*_new.ejs files
- Displays CDN URLs and integrity hashes in copy-paste format
- Add DYNATRACE_RUM_UPDATE.md with complete update procedures
- Documents API setup, entity IDs, and testing procedures
- Enables maintainers to automate RUM upgrades in the future

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Report the active loading mechanism (asyncCS-script, asyncCS-inline, or global-cdn) as a custom metric to Dynatrace. This enables direct DQL queries to correlate loading mechanism with Core Web Vitals performance (LCP, CLS, page load time).

- Add window.dtrum.reportCustomMetric call to emit rum_loading_mechanism value
- Bump version to 8.17.0-alpha.1 for telemetry feature

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The RUM agent needs the data-dtconfig attribute (which includes reportUrl) to send beacons directly to the Dynatrace tenant instead of back through the app. Previously, only src and integrity were being extracted from the Dynatrace API response.

Changes:
- Extract and preserve data-dtconfig from complete script tags
- Add cdnConfigNew object in dynatrace.ejs with per-environment configs
- Apply data-dtconfig attribute to asyncCS-script and global-cdn mechanisms
- Print config extraction confirmation in fetch script

This fixes the 405 POST errors by ensuring RUM beacons are routed to the correct Dynatrace backend endpoint (reportUrl), not to the app.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Includes full data-dtconfig with proper reportUrl for direct beacon delivery to Dynatrace tenant.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The new RUM script expects config in JSON format (data-config, data-appconfig, data-envconfig) which the fetch script provides in the inline script files. External script tags cannot reliably read data-* attributes, so we use the inline approach for both asyncCS-script and global-cdn mechanisms when new RUM is enabled.

This fixes the initialization error: 'DT_RUM will not initialize: None of the supported configuration sources contain the required properties [revision, environmentId, app]'

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ease

- Reflect version bump to 8.17.0-alpha.2 in package-lock.json
- Make fetch-dynatrace-scripts.js executable

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fixes RUM initialization by using inline script approach with proper JSON config format for new RUM.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Report rum_loading_mechanism metric immediately after each RUM script loads, rather than at the end of page. This ensures the custom metric is captured in the first page summary event before any beacons are sent.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fixes telemetry timing to report RUM loading mechanism before first page summary beacon.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
asyncCS-script should load the script asynchronously to not block page rendering. Add missing async attribute to old RUM Edge CDN script.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Update fetch-dynatrace-scripts.js to call /api/v2/rum/inlineCode endpoint for actual JavaScript code
- Write actual minified inline code to _inline_*_new.ejs files (300-460KB) instead of script tags
- Fix dynatrace.ejs to properly differentiate the three mechanisms:
  * asyncCS-script: async external from CDN (Edge for old, Dynatrace for new)
  * asyncCS-inline: full inline embedding (300-460KB for new, ~35KB for old)
  * global-cdn: synchronous external from Dynatrace CDN
- Each mechanism now clearly uses distinct loading patterns for telemetry differentiation

This enables accurate performance comparison of mechanisms via Dynatrace telemetry.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fetch and display:
- /api/v2/rum/javaScriptTag (simple tag, 157 chars)
- /api/v2/rum/oneAgentJavaScriptTagWithSri (SRI tag, ~1KB)
- /api/v2/rum/inlineCode (inline code, 300-460KB)

Shows both CDN URLs for comparison: basic variant vs new SRI variant.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Extract env values into separate helper function for reusability
- Update dynatrace.ejs with per-env fallback pattern using locals.dynatrace
- Generate dynatrace-rum-config.json for Snow to publish to CDN
- Snow can inject fresh values at runtime via locals.dynatrace override
- Clearer next steps in console output

This enables decoupling: react-scripts has built-in fallback values, but
Snow can override with latest values from dynatrace-rum-config.json CDN file.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Update dynatrace.ejs to support Snow providing fresh config values via
res.locals.dynatrace, with fallback to hardcoded values. This enables:

- Old Snow: transparently uses hardcoded fallback values
- New Snow: overrides with fresh values from CDN config file
- Old react-scripts + new Snow: new RUM works with stale but functional config
- New react-scripts + old Snow: new RUM works with publish-time config

For asyncCS-inline, also check for Snow-provided inline script before
falling back to included file.

This completes the progressive enhancement architecture where react-scripts
carries current values at publish time, and Snow can override them dynamically
without breaking older app/runtime combinations.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Script now publishes dynatrace-rum-config.json to S3 after updating local files.
Uses AWS CLI (aws s3 cp) instead of SDK, which:
- Leverages existing AWS credential infrastructure (~/.aws/credentials or env vars)
- No additional Node dependencies needed
- Simpler setup: just 'aws configure' or set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
- Works with standard AWS credential chain (no custom keychain storage)

Configuration:
- S3_PUBLISH_BUCKET: S3 bucket for config file (default: frontier-rum-config)
- S3_PUBLISH_REGION: AWS region (default: us-east-1)
- Both can be overridden via environment variables

Keychain setup now only stores Dynatrace API token (AWS uses standard auth).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Simplify the configuration property names published to S3 and used by Snow:
- cdnUrlsNew → cdnUrls
- cdnIntegrityNew → cdnIntegrity
- cdnConfigNew → cdnConfig
- inlineScriptNew → inlineScript

Updates:
- fetch-dynatrace-scripts.js: generate cleaner property names
- dynatrace.ejs: read from locals.dynatrace with cleaner keys
- dynatrace-rum-config.json: updated config structure

The 'New' postfix was redundant since these are already scoped to the new RUM.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Snow servers and other services can now fetch dynatrace-rum-config.json
without requiring AWS credentials or special access.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ents

Convert template literals to string concatenation in include() calls for
asyncCS-inline, asyncCS-script, and global-cdn fallback branches. EJS
parser cannot handle backticks inside template tags.

Also consolidate reporting mechanism calls to reduce duplication.
EJS requires <%- %> (with hyphen) to output included file content.
Control-flow syntax <% %> cannot execute include() function calls.
Revert to <% include(...) %> syntax without hyphen, matching the pattern
in layout.ejs. Remove leading './' from path since include() resolves
relative to views directory.
EJS include directive requires a static identifier or variable, not
inline function calls. Pre-compute dynamic paths using string concatenation,
then pass the variable to include for proper resolution.
…ents

Convert template literals to string concatenation in include() calls for
asyncCS-inline, asyncCS-script, and global-cdn fallback branches. EJS
parser cannot handle backticks inside template tags. Use <%- include() %>
with string concatenation to support dynamic path calculation.
Log which treatment and code path is taken during dynatrace initialization
to confirm we're running the latest deployed version.
Replace optional chaining (?.) with standard property access checks.
Pre-compute include paths as variables to avoid string concatenation
in include() calls, which may confuse EJS parser.
The new RUM agent's minified JS contains the "<%" sequence, which EJS
include() misreads as an unterminated scriptlet ("Could not find matching
close tag"), causing a 500. Load the new agent exclusively via <script>
tags (SRI tag or _complete.js) instead of inlining, and remap each
treatment to a Dynatrace snippet format:
  asyncCS-script -> OneAgent JS tag + SRI, async
  asyncCS-inline -> OneAgent JS tag + SRI, sync (no longer inlines)
  global-cdn     -> _complete.js, async

- Rewrite fetch-dynatrace-scripts.js: drop /inlineCode fetch and
  _inline_*_new.ejs generation; add cdnCompleteUrls; robust fallback
  rewriter that preserves the locals-first progressive-enhancement guard
- Delete generated _inline_*_new.ejs; old-RUM _inline_*.ejs untouched
- Refresh CDN config to agent 10337260526121002; update docs and changelog

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…um-upgrade

# Conflicts:
#	CHANGELOG-FRONTIER.md
#	package-lock.json
#	packages/react-scripts/package.json
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
redbugz and others added 4 commits July 29, 2026 06:56
…n fix

- Preflight 'aws sts get-caller-identity' before any Dynatrace work; warn when
  AWS_PROFILE is unset and print SSO-aware setup guidance on failure (fail fast)
- Rely on the standard AWS_PROFILE / credential chain (aws children inherit env)
- Print the downloaded RUM agent version at the end of the run
- Align KEYCHAIN_SERVICE_DYNATRACE with the stored keychain item + fix setup doc

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consume the latest RUM release via fetch-dynatrace-scripts.js: refresh the
dynatrace.ejs publish-time fallbacks and dynatrace-rum-config.json (also
republished to S3). Supersedes agent 10337260526121002.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop the per-render '[dynatrace.ejs] Loaded' server log (redundant with the
  existing sendSessionProperties RUM telemetry; avoids prod log spam)
- Sync package-lock.json self-version to 8.17.0 to match package.json (final release)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@redbugz
redbugz marked this pull request as draft July 29, 2026 16:28
@redbugz
redbugz requested a review from Copilot July 29, 2026 16:28

Copilot AI 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.

Pull request overview

This PR upgrades Dynatrace RUM to a newer agent version and switches new-RUM loading to <script> tags (avoiding EJS include() compilation failures), adds runtime-config fallbacks via locals.dynatrace.*, and introduces tooling/docs to automate future RUM updates and publishing of a config artifact.

Changes:

  • Reworked dynatrace.ejs to support dual-flag rollout (mechanism vs version) and load new RUM exclusively via script tags with SRI/config support.
  • Added a fetch/publish automation script plus a generated dynatrace-rum-config.json artifact for Snow runtime injection.
  • Bumped @fs/react-scripts to 8.17.0 and documented/released the change.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/react-scripts/layout/views/partials/dynatrace.ejs Adds new-RUM script-tag branches, locals-first runtime config, and dual-flag gating.
packages/react-scripts/scripts/fetch-dynatrace-scripts.js New automation script to fetch Dynatrace tags, update EJS fallbacks, generate config JSON, and publish to S3.
packages/react-scripts/scripts/dynatrace-rum-config.json Generated per-env CDN URL/SRI/config values for Snow to consume at runtime.
packages/react-scripts/scripts/DYNATRACE_RUM_UPDATE.md Operator runbook for fetching/publishing and validating treatment mappings.
packages/react-scripts/package.json Version bump to 8.17.0.
package-lock.json Lockfile version update for the react-scripts package entry.
CHANGELOG-FRONTIER.md Release notes for 8.17.0 Dynatrace RUM changes.
Comments suppressed due to low confidence (3)

packages/react-scripts/scripts/DYNATRACE_RUM_UPDATE.md:81

  • This relative link points to ./partials/dynatrace.ejs, but the actual file lives under layout/views/partials/dynatrace.ejs. As written, the link is broken.
Two flags drive the experiment (see [dynatrace.ejs](./partials/dynatrace.ejs)):

packages/react-scripts/scripts/DYNATRACE_RUM_UPDATE.md:184

  • These relative links are incorrect from the scripts/ directory. dynatrace.ejs is under layout/views/partials/, and fetch-dynatrace-scripts.js is the current file (same directory), so the links should be updated to avoid 404s in the runbook.
- [dynatrace.ejs](./partials/dynatrace.ejs) - Main RUM configuration file
- [fetch-dynatrace-scripts.js](./fetch-dynatrace-scripts.js) - The fetch script itself

packages/react-scripts/scripts/DYNATRACE_RUM_UPDATE.md:73

  • The example snippet treats cdnIntegrityNew as a single string, but dynatrace.ejs defines cdnIntegrityNew as an environment-keyed object (and integrity values may differ per env). The example should reflect the actual shape.
```javascript
const cdnUrlsNew = {
  int: '...',    // Copy from script output
  beta: '...',   // Copy from script output
  prod: '...'    // Copy from script output

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/react-scripts/scripts/DYNATRACE_RUM_UPDATE.md Outdated
Comment thread packages/react-scripts/layout/views/partials/dynatrace.ejs
Comment thread packages/react-scripts/scripts/fetch-dynatrace-scripts.js
redbugz and others added 3 commits July 29, 2026 23:34
- dynatrace.ejs: poll for window.dtrum before sendSessionProperties so the
  rum_loading_mechanism signal isn't dropped for async treatments
  (asyncCS-script / global-cdn), where the agent loads after the inline script
- fetch-dynatrace-scripts.js: move temp-file cleanup into a finally block so it
  isn't left behind when the S3 upload fails
- DYNATRACE_RUM_UPDATE.md: Step 2 now reflects that the script auto-updates the
  fallbacks and that integrity/config are per-environment (no single shared hash)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse Dynatrace RUM loading from three treatments across two flags down
to one flag with three states, and remove all RUM Classic code paths.

  off             no agent (kill switch, defined in int/beta/prod)
  asyncCS-inline  versioned OneAgent tag + SRI, loaded sync
  anything else   _complete.js from the global CDN, loaded async

The default is a fallthrough rather than an equality check on 'global-cdn'.
Retired treatment names, a renamed flag, and a Split outage returning
'control' must all still load RUM: failing closed produces a silent
monitoring gap, which is the one failure mode invisible in the data itself.

Remove frontier_snow_dynatraceNewRUM. It never controlled the New RUM
Experience -- both its branches emitted the same URL and it only toggled
`async`. New RUM enablement is the tenant's `enabledOnGrail` setting,
toggled per application with no deploy.

_complete.js from the Dynatrace CDN is the only mechanism that tracks tenant
configuration without a react-scripts release. Two of the three previous
treatments had been serving a RUM agent artifact dated 2022-11-04 to
production, because refreshing a self-hosted artifact required a fetch-script
run, a release, and a redeploy of every consuming app.

The retained SRI arm covers both documented fallback cases at once: it is
immutably cached for a year (if Dynatrace never adds a cache validator to
_complete.js) and synchronous (no blind window if async proves to lose early
interactions or errors).

Delete the three _inline_*.ejs bootstraps (~105 KB). Inlining is permanently
ruled out for New RUM: EJS include() compiles the file as a template and the
minified agent contains the EJS open-delimiter sequence inside a string,
which caused a production 500 (Could not find matching close tag for "<%").

Add dtWhenReady(which, fn) for both RUM APIs. enableManualPageDetection and
window.dtinfo.appName previously sat behind `if (window.dtrum)`, which is
always false under async loading -- they were silently inactive on the async
treatments.

Drop the rum_loading_mechanism session property: with one mechanism there is
nothing to segment by.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
scripts/ is for scripts consumers of @fs/react-scripts run (build, start,
test, init, eject). The Dynatrace fetch script, its config artifact, and the
RUM docs are maintainer-only -- the fetch script needs a Dynatrace API token,
AWS credentials and keychain access, and is documented as being run from a
checkout rather than from node_modules.

Because "scripts" is in package.json `files`, all of it was being published.
Moving it to tools/, which is not in `files`, excludes it by construction --
no negation patterns to remember when adding future maintainer material.

The fetch script also carried internal infrastructure details into the
published artifact: the S3 bucket, the tenant URL, keychain service names,
and the three APPLICATION-* entity IDs.

Published package: 119 files / 392 KB -> 104 files / 247 KB, combined with
the _inline_*.ejs removal in the previous commit. The only Dynatrace file
still shipping is dynatrace.ejs, which consumers actually render.

Also here:

- Add DYNATRACE_RUM_MECHANISMS.md -- the option matrix, the candidates that
  were evaluated and rejected (inline bootstrap, self-hosted agent,
  auto-injection) with the case for each and why it lost, the decision
  rationale, revisit triggers, and the revival recipe.
- Add beacon-harness/ with the retained raw JSONL from the run that measured
  New RUM's ~2.3x per-session upload increase. Standalone: its package.json
  is not picked up by the workspace, whose glob is packages/* (direct
  children only).
- Fix paths broken by the move: the fetch script's relative path to
  dynatrace.ejs, and the doc cross-links.
- Rewrite the unreleased 8.17.0 changelog entry, which described the
  superseded dual-flag design.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@redbugz redbugz changed the title Upgrade Dynatrace RUM: script-tag loading, runtime config, fetch tooling Migrate to Dynatrace New RUM Experience; single loading mechanism Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants