diff --git a/DATAMODEL.md b/DATAMODEL.md new file mode 100644 index 0000000..646a2e2 --- /dev/null +++ b/DATAMODEL.md @@ -0,0 +1,290 @@ +# Introduction + +This document provides an overview of the Events and Attributes used for media monitoring in New Relic. + +# Glossary + +This section defines the key terms used in the context of New Relic Media monitoring: + +## Event Types + +- **RokuSystem**: Events related to system tracking. +- **videoAction**: Events triggered by general video interactions, such as starting, pausing, or seeking. +- **videoAdAction**: Events related to ad playback, such as starting, completing, or skipping an ad. +- **videoErrorAction**: Events triggered by errors encountered during video or ad playback. +- **videoCustomAction**: Custom events defined to capture specific actions or interactions beyond default event types. + +## Attribute + +An Attribute is a piece of data associated with an event. Attributes provide additional context or details about the event, such as the video’s title, duration, or playback position. + +- Most attributes are included with every event. +- Some attributes are specific to certain event types, such as ad-related data sent with ad events. + +## Event Type Reference + +### RokuSystem + +| Attribute Name | Description | +|---|---| +| instrumentation.provider | Always “media”. | +| instrumentation.name | Always “roku”. | +| instrumentation.version | Agent’s version. | +| newRelicAgent | Always “RokuAgent”. | +| newRelicVersion | Agent’s version. | +| sessionId | Session ID, a hash that is generated every time the Roku app starts. | +| hdmiIsConnected | Boolean. HDMI is connected or not. | +| hdmiHdcpVersion | HDCP version. | +| deviceUuid | Roku Device UUID. | +| deviceName | Roku device name. | +| deviceSize | Always "xLarge". | +| deviceGroup | Always “Roku”. | +| deviceManufacturer | Always “Roku”. | +| deviceModel | Roku model. | +| deviceType | Roku model type. | +| vendorName | Roku model vendor. | +| modelNumber | Roku model number. | +| vendorUsbName | USB vendor. | +| screenSize | Size of the Roku TV. | +| osName | Always “RokuOS”. | +| osVersion | Firmware version. | +| countryCode | Country from where the current user is connected. | +| timeZone | Current user’s timezone. | +| locale | Current user’s locale. | +| memoryLevel | Device memory level. | +| memLimitPercent | Usage percentage of memory limit for the channel. Only reported if the memory monitor is enabled. | +| channelAvailMem | Estimated kilobytes of memory available for the channel. Only reported if the memory monitor is enabled. | +| connectionType | Network connection type (WiFi, etc). | +| displayType | Type of display, screen, TV, etc. | +| contentRenditionName | Display mode. | +| displayAspectRatio | Aspect ratio. | +| videoMode | Video mode. | +| graphicsPlatform | Graphics platform (OpenGL, etc). | +| timeSinceLastKeyPress | Time since last keypress in the remote. Milliseconds. | +| appId | Application ID. | +| appVersion | Application version. | +| appName | Application name. | +| appDevId | Developer ID. | +| appBuild | Application build number. | +| timeSinceLoad | Time since NewRelic function call. Seconds. | +| uptime | Uptime of the system since the last reboot. Seconds. | +| httpCode | Response code. | +| method | HTTP method. | +| origUrl | Original URL of request. | +| domain | Host part of `origUrl`. | +| status | Current request status. | +| targetIp | Target IP address of request. | +| url | Actual URL of request. | +| bytesDownloaded | Number of bytes downloaded. | +| bytesUploaded | Number of bytes uploaded. | +| connectTime | Total connection time. | +| contentType | Mime type of response body. | +| dnsLookupTime | DNS lookup time. | +| downloadSpeed | Download speed. | +| firstByteTime | Time elapsed until the first bytes arrived. | +| transferTime | Total transfer time. | +| uploadSpeed | Upload speed. | +| bandwidth | Bandwidth. | +| lastExitOrTerminationReason | The reason for the last app exit / termination. | +| splashTime | The splash time in ms. | +| instantOnRunMode | Value of `instant_on_run_mode` property sent to Main. | +| launchSource | Value of `source` property sent to Main. | +| httpResult | Request final status. | +| http* | Multiple attributes. All the header keys. | +| transferIdentity | HTTP request identificator. | +| sceneName | Identifier of the scene. | +| timeSinceHttpRequest | Time since `HTTP_REQUEST` in milliseconds. | +| enduser.id | User ID. | + + + +#### List of possible RokuSystem Actions + +| Action Name | Description | +|---|---| +| BANDWIDTH_MINUTE | Report the bandwidth every minute. | +| HTTP_CONNECT | An HTTP request, generated by roSystemLog. | +| HTTP_COMPLETE | An HTTP response, generated by roSystemLog. | +| HTTP_ERROR | An HTTP error, generated by roSystemLog. | +| HTTP_REQUEST | An HTTP request. Generated by nrSendHttpRequest function. | +| HTTP_RESPONSE | An HTTP response. Generated by nrSendHttpResponse function. | +| APP_STARTED | The app did start. Generated by nrAppStarted function. | +| SCENE_LOADED | A scene did load. Generated by nrSceneLoaded function. | + +### VideoAction + +| Attribute Name | Definition | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| actionName | The specific action being performed in the video player, such as play, pause, resume, content buffering, etc. | +| appId | The ID of your application, as recorded by New Relic. | +| appName | The name of the application. | +| playerName | The name of the video player. | +| playerVersion | The version of the video player. | +| deviceType | The specific type of the device: iPhone 8, iPad Pro, etc. | +| deviceGroup | The category of the device, such as iPhone or Tablet. | +| deviceManufacturer | The manufacturer of the device, such as Motorola or HTC. | +| deviceModel | The model number of the device. | +| deviceName | The device's name. | +| deviceSize | The display size of the device: Small, normal, large, xlarge. | +| deviceUuid | A unique identifier assigned at the time of app installation by New Relic. | +| viewSession | Trackers will generate unique IDs for every new video session. This could be the session ID from the client. | +| viewId | Trackers will generate unique IDs for every new video iteration. | +| contentId | The ID of the video. | +| contentTitle | The title of the video. | +| contentBitrate | Bitrate (in bits) of the video. | +| contentIsFullscreen | Always "true". | +| contentRenditionName | Name of the rendition (e.g., 1080p). | +| contentDuration | Duration of the video, in ms. | +| contentPlayhead | Playhead (currentTime) of the video, in ms. | +| contentSrc | URL of the resource being played. | +| contentIsFullscreen | True if the video is currently fullscreen. | +| contentIsMuted | True if the video is currently muted. | +| totalAdPlaytime | Total time ad is played for this video session. | +| elapsedTime | Time that has passed since the last event. | +| bufferType | When buffer starts, i.e., initial, seek, pause & connection. | +| timestamp | The time (date, hour, minute, second) at which the interaction occurred. | +| instrumentation.provider | Player/agent name. | +| instrumentation.name | Name of the instrumentation collecting the data. | +| instrumentation.version | Agent’s version. | +| enduser.id | User ID. | + +#### List of possible Video Actions + +| Action Name | Definition | +| ------------------------ | ------------------------------------------------------------------------------------------------ | +| PLAYER_READY | The player is ready to start sending events. | +| CONTENT_REQUEST | Content video has been requested. | +| CONTENT_START | Content video started (first frame shown). | +| CONTENT_END | Content video ended. | +| CONTENT_PAUSE | Content video paused. | +| CONTENT_RESUME | Content video resumed. | +| CONTENT_BUFFER_START | Content video buffering started. | +| CONTENT_BUFFER_END | Content video buffering ended. | +| CONTENT_HEARTBEAT | Content video heartbeat, an event that happens once every 30 seconds while the video is playing. | + +### VideoAdAction + +| Attribute Name | Definition | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| actionName | The specific action being performed in the video player, such as play, pause, resume, content buffering, etc. | +| appId | The ID of your application, as recorded by New Relic. | +| appName | The name of the application. | +| playerName | The name of the video player. | +| playerVersion | The version of the video player. | +| deviceType | The specific type of the device: iPhone 8, iPad Pro, etc. | +| deviceGroup | The category of the device, such as iPhone or Tablet. | +| deviceManufacturer | The manufacturer of the device, such as Motorola or HTC. | +| deviceModel | The model number of the device. | +| viewSession | Trackers will generate unique IDs for every new video session. This could be the session ID from the client. | +| viewId | Trackers will generate unique IDs for every new video iteration. | +| adId | The ID of the video. | +| adTitle | The title of the video. | +| adDuration | Duration of the video, in ms. | +| adPlayhead | Playhead (currentTime) of the video, in ms. | +| adSrc | URL of the resource being played. | +| adIsMuted | True if the video is currently muted. | +| adQuartile | Quartile of the ad. 0 before first, 1 after first quartile, 2 after midpoint, 3 after third quartile, 4 when completed. | +| adPosition | The position of the ad. | +| adCreativeId | The creative ID of the ad. | +| adPartner | The ad partner, e.g., ima, freewheel. | +| timestamp | The time (date, hour, minute, second) at which the interaction occurred. | +| elapsedTime | Time that has passed since the last event. | +| instrumentation.provider | Player/agent name. | +| instrumentation.name | Name of the instrumentation collecting the data. | +| instrumentation.version | Agent’s version. | +| enduser.id | User ID. | + +#### List of possible Video Ad Actions + +| Action Name | Definition | +| ------------------- | ------------------------------------------------------------------------------------------- | +| AD_REQUEST | Ad video has been requested. | +| AD_START | Ad video started (first frame shown). | +| AD_END | Ad video ended. | +| AD_PAUSE | Ad video paused. | +| AD_RESUME | Ad video resumed. | +| AD_BREAK_START | Ad break (a block of ads) started. | +| AD_BREAK_END | Ad break ended. | +| AD_QUARTILE | Ad quartile happened. | + +### VideoErrorAction + +| Attribute Name | Definition | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| actionName | The specific action being performed in the video player, such as play, pause, resume, content buffering, etc. | +| appId | The ID of your application, as recorded by New Relic. | +| appName | The name of the application. | +| playerName | The name of the video player. | +| playerVersion | The version of the video player. | +| deviceType | The specific type of the device: iPhone 8, iPad Pro, etc. | +| deviceGroup | The category of the device, such as iPhone or Tablet. | +| deviceManufacturer | The manufacturer of the device, such as Motorola or HTC. | +| deviceModel | The model number of the device. | +| viewSession | Trackers will generate unique IDs for every new video session. This could be the session ID from the client. | +| viewId | Trackers will generate unique IDs for every new video iteration. | +| contentId | The ID of the video. | +| contentTitle | The title of the video. | +| errorName | Name of the error. | +| errorCode | Error code if it's known. | +| backTrace | Stack trace of the error. | +| contentSrc | Content source URL. | +| elapsedTime | Time that has passed since the last event. | +| timestamp | The time (date, hour, minute, second) at which the interaction occurred. | +| instrumentation.provider | Player/agent name. | +| instrumentation.name | Name of the instrumentation collecting the data. | +| instrumentation.version | Agent’s version. | +| enduser.id | User ID. | + +#### List of possible Video Error Actions + +| Action Name | Definition | +| ------------- | -------------------- | +| AD_ERROR | Ad video error. | +| HTTP_ERROR | An HTTP error. | +| CONTENT_ERROR | Content video error. | + +### VideoCustomAction + +| Attribute Name | Definition | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| actionName | The name of the PageAction, as defined by client in their code. | +| appId | The ID of your application, as recorded by New Relic. | +| appName | The name of the application. | +| playerName | The name of the video player. | +| playerVersion | The version of the video player. | +| deviceType | The specific type of the device: iPhone 8, iPad Pro, etc. | +| deviceGroup | The category of the device, such as iPhone or Tablet. | +| deviceManufacturer | The manufacturer of the device, such as Motorola or HTC. | +| deviceModel | The model number of the device. | +| viewSession | Trackers will generate unique IDs for every new video session. This could be the session ID from the client. | +| viewId | Trackers will generate unique IDs for every new video iteration. | +| contentId | The ID of the video. | +| contentTitle | The title of the video. | +| timestamp | The time (date, hour, minute, second) at which the interaction occurred. | +| instrumentation.provider | Player/agent name. | +| instrumentation.name | Name of the instrumentation collecting the data. | +| instrumentation.version | Agent’s version. | +| enduser.id | User ID. | + +#### The following attributes are also a part of VideoAction + +| Attribute Name | Definition | +| ------------- | -------------------- | +| contentMeasuredBitrate | Video Measured Bitrate. | +| contentSegmentBitrate | In case of segmented video sources (HLS, DASH), the current segment’s bitrate. | +| sessionDuration | Time since the session started.. | +| trackerName | Always “rokutracker”. | +| trackerVersion | Agent version. | +| numberOfVideos | Number of videos played. | +| numberOfErrors | Number of errors happened. | +| timeSinceLastHeartbeat | Time since last heartbeat, in milliseconds. | +| timeSinceRequested | Time since the video requested, in milliseconds. | +| timeSinceStarted | Time since the video started, in milliseconds. | +| timeSinceTrackerReady | Time since `PLAYER_READY`, in milliseconds. | +| totalPlaytime | Total time the user spend seeing the video. | +| playtimeSinceLastEvent | Total time the user spend seeing the video since last video event. | +| timeToStartStreaming | The time in milliseconds from playback being started until the video actually began playing. | +| isPlaylist | Content is a playlist. Boolean. | +| videoFormat | Video format, a mime type. | +| timeSinceBufferBegin | Time since video last video buffering began, in milliseconds. | +| timeSincePaused | Time since the video was paused, in milliseconds. | \ No newline at end of file diff --git a/README.md b/README.md index ff95218..1a8b5d6 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,16 @@ The New Relic Roku Agent tracks the behavior of a Roku App. It contains two parts, one to monitor general system level events and one to monitor video related events, for apps that use a video player. -Internally, it uses the Event API to send events using the REST interface. It sends two types of events: RokuSystem for system events and RokuVideo for video events. After the agent has sent some data it will be accessible in NR One Dashboards with a simple NRQL request like: +Internally, it uses the Event API to send events using the REST interface. It sends five types of events: +- RokuSystem for system events +- VideoAction for video events. +- VideoErrorAction for errors. +- VideoAdAction for ads. +- VideoCustomAction for custom actions. +After the agent has sent some data it will be accessible in NR One Dashboards with a simple NRQL request like: ``` -SELECT * FROM RokuSystem, RokuVideo +SELECT * FROM RokuSystem, VideoAction, VideoErrorAction, VideoAdAction, VideoCustomAction ``` Will result in something like the following: @@ -18,10 +24,7 @@ Will result in something like the following: * [Installation](#installation) * [Usage](#usage) * [Agent API](#api) - * [Data Model](#data-model) -   * [Roku System](#roku-system) -   * [Roku Video](#roku-video) -   * [Metrics](#metrics) + * [Data Model](#data-model) * [Ad Tracking](#ad-track) * [Testing](#testing) * [Open Source License](#open-source) @@ -416,7 +419,7 @@ Example: nrSendVideoEvent(nr as Object, actionName as String, attr = invalid) as Void Description: - Send a video event, type RokuVideo. + Send a video event, type VideoAction. Arguments: nr: New Relic Agent object. @@ -801,215 +804,27 @@ Example: nrSendSummaryMetric(m.nr, "test", 2000, 5, 1000, 100, 200) ``` - +**setUserId** -### Data Model +``` +setUserId(userId as String) as Void + +Description: + Sets userId. + +Arguments: + userId: attribute + +Return: + Nothing. + +Example: + setUserId("TEST_USER") +``` -The agent generates two different event types: `RokuSystem` and `RokuVideo`. - - - -#### 1. RokuSystem - -This event groups all actions related to system tracking. - -#### 1.1 Actions - -| Action Name | Description | -|---|---| -| `BANDWIDTH_MINUTE` | Report the bandwidth every minute. | -| `HTTP_CONNECT` | An HTTP request, generated by roSystemLog. | -| `HTTP_COMPLETE` | An HTTP response, generated by roSystemLog. | -| `HTTP_ERROR` | An HTTP error, generated by roSystemLog. | -| `HTTP_REQUEST` | An HTTP request. Generated by nrSendHttpRequest function. | -| `HTTP_RESPONSE` | An HTTP response. Generated by nrSendHttpResponse function. | -| `APP_STARTED` | The app did start. Generated by nrAppStarted function. | -| `SCENE_LOADED` | A scene did load. Generated by nrSceneLoaded function. | - -#### 1.2 Attributes - -There is a set of attributes common to all actions sent over a `RokuSystem` and others are specific to a certain action. - -#### 1.2.1 Common Attributes - -| Attribute Name | Description | -|---|---| -| `instrumentation.provider` | Always “media”. | -| `instrumentation.name` | Always “roku”. | -| `instrumentation.version` | Agent’s version. | -| `newRelicAgent` | Always “RokuAgent”. | -| `newRelicVersion` | Agent’s version. | -| `sessionId` | Session ID, a hash that is generated every time the Roku app starts. | -| `hdmiIsConnected` | Boolean. HDMI is connected or not. | -| `hdmiHdcpVersion` | HDCP version. | -| `uuid` | Roku Device UUID. | -| `device` | Roku device name. | -| `deviceGroup` | Always “Roku”. | -| `deviceManufacturer` | Always “Roku”. | -| `deviceModel` | Roku model. | -| `deviceType` | Roku model type. | -| `vendorName` | Roku model vendor. | -| `modelNumber` | Roku model number. | -| `vendorUsbName` | USB vendor. | -| `screenSize` | Size of the Roku TV. | -| `osName` | Always “RokuOS”. | -| `osVersion` | Firmware version. | -| `countryCode` | Country from where the current user is connected. | -| `timeZone` | Current user’s timezone. | -| `locale` | Current user’s locale. | -| `memoryLevel` | Device memory level. | -| `memLimitPercent` | Usage percentage of memory limit for the channel. Only reported if the memory monitor is enabled. | -| `channelAvailMem` | Estimated kilobytes of memory available for the channel. Only reported if the memory monitor is enabled. | -| `connectionType` | Network connection type (WiFi, etc). | -| `displayType` | Type of display, screen, TV, etc. | -| `displayMode` | Display mode. | -| `displayAspectRatio` | Aspect ratio. | -| `videoMode` | Video mode. | -| `graphicsPlatform` | Graphics platform (OpenGL, etc). | -| `timeSinceLastKeyPress` | Time since last keypress in the remote. Milliseconds. | -| `appId` | Application ID. | -| `appVersion` | Application version. | -| `appName` | Application name. | -| `appDevId` | Developer ID. | -| `appBuild` | Application build number. | -| `timeSinceLoad` | Time since NewRelic function call. Seconds. | -| `uptime` | Uptime of the system since the last reboot. Seconds. | - -#### 1.2.2 Action Specific Attributes - -| Attribute Name | Description | Actions | -|---|---|---| -| `httpCode` | Response code. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR`, `HTTP_RESPONSE` | -| `method` | HTTP method. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR`, `HTTP_REQUEST ` | -| `origUrl` | Original URL of request. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR`, `HTTP_REQUEST`, `HTTP_RESPONSE` | -| `domain` | Host part of `origUrl`. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR`, `HTTP_REQUEST`, `HTTP_RESPONSE` | -| `status` | Current request status. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR` | -| `targetIp` | Target IP address of request. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR` | -| `url` | Actual URL of request. | `HTTP_COMPLETE`, `HTTP_CONNECT`, `HTTP_ERROR` | -| `bytesDownloaded` | Number of bytes downloaded. | `HTTP_COMPLETE` | -| `bytesUploaded` | Number of bytes uploaded. | `HTTP_COMPLETE` | -| `connectTime` | Total connection time. | `HTTP_COMPLETE` | -| `contentType` | Mime type of response body. | `HTTP_COMPLETE` | -| `dnsLookupTime` | DNS lookup time. | `HTTP_COMPLETE` | -| `downloadSpeed` | Download speed. | `HTTP_COMPLETE` | -| `firstByteTime` | Time elapsed until the first bytes arrived. | `HTTP_COMPLETE` | -| `transferTime` | Total transfer time. | `HTTP_COMPLETE` | -| `uploadSpeed` | Upload speed. | `HTTP_COMPLETE` | -| `bandwidth` | Bandwidth. | `BANDWIDTH_MINUTE` | -| `lastExitOrTerminationReason` | The reason for the last app exit / termination. | `APP_STARTED` | -| `splashTime` | The splash time in ms. | `APP_STARTED` | -| `instantOnRunMode` | Value of `instant_on_run_mode` property sent to Main. | `APP_STARTED` | -| `launchSource ` | Value of `source` property sent to Main. | `APP_STARTED` | -| `httpResult` | Request final status. | `HTTP_RESPONSE` | -| `http*` | Multiple attributes. All the header keys. | `HTTP_RESPONSE` | -| `transferIdentity` | HTTP request identificator. | `HTTP_REQUEST`, `HTTP_RESPONSE` | -| `sceneName` | Identifier of the scene. | `SCENE_LOADED` | -| `timeSinceHttpRequest` | Time since `HTTP_REQUEST` in milliseconds. | `HTTP_RESPONSE` | - - - -#### 2. RokuVideo - -This event groups all actions related to video tracking. - -#### 2.1 Actions - -| Action Name | Description | -|---|---| -| `PLAYER_READY` | Player is ready to start working. It happens when the video agent is started. | -| `CONTENT_REQUEST` | “Play” button pressed or autoplay activated. | -| `CONTENT_START` | Video just started playing. | -| `CONTENT_END` | Video ended playing. | -| `CONTENT_PAUSE` | Video paused. | -| `CONTENT_RESUME` | Video resumed. | -| `CONTENT_BUFFER_START` | Video started buffering. | -| `CONTENT_BUFFER_END` | Video ended buffering. | -| `CONTENT_ERROR` | Video error happened. | -| `CONTENT_HEARTBEAT` | Sent every 30 seconds between video start and video end. | -| `LICENSE_STATUS` | Video has received a DRM license response | - -#### 2.2 Attributes - -There is a set of attributes common to all actions sent over a `RokuVideo` and others are specific to a certain action. - -#### 2.2.1 Common Attributes - -For video events, the common attributes include all `RokuSystem` common attributes (1.2.1) plus the video event ones. Here we will describe only the video common attributes. - -| Attribute Name | Description | -|---|---| -| `contentDuration` | Total video duration in milliseconds. | -| `contentPlayhead` | Current video position in milliseconds. | -| `contentIsMuted` | Video is muted or not. | -| `contentSrc` | Video URL. | -| `contentId` | Content ID, a CRC32 of contentSrc. | -| `contentBitrate` | Video manifest bitrate. | -| `contentMeasuredBitrate` | Video measured bitrate. | -| `contentSegmentBitrate` | In case of segmented video sources (HLS, DASH), the current segment’s bitrate. | -| `playerName` | Always “RokuVideoPlayer”. | -| `playerVersion` | Current firmware version. | -| `sessionDuration` | Time since the session started. | -| `viewId` | sessionId + “-“ + video counter. | -| `viewSession` | Copy of sessionId. | -| `trackerName` | Always “rokutracker”. | -| `trackerVersion` | Agent version. | -| `numberOfVideos` | Number of videos played. | -| `numberOfErrors` | Number of errors happened. | -| `timeSinceLastHeartbeat` | Time since last heartbeat, in milliseconds. | -| `timeSinceRequested` | Time since the video requested, in milliseconds. | -| `timeSinceStarted` | Time since the video started, in milliseconds. | -| `timeSinceTrackerReady` | Time since `PLAYER_READY`, in milliseconds. | -| `totalPlaytime` | Total time the user spend seeing the video. | -| `playtimeSinceLastEvent` | Total time the user spend seeing the video since last video event. | -| `timeToStartStreaming` | The time in milliseconds from playback being started until the video actually began playing. | -| `isPlaylist` | Content is a playlist. Boolean. | -| `videoFormat` | Video format, a mime type. | - -#### 2.2.2 Action Specific Attributes - -| Attribute Name | Description | Actions | -|---|---|---| -| `timeSinceBufferBegin` | Time since video last video buffering began, in milliseconds. | `CONTENT_BUFFER_END` | -| `timeSincePaused` | Time since the video was paused, in milliseconds. | `CONTENT_RESUME` | -| `errorMessage` | Descriptive error message. | `CONTENT_ERROR` | -| `errorCode` | Numeric error code. | `CONTENT_ERROR` | -| `errorStr` | Detailed error message. | `CONTENT_ERROR` | -| `errorClipId` | Property `clip_id` from Video object errorInfo. | `CONTENT_ERROR` | -| `errorIgnored` | Property `ignored` from Video object errorInfo. | `CONTENT_ERROR` | -| `errorSource` | Property `source` from Video object errorInfo. | `CONTENT_ERROR` | -| `errorCategory` | Property `category` from Video object errorInfo. | `CONTENT_ERROR` | -| `errorInfoCode` | Property `error_code` from Video object errorInfo. | `CONTENT_ERROR` | -| `errorDebugMsg` | Property `dbgmsg` from Video object errorInfo. | `CONTENT_ERROR` | -| `errorAttributes` | Property `error_attributes` from Video object errorInfo. | `CONTENT_ERROR` | -| `licenseStatusDuration` | Property `duration` from Video object licenseStatus. | `CONTENT_ERROR`, `LICENSE_STATUS` | -| `licenseStatusKeySystem` | Property `keySystem` from Video object licenseStatus. | `CONTENT_ERROR`, `LICENSE_STATUS` | -| `licenseStatusResponse` | Property `response` from Video object licenseStatus. | `CONTENT_ERROR`, `LICENSE_STATUS` | -| `licenseStatusStatus` | Property `status` from Video object licenseStatus. | `CONTENT_ERROR`, `LICENSE_STATUS` | -| `isInitialBuffering` | Is the initial buffering event, and not a rebuffering. In playlists it only happens at the beginning, and not on every video. | `CONTENT_BUFFER_*` | - - -#### 3. Metrics - -The Roku agent generates the following metrics OOTB: - -| Metric Name | Associated event | Metric type | Description | -|---|---|---|---| -| `roku.http.connect.count` | `HTTP_CONNECT` | count | Number of `HTTP_CONNECT` events generated in a period of time. | -| `roku.http.complete.count` | `HTTP_COMPLETE` | count | Number of `HTTP_COMPLETE` events generated in a period of time. | -| `roku.http.complete.connectTime` | `HTTP_COMPLETE` | gauge | Time taken to connect to the server in seconds. | -| `roku.http.complete.dnsTime` | `HTTP_COMPLETE` | gauge | DNS name resolution time in seconds. | -| `roku.http.complete.downSpeed` | `HTTP_COMPLETE` | gauge | Transfer download speed in bytes per second. | -| `roku.http.complete.firstByteTime` | `HTTP_COMPLETE` | gauge | Time taken to receive the first byte from the server in seconds. | -| `roku.http.complete.upSpeed` | `HTTP_COMPLETE` | gauge | Transfer upload speed in bytes per second. | -| `roku.http.error.count` | `HTTP_ERROR` | count | Number of `HTTP_ERROR` events generated in a period of time. | -| `roku.http.request.count` | `HTTP_REQUEST` | count | Number of `HTTP_REQUEST` events generated in a period of time. | -| `roku.http.response.count` | `HTTP_RESPONSE` | count | Number of `HTTP_RESPONSE` events generated in a period of time. | -| `roku.http.response.error.count` | `HTTP_RESPONSE` | count | Number of `HTTP_REQUEST` events generated in a period of time, that returned an error. | -| `roku.http.response.time` | `HTTP_RESPONSE` | gauge | Time since request in milliseconds. | - -Each metric is associated with an event generated by the agent. - -All metrics include the attribute `domain`, that is the host of the HTTP request. + +### Data Model +To understand which actions and attributes are captured and emitted by the Dash Player under different event types, see [DataModel.md](./DATAMODEL.md). ### Ad Tracking @@ -1114,49 +929,6 @@ Where `m.top.tracker` is the tracker object passed to the task. For a complete usage example, checkout files `VideoScene.brs` (function `setupVideoWithIMA()`) and `imasdk.brs` in the present repo. -#### Data Model - -**Actions** - -| Action Name | Description | -|---|---| -| `AD_BREAK_START` | An Ad Break has started. | -| `AD_BREAK_END` | An Ad Break has ended. | -| `AD_REQUEST` | Ad requested. | -| `AD_START` | Ad started. | -| `AD_PAUSE` | Ad paused. | -| `AD_RESUME` | Ad resumed. | -| `AD_END` | Ad ended. | -| `AD_QUARTILE` | Ad quartile happened. There are 3 quartile events during the ad playback. First happens after 25% of the ad is played. Second after 50% and third after 75%. | -| `AD_SKIP` | Ad skipped. | -| `AD_ERROR` | An error happened. | - -**Attributes** - -| Attribute Name | Description | Actions | -|---|---|---| -| `timeSinceAdBreakBegin` | Time since `AD_BREAK_START` happened, in milliseconds. | `AD_BREAK_END` | -| `timeSinceAdStarted` | Time since `AD_START` happened, in milliseconds. | All `AD_` events. | -| `timeSinceAdRequested` | Time since `AD_REQUEST` happened, in milliseconds. | All `AD_` events. | -| `timeSinceAdPaused` | Time since `AD_PAUSE` happened, in milliseconds. | `AD_RESUME` | -| `adTitle` | Title of current ad. | All `AD_` events. | -| `adId` | ID of current Ad. | All `AD_` events. | -| `adSystem` | Ad System of current Ad. | All `AD_` events. | -| `adDuration` | Ad duration in milliseconds. | All `AD_` events. | -| `adPosition` | Ad break position, "pre", "mid", "post" or "live". | All `AD_` events. | -| `adSrc` | Stream source of current Ad. | All `AD_` events. | -| `adCreativeId` | Creative ID of current Ad. | All `AD_` events. | -| `adPartner` | Partner of current Ad. | All `AD_` events. | -| `numberOfAds` | Number of started ads. | All `AD_` events. | -| `adQuartile` | Quartile number: 1, 2 or 3. | `AD_QUARTILE`. | -| `adErrorType` | Error type. | `AD_ERROR`. | -| `adErrorCode` | Error code. | `AD_ERROR`. | -| `adErrorMsg` | Error message. | `AD_ERROR`. | - -Not all events and attributes are supported in all Ad trackers. - - - ### Testing To run the unit tests, first copy the file `UnitTestFramework.brs` from [unit-testing-framework](https://github.com/rokudev/unit-testing-framework) to `source/testFramework/`. Then install the demo channel provided in the present repo and from a terminal run: diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index e6a172b..f23457d 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -17,6 +17,7 @@ sub init() print " New Relic Agent for Roku v" + m.nrAgentVersion print " Copyright 2019-2023 New Relic Inc. All Rights Reserved." print "************************************************************" + InitializeLastEventTimestamps() end sub '==========================' @@ -243,15 +244,15 @@ function nrAppStarted(aa as Object) as Void "instantOnRunMode": aa["instant_on_run_mode"], "launchSource": aa["source"] } - nrSendCustomEvent("RokuSystem", "APP_STARTED", attr) + nrSendSystemEvent("RokuSystem", "APP_STARTED", attr) end function function nrSceneLoaded(sceneName as String) as Void - nrSendCustomEvent("RokuSystem", "SCENE_LOADED", {"sceneName": sceneName}) + nrSendSystemEvent("RokuSystem", "SCENE_LOADED", {"sceneName": sceneName}) end function -function nrSendCustomEvent(eventType as String, actionName as String, attr = invalid as Object) as Void - nrLog("nrSendCustomEvent") +function nrSendSystemEvent(eventType as String, actionName as String, attr = invalid as Object) as Void + nrLog("nrSendSystemEvent") ev = nrCreateEvent(eventType, actionName) ev = nrAddCustomAttributes(ev) if attr <> invalid @@ -260,12 +261,8 @@ function nrSendCustomEvent(eventType as String, actionName as String, attr = inv nrRecordEvent(ev) end function -function nrSendSystemEvent(actionName as String, attr = invalid) as Void - nrSendCustomEvent("RokuSystem", actionName, attr) -end function - function nrSendVideoEvent(actionName as String, attr = invalid) as Void - ev = nrCreateEvent("RokuVideo", actionName) + ev = nrCreateEvent("VideoAction", actionName) ev = nrAddVideoAttributes(ev) ev = nrAddCustomAttributes(ev) if type(attr) = "roAssociativeArray" @@ -280,6 +277,41 @@ function nrSendVideoEvent(actionName as String, attr = invalid) as Void end if end function +function nrSendErrorEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void + ev = nrCreateEvent("VideoErrorAction", actionName) + ev = nrAddVideoAttributes(ev) + ev = nrAddCustomAttributes(ev) + if type(attr) = "roAssociativeArray" + ev.Append(attr) + end if + nrRecordEvent(ev) +end function + +function nrSendCustomEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void + ev = nrCreateEvent("VideoCustomAction", actionName) + ' Check if ctx contains attributes other than adpartner with value "raf" + hasOtherAdAttributes = false + if ctx <> invalid and type(ctx) = "roAssociativeArray" + for each key in ctx + if key <> "adpartner" or (key = "adpartner" and ctx[key] <> "raf") + hasOtherAdAttributes = true + exit for + end if + end for + end if + ' Add RAF attributes only if there are other attributes present + if hasOtherAdAttributes + ev = nrAddRAFAttributes(ev, ctx) + else + ev = nrAddVideoAttributes(ev) + end if + ev = nrAddCustomAttributes(ev) + if type(attr) = "roAssociativeArray" + ev.Append(attr) + end if + nrRecordEvent(ev) +end function + function nrSendHttpRequest(attr as Object) as Void domain = nrExtractDomainFromUrl(attr["origUrl"]) attr["domain"] = domain @@ -305,7 +337,7 @@ function nrSendHttpRequest(attr as Object) as Void m.num_http_request_counters.AddReplace(domain, 1) end if - nrSendCustomEvent("RokuSystem", "HTTP_REQUEST", attr) + nrSendSystemEvent("RokuSystem", "HTTP_REQUEST", attr) end function function nrSendHttpResponse(attr as Object) as Void @@ -337,7 +369,7 @@ function nrSendHttpResponse(attr as Object) as Void end if end if - nrSendCustomEvent("RokuSystem", "HTTP_RESPONSE", attr) + nrSendSystemEvent("RokuSystem", "HTTP_RESPONSE", attr) end function function nrEnableHttpEvents() as Void @@ -459,9 +491,9 @@ function nrTrackRAF(evtType = invalid as Dynamic, ctx = invalid as Dynamic) as V else if evtType = "Error" attr = {} if ctx.errType <> invalid then attr.AddReplace("adErrorType", ctx.errType) - if ctx.errCode <> invalid then attr.AddReplace("adErrorCode", ctx.errCode) - if ctx.errMsg <> invalid then attr.AddReplace("adErrorMsg", ctx.errMsg) - nrSendRAFEvent("AD_ERROR", ctx, attr) + if ctx.errCode <> invalid then attr.AddReplace("errorCode", ctx.errCode) + if ctx.errMsg <> invalid then attr.AddReplace("errorName", ctx.errMsg) + nrSendErrorEvent("AD_ERROR", ctx, attr) end if else if ctx <> invalid and ctx.time <> invalid and ctx.duration <> invalid 'Time progress event @@ -723,8 +755,9 @@ function nrAddBaseAttributes(ev as Object) as Object ev.AddReplace("hdmiIsConnected", hdmi.IsConnected()) ev.AddReplace("hdmiHdcpVersion", hdmi.GetHdcpVersion()) dev = CreateObject("roDeviceInfo") - ev.AddReplace("uuid", dev.GetChannelClientId()) 'GetDeviceUniqueId is deprecated, so we use GetChannelClientId - ev.AddReplace("device", dev.GetModelDisplayName()) + ev.AddReplace("deviceUuid", dev.GetChannelClientId()) 'GetDeviceUniqueId is deprecated, so we use GetChannelClientId + ev.AddReplace("deviceSize", "xLarge") + ev.AddReplace("deviceName", dev.GetModelDisplayName()) ev.AddReplace("deviceGroup", "Roku") ev.AddReplace("deviceManufacturer", "Roku") ev.AddReplace("deviceModel", dev.GetModel()) @@ -745,7 +778,7 @@ function nrAddBaseAttributes(ev as Object) as Object ev.AddReplace("connectionType", dev.GetConnectionType()) 'ev.AddReplace("ipAddress", dev.GetExternalIp()) ev.AddReplace("displayType", dev.GetDisplayType()) - ev.AddReplace("displayMode", dev.GetDisplayMode()) + ev.AddReplace("contentRenditionName", dev.GetDisplayMode()) ev.AddReplace("displayAspectRatio", dev.GetDisplayAspectRatio()) ev.AddReplace("videoMode", dev.GetVideoMode()) ev.AddReplace("graphicsPlatform", dev.GetGraphicsPlatform()) @@ -784,6 +817,9 @@ function nrAddCustomAttributes(ev as Object) as Object actionName = ev["actionName"] actionCustomAttr = m.nrCustomAttributes[actionName] if actionCustomAttr <> invalid then ev.Append(actionCustomAttr) + ' Calculate and add elapsed time for the action + elapsedTime = nrCalculateElapsedTime(actionName) + ev.AddReplace("elapsedTime", elapsedTime) return ev end function @@ -800,6 +836,37 @@ function nrAddCommonHTTPAttr(info as Object) as Object return attr end function +function nrCalculateElapsedTime(actionName as String) as Integer + currentTime = CreateObject("roDateTime") + currentTimestamp = currentTime.AsSeconds() + if m.lastEventTimestamps[actionName] <> invalid + timeSinceLastEvent = (currentTimestamp - m.lastEventTimestamps[actionName]) * 1000 + elapsedTime = timeSinceLastEvent + else + elapsedTime=0 + end if + m.lastEventTimestamps[actionName] = currentTimestamp + return elapsedTime +end function + +function nrCalculateBufferType(actionName as String) as String + bufferType = "connection" ' Default buffer type + if m.nrTimeSinceStarted = 0 + m.nrIsInitialBuffering = true + bufferType = "initial" + else + m.nrIsInitialBuffering = false + if m.nrLastVideoState = "paused" + bufferType = "pause" + else if m.nrLastVideoState = "seeking" + bufferType = "seek" + else + bufferType = "connection" + end if + end if + return bufferType +end function + function nrSendHTTPError(info as Object) as Void attr = nrAddCommonHTTPAttr(info) domain = attr["domain"] @@ -811,8 +878,7 @@ function nrSendHTTPError(info as Object) as Void else m.num_http_error_counters.AddReplace(domain, 1) end if - - nrSendCustomEvent("RokuSystem", "HTTP_ERROR", attr) + nrSendErrorEvent("HTTP_ERROR", attr) end function function nrSendHTTPConnect(info as Object) as Void @@ -827,7 +893,7 @@ function nrSendHTTPConnect(info as Object) as Void m.num_http_connect_counters.AddReplace(domain, 1) end if - if m.http_events_enabled then nrSendCustomEvent("RokuSystem", "HTTP_CONNECT", attr) + if m.http_events_enabled then nrSendSystemEvent("RokuSystem", "HTTP_CONNECT", attr) end function function nrSendHTTPComplete(info as Object) as Void @@ -854,7 +920,7 @@ function nrSendHTTPComplete(info as Object) as Void m.num_http_complete_counters.AddReplace(domain, 1) end if - if m.http_events_enabled then nrSendCustomEvent("RokuSystem", "HTTP_COMPLETE", attr) + if m.http_events_enabled then nrSendSystemEvent("RokuSystem", "HTTP_COMPLETE", attr) domain = nrExtractDomainFromUrl(attr["origUrl"]) nrSendMetric("roku.http.complete.connectTime", attr["connectTime"], {"domain": domain}) @@ -868,7 +934,7 @@ function nrSendBandwidth(info as Object) as Void attr = { "bandwidth": info["bandwidth"] } - nrSendCustomEvent("RokuSystem", "BANDWIDTH_MINUTE", attr) + nrSendSystemEvent("RokuSystem", "BANDWIDTH_MINUTE", attr) end function 'TODO: Testing endpoint. If nrRegion is not US or EU, use it as endpoint. Deprecate the "TEST" region and "m.testServer". @@ -911,6 +977,30 @@ end function ' Video functions ' '=================' +sub InitializeLastEventTimestamps() + m.lastEventTimestamps = { + "CONTENT_REQUEST": invalid, + "CONTENT_START": invalid, + "CONTENT_END": invalid, + "CONTENT_PAUSE": invalid, + "CONTENT_RESUME": invalid, + "CONTENT_BUFFER_START": invalid, + "CONTENT_BUFFER_END": invalid, + "HTTP_ERROR": invalid, + "CONTENT_ERROR": invalid, + "AD_ERROR": invalid, + "AD_BREAK_START": invalid, + "AD_BREAK_END": invalid, + "AD_REQUEST": invalid, + "AD_START": invalid, + "AD_END": invalid, + "AD_PAUSE": invalid, + "AD_RESUME": invalid, + "AD_QUARTILE": invalid, + "AD_SKIP": invalid + } +end sub + function nrSendPlayerReady() as Void m.nrTimeSinceTrackerReady = m.nrTimer.TotalMilliseconds() nrSendVideoEvent("PLAYER_READY") @@ -957,7 +1047,7 @@ function nrSendBufferStart() as Void else m.nrIsInitialBuffering = false end if - nrSendVideoEvent("CONTENT_BUFFER_START", {"isInitialBuffering": m.nrIsInitialBuffering}) + nrSendVideoEvent("CONTENT_BUFFER_START", {"isInitialBuffering": m.nrIsInitialBuffering, "bufferType": nrCalculateBufferType("CONTENT_BUFFER_START")}) nrPausePlaytime() m.nrPlaytimeSinceLastEvent = invalid end function @@ -968,14 +1058,14 @@ function nrSendBufferEnd() as Void else m.nrIsInitialBuffering = false end if - nrSendVideoEvent("CONTENT_BUFFER_END", {"isInitialBuffering": m.nrIsInitialBuffering}) + nrSendVideoEvent("CONTENT_BUFFER_END", {"isInitialBuffering": m.nrIsInitialBuffering, "bufferType": nrCalculateBufferType("CONTENT_BUFFER_END")}) nrResumePlaytime() m.nrPlaytimeSinceLastEvent = CreateObject("roTimespan") end function function nrSendError(video as Object) as Void attr = { - "errorMessage": video.errorMsg, + "errorName": video.errorMsg, "errorCode": video.errorCode } if video.errorStr <> invalid @@ -987,7 +1077,7 @@ function nrSendError(video as Object) as Void attr.append({ "errorClipId": video.errorInfo.clipid, "errorIgnored": video.errorInfo.ignored, - "errorSource": video.errorInfo.source, + "backtrace": video.errorInfo.source, "errorCategory": video.errorInfo.category, "errorInfoCode": video.errorInfo.errcode, "errorDrmInfoCode": video.errorInfo.drmerrcode, @@ -999,7 +1089,7 @@ function nrSendError(video as Object) as Void attr.append(getLicenseStatusAttributes(video.licenseStatus)) end if - nrSendVideoEvent("CONTENT_ERROR", attr) + nrSendErrorEvent("CONTENT_ERROR", attr) end function function nrSendBackupVideoEvent(actionName as String, attr = invalid) as Void @@ -1062,6 +1152,7 @@ function nrAddVideoAttributes(ev as Object) as Object ev.AddReplace("contentDuration", m.nrVideoObject.duration * 1000) ev.AddReplace("contentPlayhead", m.nrVideoObject.position * 1000) ev.AddReplace("contentIsMuted", m.nrVideoObject.mute) + ev.AddReplace("contentIsFullscreen","true") streamUrl = nrGenerateStreamUrl() ev.AddReplace("contentSrc", streamUrl) 'Generate Id from Src (hashing it) @@ -1120,8 +1211,15 @@ function nrAddVideoAttributes(ev as Object) as Object if m.nrTotalAdPlaytime > 0 ev.AddReplace("totalAdPlaytime", m.nrTotalAdPlaytime) end if - - return ev + contentNode = m.nrVideoObject.content.getChild(m.nrVideoObject.contentIndex) + ' Check if contentNode is valid + if contentNode <> invalid + contentTitle = contentNode.title + if contentTitle <> invalid + ev.AddReplace("contentTitle", contentTitle) + end if + end if +return ev end function '==============' @@ -1170,7 +1268,7 @@ function nrAddRAFAttributes(ev as Object, ctx as Dynamic) as Object end function function nrSendRAFEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void - ev = nrCreateEvent("RokuVideo", actionName) + ev = nrCreateEvent("VideoAdAction", actionName) ev = nrAddVideoAttributes(ev) ev = nrAddRAFAttributes(ev, ctx) ev = nrAddCustomAttributes(ev) diff --git a/components/NewRelicAgent/NRAgent.xml b/components/NewRelicAgent/NRAgent.xml index ac5a5b3..d78dcca 100644 --- a/components/NewRelicAgent/NRAgent.xml +++ b/components/NewRelicAgent/NRAgent.xml @@ -23,9 +23,10 @@ - + + diff --git a/components/VideoScene.brs b/components/VideoScene.brs index c12d8d4..5f81a80 100644 --- a/components/VideoScene.brs +++ b/components/VideoScene.brs @@ -3,6 +3,8 @@ sub init() print "INIT VideoScene" m.top.setFocus(true) + 'Uncomment the following line to set a user ID + 'setUserId("TEST_USER") end sub function nrRefUpdated() @@ -45,6 +47,11 @@ function updateCustomAttr() as Void 'Set a list of custom attributes to CONTENT_HEARTBEAT actions dict = {"key0":"val0", "key1":"val1"} nrSetCustomAttributeList(m.nr, dict, "CONTENT_HEARTBEAT") + nrSetCustomAttribute(m.nr,"enduser.id", m.userId) +end function + +function setUserId(userId as String) as Void + m.userId = userId end function function setupSingleVideo() as void diff --git a/source/Main.brs b/source/Main.brs index e94c9f9..33a9b58 100644 --- a/source/Main.brs +++ b/source/Main.brs @@ -40,7 +40,7 @@ sub Main(aa as Object) 'Send APP_STARTED event nrAppStarted(m.nr, aa) 'Send a custom system - nrSendSystemEvent(m.nr, "TEST_ACTION") + nrSendSystemEvent(m.nr, "RokuSystem","TEST_ACTION") 'Define multiple domain substitutions nrAddDomainSubstitution(m.nr, "^www\.google\.com$", "Google COM") diff --git a/source/NewRelicAgent.brs b/source/NewRelicAgent.brs index e75b6e0..55e8c80 100644 --- a/source/NewRelicAgent.brs +++ b/source/NewRelicAgent.brs @@ -123,26 +123,26 @@ function nrSceneLoaded(nr as Object, sceneName as String) as Void nr.callFunc("nrSceneLoaded", sceneName) end function -' Send a custom event. +' Send a system event, type RokuSystem. ' ' @param nr New Relic Agent object. ' @param eventType Event type. ' @param actionName Action name. ' @param attr (optional) Attributes associative array. -function nrSendCustomEvent(nr as Object, eventType as String, actionName as String, attr = invalid as Object) as Void - nr.callFunc("nrSendCustomEvent", eventType, actionName, attr) +function nrSendSystemEvent(nr as Object, eventType as String, actionName as String, attr = invalid as Object) as Void + nr.callFunc("nrSendSystemEvent", eventType, actionName, attr) end function -' Send a system event, type RokuSystem. +' Send a custom event, type VideoCustomAction. ' ' @param nr New Relic Agent object. ' @param actionName Action name. ' @param attr (optional) Attributes associative array. -function nrSendSystemEvent(nr as Object, actionName as String, attr = invalid) as Void - nr.callFunc("nrSendSystemEvent", actionName, attr) +function nrSendCustomEvent(nr as Object, actionName as String, attr = invalid as Object) as Void + nr.callFunc("nrSendCustomEvent", actionName, attr) end function -' Send a video event, type RokuVideo. +' Send a video event, type VideoAction. ' ' @param nr New Relic Agent object. ' @param actionName Action name.