Enrich and control browser RUM data with beforeSend

Enrich and control browser RUM data with beforeSend

Overview

The RUM browser SDK captures RUM events and populates their main attributes. The beforeSend callback function gives you access to every event collected by the RUM SDK before they are sent to Datadog. Intercepting the RUM events allows you to:

  • Enrich your RUM events with additional context attributes
  • Modify your RUM events to modify their content, or redact sensitive sequences (see list of editable properties)
  • Discard selected RUM events

Event and context structure

The beforeSend callback function gives you access to two objects: event and context.

function beforeSend(event, context)

Event

The event is generated by the RUM SDK. Find more details about different event types and collected attributes in the RUM data collected documentation.

The event.type property lets you identify the type of the event:

{
    ...,
    "event.type": "resource",
    ...
}

Context

The context is made of the Browser APIs that trigger the event creation. The context values depend on the event.type:

RUM event typeContext
ViewLocation
ActionEvent
Resource (XHR)XMLHttpRequest and PerformanceResourceTiming
Resource (Fetch)Request, Response, and PerformanceResourceTiming
Resource (Other)PerformanceResourceTiming
ErrorError
Long TaskPerformanceLongTaskTiming

More details about the structure of context objects is available in the Browser SDK repository.

Examples

Collect HTTP headers from a Fetch response

Collect HTTP headers from a Fetch response with the following beforeSend configuration. Additional context attributes must be stored in the event.context object.

import { datadogRum } from '@datadog/browser-rum';

datadogRum.init({
    ...,
    beforeSend: (event, context) => {
        // collect a RUM resource's response headers
        if (event.type === 'resource' && event.resource.type === 'fetch') {
            event.context = {...event.context, responseHeaders: context.response.headers}
        }
    },
    ...
});

The following tabs show the information contained in the beforeSend event and context objects for this example:

{
    "application": {
        "id": "<YOUR_APPLICATION_ID>"
    },
    "date": 1623933472075,
    "service": "shopist-web-ui",
    "session": {
        "type": "user",
        "id": "308f14ac-2a27-4b50-945c-be345778994f",
        "has_replay": true
    },
    "view": {
        "id": "768f0eb9-39c5-4a1f-9c13-476bd08166bb",
        "referrer": "http://localhost:3000/",
        "url": "http://localhost:3000/"
    },
    "resource": {
        "id": "e5d1d3a4-7240-4910-bd0a-af253e06d301",
        "type": "fetch",
        "duration": 2577300000,
        "method": "get",
        "status_code": 200,
        "url": "https://api.shopist.io/products.json",
        "size": 10307,
        "download": {
            "duration": 1800000,
            "start": 2575500000
        },
        "first_byte": {
            "duration": 2574600000,
            "start": 900000
        }
    },
    "type": "resource",
    "context": {
        "browser_test": false,
        "usr.email": "jane@doe.com",
        "usr.id": "f57eg30cc9"
    }
}
{
    "performanceEntry": {
        "name": "https://api.shopist.io/products.json",
        "entryType": "resource",
        "startTime": 230,
        "duration": 2577.300000011921,
        "initiatorType": "fetch",
        "nextHopProtocol": "h2",
        "workerStart": 0,
        "redirectStart": 0,
        "redirectEnd": 0,
        "fetchStart": 230,
        "domainLookupStart": 230,
        "domainLookupEnd": 230,
        "connectStart": 230,
        "connectEnd": 230,
        "secureConnectionStart": 230,
        "requestStart": 230.90000000596046,
        "responseStart": 2805.5,
        "responseEnd": 2807.300000011921,
        "transferSize": 10743,
        "encodedBodySize": 10307,
        "decodedBodySize": 10307,
        "serverTiming": [],
        "workerTiming": []
    },
    "response": {
        "body": (...),
        "bodyUsed": true,
        "headers": Headers {},
        "ok": true,
        "redirected": false,
        "status": 200,
        "statusText": "",
        "type": "basic",
        "url": "https://api.shopist.io/products.json"
    },
    "requestInput": "https://api.shopist.io/products.json",
    "requestInit": {
        "headers": [
            [
                "Content-Type",
                "application/json; charset=utf-8"
            ],
            [
                "x-datadog-origin",
                "rum"
            ],
            [
                "x-datadog-parent-id",
                "595857188965892467"
            ],
            [
                "x-datadog-sampled",
                "1"
            ],
            [
                "x-datadog-sampling-priority",
                "1"
            ],
            [
                "x-datadog-trace-id",
                "796856647783126791"
            ]
        ],
        "method": "get",
        "cache": "no-cache"
    }
}

