Dynatrace’s JavaScript Real User Monitoring (RUM) agent is a powerful tool for understanding user experience in your web applications, but its setup can feel like a black box. The most surprising thing is that it works by passively observing everything your browser does, then intelligently filtering and sending only the most relevant data, rather than explicitly being told what to monitor.

Let’s see it in action. Imagine you have a simple React app. To instrument it for Dynatrace RUM, you’ll typically add a JavaScript snippet to your index.html file. This snippet, provided by Dynatrace, bootstraps the RUM agent.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Awesome App</title>
    <!-- Dynatrace RUM Snippet -->
    <script>
        window.dtConfig = {
            // Your Dynatrace environment ID
            applicationID: "YOUR_APP_ID",
            // Your Dynatrace environment URL
            beaconUrl: "https://YOUR_TENANT.live.dynatrace.com/bf/bf"
        };
    </script>
    <script src="https://YOUR_TENANT.live.dynatrace.com/bf/bf.js" defer></script>
    <!-- End Dynatrace RUM Snippet -->
</head>
<body>
    <div id="root"></div>
    <script src="bundle.js"></script>
</body>
</html>

Once this script is loaded, the Dynatrace agent starts observing. It hooks into browser APIs like fetch, XMLHttpRequest, addEventListener, and even setTimeout and requestAnimationFrame. When a user interacts with your app – perhaps by clicking a button that triggers an API call – the agent captures the initiation of that request, the response, and any errors. It also monitors page loads, JavaScript errors, and user actions. This raw data is then processed to create metrics like Apdex scores, user session durations, and detailed traces of frontend requests.

The core problem Dynatrace RUM solves is bridging the gap between server-side performance and the actual user experience. Server logs might show a 200ms response time for an API call, but if the browser takes another 500ms to render that data, or if a JavaScript error prevents the UI from updating, the user’s perception is of a slow or broken application. Dynatrace RUM captures this entire end-to-end journey from the user’s perspective.

Internally, the agent works by creating "OneAgent" instances within the browser’s JavaScript execution context. These instances use a combination of techniques:

  • Monkey-patching: It overrides native browser functions (like fetch) to intercept calls.
  • Event Listeners: It attaches listeners to various DOM events and browser lifecycle events.
  • Performance API: It leverages browser-provided performance APIs for precise timing information.
  • Error Handling: It wraps global error handlers (window.onerror, window.onunhandledrejection) to capture exceptions.

The dtConfig object is your primary lever for controlling the agent’s behavior. Key properties include:

  • applicationID: This is crucial. It tells Dynatrace which application in your environment this data belongs to. Without it, data won’t be associated correctly.
  • beaconUrl: This is the endpoint on your Dynatrace tenant where the agent sends its captured data.
  • spaMonitoring: When set to true (often the default for modern frameworks), the agent understands Single Page Application route changes as distinct "user actions," providing more granular insights than just initial page loads.
  • maxBeaconsPerMinute: To prevent overwhelming your tenant or network, you can cap the number of beacons sent.
  • jsApiInjection: If true (default), the agent automatically instruments XMLHttpRequest and fetch. You can disable this and manually instrument if needed for specific control.

The agent can dynamically adjust its sampling rate based on the load and health of your Dynatrace environment, ensuring it provides value without becoming a performance burden itself. It also performs client-side aggregation of data before sending it to the backend, reducing the volume of raw telemetry.

What most people don’t realize is how deeply the agent integrates with the browser’s event loop. It doesn’t just time the fetch request; it times the entire process from when you initiate an action (like clicking a button) through the asynchronous fetch call, the browser rendering the response, and any subsequent JavaScript that updates the DOM. This holistic timing is what allows it to accurately attribute frontend performance bottlenecks to specific user interactions or code paths.

Once you have this data flowing, the next step is to understand how to correlate frontend user actions with backend service calls and infrastructure metrics.

Want structured learning?

Take the full Dynatrace course →