Datadog SDK for Roku

July 30, 2026 · View on GitHub

A client-side Roku library to interact with Datadog.

NOTE This library is still in early Developer Preview.

Getting Started

ROPM is a package manager for the Roku platform (based on NPM). If you're not already using ROPM in your Roku project, read their Getting started guide. Once your project is set up to use ROPM, you can use the following command to install the Datadog dependency:

ropm install datadog-roku

Setup manually

If your project does not use ROPM, install the library manually by downloading the Roku SDK zip archive, and unzipping it in your project's root folder.

Make sure you have a roku_modules/datadogroku subfolder in both the components and  source folders of your project.

Configure Datadog

To configure the Datadog SDK, copy the following code snippet into the RunUserInterface() method of your main.brs file. Make sure that you call datadogroku_initialize after the show method of your screen.

sub RunUserInterface(args as dynamic)

    screen = CreateObject("roSGScreen")
    scene = screen.CreateScene("MyScene")
    screen.show()

    ' Setup Datadog
    datadogroku_initialize({
        clientToken: "pub00000000000000000000000000000000", ' replace with your client token
        applicationId: "00000000-0000-0000-0000-000000000000", ' replace with your RUM application ID
        site: "us1", ' replace with the site you're targeting, see the table below
        env: "prod", ' replace with the environment you're targeting, for example: prod, staging, …
        sessionSampleRate: 100, ' the percentage (integer) of sessions to track
        launchArgs: args
    }, globalNode)

    ' complete your channel setup here
end sub

The configuration object passed to datadogroku_initialize supports the following fields:

FieldTypeRequiredDescription
clientTokenStringYesThe token used to upload data to Datadog.
applicationIdStringYesThe application ID used in RUM events.
siteStringYesThe site to send data to: "us1", "us3", "us5", "eu1", "ap1", "ap2", "uk1", or "staging".
envStringYesThe name of the environment to report in logs and RUM events.
sessionSampleRateIntegerYesThe percentage of sessions to keep and send to Datadog, as an integer between 0 and 100 (default is 100).
serviceStringNoThe name of the service to report in logs and RUM events (defaults to the channel's title from the manifest).
versionStringNoThe version of the channel to report in logs and RUM events (defaults to the channel's version from the manifest).
traceSampleRateDoubleNoThe rate of traces to keep when instrumenting network requests, as a number between 0 and 100 (default is 100).
tracingHeaderTypesArrayNoAn array of associative arrays, each with a host (string) and a header (a single tracing header type, or an array of them: "b3", "b3multi", "tracecontext", or "datadog"), used to enable distributed tracing for requests to matching hosts. See Track RUM Resources.
traceContextInjectionStringNoWhether the trace context should be injected into all requests or only sampled ones: "all" or "sampled" (default is "sampled").
ignoredExitEventsArrayNoAn array of exit status strings to ignore when detecting crashes. Any exit status not in this list is reported as a crash. Defaults to ["EXIT_UNKNOWN", "EXIT_POWER_MODE", "EXIT_IDLE_AUTO_EXIT", "EXIT_DIAL_DELETE", "EXIT_USER_KILL", "EXIT_USER_NAV"].
launchArgsObjectNoThe args object passed to your RunUserInterface() method.

Track RUM events manually

Track RUM Views

To split user sessions into logical steps, manually start a View using the following code. Every navigation to a new screen within your channel should correspond to a new RUM View.

    viewName = "VideoDetails"
    viewUrl = "components/screens/VideoDetails.xml"
    m.global.datadogRumAgent.callfunc("startView", viewName, viewUrl)

    ' optionally you can also attach a context with custom properties
    context = { myProperty: 42 }
    m.global.datadogRumAgent.callfunc("startView", viewName, viewUrl, context)

Track RUM Actions

RUM Actions represent the interactions your users have with your channel. You can forward actions to Datadog as follows:

    targetName = "playButton" ' the name of the SG Node the user interacted with
    actionType = "click" ' the type of interaction, should be one of "click", "back", or "custom" 
    customAction = { target: targetName, type: actionType}
    m.global.datadogRumAgent.callfunc("addAction", customAction)

    ' optionally you can also attach a context with custom properties
    context = { myProperty: 42 }
    m.global.datadogRumAgent.callfunc("addAction", customAction, context)

Track RUM Operations

RUM Operations let you track the lifecycle of a specific feature or user flow (for example, login, checkout, content load) as a named span, independent of Views. Start an operation, then report whether it succeeded or failed:

    m.global.datadogRumAgent.callfunc("startOperation", "login")

    ' ... perform the login flow ...

    m.global.datadogRumAgent.callfunc("succeedOperation", "login")
    ' or, if it failed:
    m.global.datadogRumAgent.callfunc("failOperation", "login", "error") ' failureReason: "error", "abandoned", or "other"

If several instances of the same operation can run concurrently, pass an operationKey to tell them apart, and optionally attach a context with custom properties:

    opKey = "content-42"
    context = { content_type: "movie" }
    m.global.datadogRumAgent.callfunc("startOperation", "contentLoad", opKey, context)

    ' ... later, on the matching operationKey ...
    m.global.datadogRumAgent.callfunc("succeedOperation", "contentLoad", opKey)

Track RUM Errors

Whenever you perform an operation that might throw an exception, you can forward the error to Datadog as follows:

    try
        doSomethingThatMightThrowAnException()
    catch error
        m.global.datadogRumAgent.callfunc("addError", error)
    end try

You can also track errors manually without using a try-catch block, as follows:

    customError = {
        type: "MyError",
        message: "An unexpected situation occurred"
    }
    m.global.datadogRumAgent.callfunc("addError", customError)

    ' optionally you can also attach a context with custom properties
    context = { myProperty: 42 }
    m.global.datadogRumAgent.callfunc("addError", customError, context)

Track RUM Resources

roUrlTransfer

Network requests made directly with a roUrlTransfer node must be tracked.

For synchronous requests, you can use our wrapper to track the resource automatically, by using our datadogroku_DdUrlTransfer wrapper, which supports most features of the roUrlTransfer component (except anything related to async network calls).

For example, here's how to do a GetToString call:

    ddUrlTransfer = datadogroku_DdUrlTransfer(m.global)
    ddUrlTransfer.SetUrl(url)
    ddUrlTransfer.EnablePeerVerification(false)
    ddUrlTransfer.EnableHostVerification(false)
    result = ddUrlTransfer.GetToString()

datadogroku_DdUrlTransfer only takes the global node. It uses the tracingHeaderTypes and traceSampleRate set on the SDK's configuration (see Configure Datadog) to decide which requests get distributed tracing headers injected, and at which rate. If you need to override those values for a specific instance, use its SetTracingHeaderTypes(tracingHeaderTypes as object) and SetTraceSampleRate(traceSampleRate as double) setters before making the request:

    ddUrlTransfer = datadogroku_DdUrlTransfer(m.global)
    ddUrlTransfer.SetTracingHeaderTypes([
        ' add tracing for requests to "example.com" URLs using W3C's tracecontext headers
        { host: "example.com", header: "tracecontext" }
        ' a host can be associated with several header types (like the Browser SDK's
        ' allowedTracingUrls). All of them are injected sharing the same trace/span id:
        { host: "api.example.com", header: ["datadog", "tracecontext"] }
    ])
    ddUrlTransfer.SetTraceSampleRate(50.0) ' only trace 50% of requests
    ddUrlTransfer.SetUrl(url)
    result = ddUrlTransfer.GetToString()

For asynchronous request, there's no automatic instrumentation yet, meaning you need to track the resource manually. The following code snippet shows how to report the request as a RUM Resource:

sub performRequest()

    m.port = CreateObject("roMessagePort")
    request = CreateObject("roUrlTransfer")
    ' setup the node url, headers, …

    timer = CreateObject("roTimespan")
    timer.Mark()
    request.AsyncGetToString()
    
    while (true)
        msg = wait(1000, m.port)
        if (msg <> invalid)
            msgType = type(msg)
            if (msgType = "roUrlEvent")
                if (msg.GetInt() = 1) ' transfer complete
                    durationMs& = timer.TotalMilliseconds()
                    transferTime# = datadogroku_millisToSec(durationMs&)
                    httpCode = msg.GetResponseCode()
                    status = "ok"
                    if (httpCode < 0)
                        status = msg.GetFailureReason()
                    end if
                    resource = {
                        url: requestUrl
                        method: "GET"
                        transferTime: transferTime#
                        httpCode: httpCode
                        status: status
                    }
                    m.global.datadogRumAgent.callfunc("addResource", resource)
                    
                    ' optionally you can also attach a context with custom properties
                    context = { myProperty: 42 }
                    m.global.datadogRumAgent.callfunc("addResource", resource, context)
                end if
            end if
        end if
    end while
end sub
Streaming resources

Whenever you use a Video or an Audio node to stream media, you can forward all roSystemLogEvent you receive to Datadog as follows:

    sysLog = CreateObject("roSystemLog")
    sysLog.setMessagePort(m.port)
    sysLog.enableType("http.error")
    sysLog.enableType("http.complete")

    while(true)
        msg = wait(0, m.port)
        if (type(msg) = "roSystemLogEvent")
            m.global.datadogRumAgent.callfunc("addResource", msg.getInfo())
        end if
    end while

Refresh session activity

If your channel already has a natural signal that proves the user is actively engaged — a video playback position tick, a GPS fix, a sensor reading — you can use that signal to keep the RUM session alive:

    m.global.datadogRumAgent.callfunc("reportUserActivity")

This is equivalent to a real user action for session-duration purposes. It refreshes the session's inactivity clock, renewing the session if it has already expired. When a view is active, it also emits a view update, extending the session and view duration in Datadog. Use this when users may not touch the remote for long periods while remaining actively engaged, such as when watching a movie.

You can safely call reportUserActivity from a high-frequency signal, such as a video position tick that fires once per second. Calls are throttled internally, so no more than one view update is emitted during each keepAliveDelayMs window, which defaults to 60000 ms (1 minute). Reports received within the same window are ignored, keeping RUM event volume bounded. To use finer or coarser granularity, configure the RUM agent's keepAliveDelayMs field.

Identifying your users

Adding user information to your RUM sessions makes it easy to:

  • Follow the journey of a given user.
  • Know which users are the most impacted by errors.
  • Monitor performance for your most important users.

The following attributes are optional, but you should provide at least one of them:

AttributeTypeDescription
idStringUnique user identifier.
nameStringUser friendly name, displayed by default in the RUM UI.
emailStringUser email, displayed in the RUM UI if the user name is not present. It is also used to fetch Gravatars.

To identify user sessions, use the datadogUserInfo global field, after initializing the SDK, for example:

    m.global.setField("datadogUserInfo", { id: 42, name: "Abcd Efg", email: "abcd.efg@example.com"})

Track custom global attributes

In addition to the default attributes captured by the SDK automatically, you can choose to add additional contextual information, such as custom attributes, to your Logs and RUM events to enrich your observability within Datadog. Custom attributes allow you to filter and group information about observed user behavior (for example by cart value, merchant tier, or ad campaign) with code-level information (such as backend services, session timeline, error logs, and network health).

    m.global.setField("datadogContext", { foo: "Some value", bar: 123})

Send logs

In addition to standard RUM events, you can send individual logs to track any event or state of your channel, with a log message, and optionally additional custom attributes.

  • logOk: Sends a log with status "ok".
  • logDebug: Sends a log with status "debug", for messages that contain information that is useful for debugging a program.
  • logInfo: Sends a log with status "info", for confirmation that the program is working as expected.
  • logNotice: Sends a log with status "notice", for conditions that are not error conditions, but that may require special handling.
  • logWarn: Sends a log with status "warning", for warning situations.
  • logError: Sends a log with status "error", for errors.
  • logCritical: Sends a log with status "critical", for situations when the application is in a critical state.
  • logAlert: Sends a log with status "alert", for conditions that should be corrected immediately, such as a corrupted system database.
  • logEmergency: Sends a log with status "emergency", for a panic condition.

The following code snippet illustrates the logInfo function, but all functions on the LogsAgent use the same signature:

    msg = "Switching screen to video details"
    attributes = { video_id : 42 }
    m.global.datadogLogsAgent.callfunc("logInfo", msg, attributes)

Troubleshooting

To see internal messages and warnings about how the SDK is behaving, enable the SDK verbosity to see debug information when you connect to your device with telnet on port 8085.

        m.global.addFields({
        datadogVerbosity: 3 ' 0 = none; 1 = error; 2 = warning; 3 = info; 4 = verbose; 5 = internals
    })

Looking up your RUM events

Navigate to the RUM Explorer. In the side bar, select your application and explore Sessions, Views, Actions, Errors, Resources, and Long Tasks.

Looking up your Logs

Navigate to the Log Explorer. In the side bar, select your application and explore Sessions, Views, Actions, Errors, Resources, and Long Tasks.

Contributing

Pull requests are welcome. First, open an issue to discuss what you would like to change. For more information, read the Contributing Guide.

License

Apache License, v2.0