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
Setup with ROPM (recommended)
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:
| Field | Type | Required | Description |
|---|---|---|---|
| clientToken | String | Yes | The token used to upload data to Datadog. |
| applicationId | String | Yes | The application ID used in RUM events. |
| site | String | Yes | The site to send data to: "us1", "us3", "us5", "eu1", "ap1", "ap2", "uk1", or "staging". |
| env | String | Yes | The name of the environment to report in logs and RUM events. |
| sessionSampleRate | Integer | Yes | The percentage of sessions to keep and send to Datadog, as an integer between 0 and 100 (default is 100). |
| service | String | No | The name of the service to report in logs and RUM events (defaults to the channel's title from the manifest). |
| version | String | No | The version of the channel to report in logs and RUM events (defaults to the channel's version from the manifest). |
| traceSampleRate | Double | No | The rate of traces to keep when instrumenting network requests, as a number between 0 and 100 (default is 100). |
| tracingHeaderTypes | Array | No | An 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. |
| traceContextInjection | String | No | Whether the trace context should be injected into all requests or only sampled ones: "all" or "sampled" (default is "sampled"). |
| ignoredExitEvents | Array | No | An 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"]. |
| launchArgs | Object | No | The 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:
| Attribute | Type | Description |
|---|---|---|
| id | String | Unique user identifier. |
| name | String | User friendly name, displayed by default in the RUM UI. |
| String | User 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.