Discard a frontend error

Discard frontend errors if their message includes “profile is not defined” with the following beforeSend configuration:

import { datadogRum } from '@datadog/browser-rum';

datadogRum.init({
    ...,
    beforeSend: (event, context) => {
        // discard a RUM error if its message includes 'profile is not defined'
        if (event.type === 'error' && event.error.message.includes('profile is not defined')) {
            return false
        }
    },
    ...
});

The following tabs show the information contained in the beforeSend event and context objects for this example:

{
    "application": {
        "id": "75d50c62-8b66-403c-a453-aaa1c44d64bd"
    },
    "date": 1623941859639,
    "service": "shopist-web-ui",
    "session": {
        "type": "user",
        "id": "4203a142-1e3c-41b0-822d-316705d98f19",
        "has_replay": true
    },
    "view": {
        "id": "0a771c95-9bc4-4640-978e-ad28da64da45",
        "referrer": "http://localhost:3000/profile",
        "url": "http://localhost:3000/profile-edit"
    },
    "action": {
        "id": "7b30e681-ce5c-47a8-ac22-6aff8be59744"
    },
    "error": {
        "id": "3c0295b1-da48-4827-93c9-ea06be4aafd9",
        "message": "profile is not defined",
        "source": "source",
        "stack": "ReferenceError: profile is not defined\n  at VueComponent.discardEdit @ http://localhost:3000/_nuxt/pages/profile-edit.js:911:41\n  at invokeWithErrorHandling @ http://localhost:3000/_nuxt/commons.app.js:12167:26\n  at VueComponent.invoker @ http://localhost:3000/_nuxt/commons.app.js:12492:14\n  at invokeWithErrorHandling @ http://localhost:3000/_nuxt/commons.app.js:12167:26\n  at VueComponent.Vue.$emit @ http://localhost:3000/_nuxt/commons.app.js:14196:9\n  at VueComponent.cancelDraft @ http://localhost:3000/_nuxt/pages/profile-edit.js:828:12\n  at invokeWithErrorHandling @ http://localhost:3000/_nuxt/commons.app.js:12167:26\n  at HTMLButtonElement.invoker @ http://localhost:3000/_nuxt/commons.app.js:12492:14\n  at HTMLButtonElement.original._wrapper @ http://localhost:3000/_nuxt/commons.app.js:17221:25",
        "type": "ReferenceError",
        "handling": "unhandled"
    },
    "type": "error",
    "context": {
        "browser_test": false,
        "usr.email": "jane@doe.com",
        "usr.id": "f57eg30cc9"
    }
}
{
    "error": {
        "message": "profile is not defined",
        "stack": "ReferenceError: profile is not defined\n  at VueComponent.discardEdit @ http://localhost:3000/_nuxt/pages/profile-edit.js:911:41\n  at invokeWithErrorHandling @ http://localhost:3000/_nuxt/commons.app.js:12167:26\n  at VueComponent.invoker @ http://localhost:3000/_nuxt/commons.app.js:12492:14\n  at invokeWithErrorHandling @ http://localhost:3000/_nuxt/commons.app.js:12167:26\n  at VueComponent.Vue.$emit @ http://localhost:3000/_nuxt/commons.app.js:14196:9\n  at VueComponent.cancelDraft @ http://localhost:3000/_nuxt/pages/profile-edit.js:828:12\n  at invokeWithErrorHandling @ http://localhost:3000/_nuxt/commons.app.js:12167:26\n  at HTMLButtonElement.invoker @ http://localhost:3000/_nuxt/commons.app.js:12492:14\n  at HTMLButtonElement.original._wrapper @ http://localhost:3000/_nuxt/commons.app.js:17221:25"
    }
}

Further Reading

Additional helpful documentation, links, and articles: