From de8e9cbc25e35dd554121e5a0be6fbb15fcf9a48 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Thu, 28 Nov 2024 15:00:28 +0530 Subject: [PATCH 01/16] divided rokuVideo to videoAction and videoAdAction and rokuSystem to videoCustomAction and introduced videoErrorAction --- README.md | 28 ++++++++++----------- components/NewRelicAgent/NRAgent.brs | 37 +++++++++++++++++----------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index ff95218..4919168 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ 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 two types of events: VideoCustomAction for system events and VideoAction for video events. 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 VideoCustomAction, VideoAction ``` Will result in something like the following: @@ -327,7 +327,7 @@ Example: nrAppStarted(nr as Object, obj as Object) as Void Description: - Send an APP_STARTED event of type RokuSystem. + Send an APP_STARTED event of type VideoCustomAction. Arguments: nr: New Relic Agent object. @@ -349,7 +349,7 @@ Example: nrSceneLoaded(nr as Object, sceneName as String) as Void Description: - Send a SCENE_LOADED event of type RokuSystem. + Send a SCENE_LOADED event of type VideoCustomAction. Arguments: nr: New Relic Agent object. @@ -393,7 +393,7 @@ Example: nrSendSystemEvent(nr as Object, actionName as String, attr = invalid) as Void Description: - Send a system event, type RokuSystem. + Send a system event, type VideoCustomAction. Arguments: nr: New Relic Agent object. @@ -416,7 +416,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. @@ -439,7 +439,7 @@ Example: nrSendHttpRequest(nr as Object, urlReq as Object) as Void Description: - Send an HTTP_REQUEST event of type RokuSystem. + Send an HTTP_REQUEST event of type VideoCustomAction. Arguments: nr: New Relic Agent object. @@ -462,7 +462,7 @@ Example: nrSendHttpResponse(nr as Object, _url as String, msg as Object) as Void Description: - Send an HTTP_RESPONSE event of type RokuSystem. + Send an HTTP_RESPONSE event of type VideoCustomAction. Arguments: nr: New Relic Agent object. @@ -805,11 +805,11 @@ Example: ### Data Model -The agent generates two different event types: `RokuSystem` and `RokuVideo`. +The agent generates two different event types: `VideoCustomAction` and `VideoAction`. -#### 1. RokuSystem +#### 1. VideoCustomAction This event groups all actions related to system tracking. @@ -828,7 +828,7 @@ This event groups all actions related to system tracking. #### 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. +There is a set of attributes common to all actions sent over a `VideoCustomAction` and others are specific to a certain action. #### 1.2.1 Common Attributes @@ -908,7 +908,7 @@ There is a set of attributes common to all actions sent over a `RokuSystem` and -#### 2. RokuVideo +#### 2. VideoAction This event groups all actions related to video tracking. @@ -930,11 +930,11 @@ This event groups all actions related to video tracking. #### 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. +There is a set of attributes common to all actions sent over a `VideoAction` 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. +For video events, the common attributes include all `VideoCustomAction` common attributes (1.2.1) plus the video event ones. Here we will describe only the video common attributes. | Attribute Name | Description | |---|---| diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index e6a172b..0cac804 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -243,11 +243,11 @@ function nrAppStarted(aa as Object) as Void "instantOnRunMode": aa["instant_on_run_mode"], "launchSource": aa["source"] } - nrSendCustomEvent("RokuSystem", "APP_STARTED", attr) + nrSendCustomEvent("VideoCustomAction", "APP_STARTED", attr) end function function nrSceneLoaded(sceneName as String) as Void - nrSendCustomEvent("RokuSystem", "SCENE_LOADED", {"sceneName": sceneName}) + nrSendCustomEvent("VideoCustomAction", "SCENE_LOADED", {"sceneName": sceneName}) end function function nrSendCustomEvent(eventType as String, actionName as String, attr = invalid as Object) as Void @@ -261,11 +261,11 @@ function nrSendCustomEvent(eventType as String, actionName as String, attr = inv end function function nrSendSystemEvent(actionName as String, attr = invalid) as Void - nrSendCustomEvent("RokuSystem", actionName, attr) + nrSendCustomEvent("VideoCustomAction", 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 +280,16 @@ 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 nrSendHttpRequest(attr as Object) as Void domain = nrExtractDomainFromUrl(attr["origUrl"]) attr["domain"] = domain @@ -305,7 +315,7 @@ function nrSendHttpRequest(attr as Object) as Void m.num_http_request_counters.AddReplace(domain, 1) end if - nrSendCustomEvent("RokuSystem", "HTTP_REQUEST", attr) + nrSendCustomEvent("VideoCustomAction", "HTTP_REQUEST", attr) end function function nrSendHttpResponse(attr as Object) as Void @@ -337,7 +347,7 @@ function nrSendHttpResponse(attr as Object) as Void end if end if - nrSendCustomEvent("RokuSystem", "HTTP_RESPONSE", attr) + nrSendCustomEvent("VideoCustomAction", "HTTP_RESPONSE", attr) end function function nrEnableHttpEvents() as Void @@ -461,7 +471,7 @@ function nrTrackRAF(evtType = invalid as Dynamic, ctx = invalid as Dynamic) as V 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) + nrSendErrorEvent("AD_ERROR", ctx, attr) end if else if ctx <> invalid and ctx.time <> invalid and ctx.duration <> invalid 'Time progress event @@ -811,8 +821,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 +836,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 nrSendCustomEvent("VideoCustomAction", "HTTP_CONNECT", attr) end function function nrSendHTTPComplete(info as Object) as Void @@ -854,7 +863,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 nrSendCustomEvent("VideoCustomAction", "HTTP_COMPLETE", attr) domain = nrExtractDomainFromUrl(attr["origUrl"]) nrSendMetric("roku.http.complete.connectTime", attr["connectTime"], {"domain": domain}) @@ -868,7 +877,7 @@ function nrSendBandwidth(info as Object) as Void attr = { "bandwidth": info["bandwidth"] } - nrSendCustomEvent("RokuSystem", "BANDWIDTH_MINUTE", attr) + nrSendCustomEvent("VideoCustomAction", "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". @@ -999,7 +1008,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 @@ -1170,7 +1179,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) From ef416c45002b1154b1153a911abbd2012ee7ed9f Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Mon, 2 Dec 2024 16:03:47 +0530 Subject: [PATCH 02/16] replaced videoCustomAction with RokuSystem and added a function for videoCustomAction --- README.md | 28 +++++++++++++------------- components/NewRelicAgent/NRAgent.brs | 30 ++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 4919168..ff95218 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ 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: VideoCustomAction for system events and VideoAction 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 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: ``` -SELECT * FROM VideoCustomAction, VideoAction +SELECT * FROM RokuSystem, RokuVideo ``` Will result in something like the following: @@ -327,7 +327,7 @@ Example: nrAppStarted(nr as Object, obj as Object) as Void Description: - Send an APP_STARTED event of type VideoCustomAction. + Send an APP_STARTED event of type RokuSystem. Arguments: nr: New Relic Agent object. @@ -349,7 +349,7 @@ Example: nrSceneLoaded(nr as Object, sceneName as String) as Void Description: - Send a SCENE_LOADED event of type VideoCustomAction. + Send a SCENE_LOADED event of type RokuSystem. Arguments: nr: New Relic Agent object. @@ -393,7 +393,7 @@ Example: nrSendSystemEvent(nr as Object, actionName as String, attr = invalid) as Void Description: - Send a system event, type VideoCustomAction. + Send a system event, type RokuSystem. Arguments: nr: New Relic Agent object. @@ -416,7 +416,7 @@ Example: nrSendVideoEvent(nr as Object, actionName as String, attr = invalid) as Void Description: - Send a video event, type VideoAction. + Send a video event, type RokuVideo. Arguments: nr: New Relic Agent object. @@ -439,7 +439,7 @@ Example: nrSendHttpRequest(nr as Object, urlReq as Object) as Void Description: - Send an HTTP_REQUEST event of type VideoCustomAction. + Send an HTTP_REQUEST event of type RokuSystem. Arguments: nr: New Relic Agent object. @@ -462,7 +462,7 @@ Example: nrSendHttpResponse(nr as Object, _url as String, msg as Object) as Void Description: - Send an HTTP_RESPONSE event of type VideoCustomAction. + Send an HTTP_RESPONSE event of type RokuSystem. Arguments: nr: New Relic Agent object. @@ -805,11 +805,11 @@ Example: ### Data Model -The agent generates two different event types: `VideoCustomAction` and `VideoAction`. +The agent generates two different event types: `RokuSystem` and `RokuVideo`. -#### 1. VideoCustomAction +#### 1. RokuSystem This event groups all actions related to system tracking. @@ -828,7 +828,7 @@ This event groups all actions related to system tracking. #### 1.2 Attributes -There is a set of attributes common to all actions sent over a `VideoCustomAction` and others are specific to a certain action. +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 @@ -908,7 +908,7 @@ There is a set of attributes common to all actions sent over a `VideoCustomActio -#### 2. VideoAction +#### 2. RokuVideo This event groups all actions related to video tracking. @@ -930,11 +930,11 @@ This event groups all actions related to video tracking. #### 2.2 Attributes -There is a set of attributes common to all actions sent over a `VideoAction` and others are specific to a certain action. +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 `VideoCustomAction` common attributes (1.2.1) plus the video event ones. Here we will describe only the video 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 | |---|---| diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index 0cac804..5cf511b 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -243,11 +243,11 @@ function nrAppStarted(aa as Object) as Void "instantOnRunMode": aa["instant_on_run_mode"], "launchSource": aa["source"] } - nrSendCustomEvent("VideoCustomAction", "APP_STARTED", attr) + nrSendCustomEvent("RokuSystem", "APP_STARTED", attr) end function function nrSceneLoaded(sceneName as String) as Void - nrSendCustomEvent("VideoCustomAction", "SCENE_LOADED", {"sceneName": sceneName}) + nrSendCustomEvent("RokuSystem", "SCENE_LOADED", {"sceneName": sceneName}) end function function nrSendCustomEvent(eventType as String, actionName as String, attr = invalid as Object) as Void @@ -261,7 +261,7 @@ function nrSendCustomEvent(eventType as String, actionName as String, attr = inv end function function nrSendSystemEvent(actionName as String, attr = invalid) as Void - nrSendCustomEvent("VideoCustomAction", actionName, attr) + nrSendCustomEvent("RokuSystem", actionName, attr) end function function nrSendVideoEvent(actionName as String, attr = invalid) as Void @@ -290,6 +290,15 @@ function nrSendErrorEvent(actionName as String, ctx as Dynamic, attr = invalid) nrRecordEvent(ev) end function +function nrSendVideoCustomActionEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void + ev = nrCreateEvent("VideoCustomAction", actionName) + ev = nrAddVideoCustomActionAttributes(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 @@ -315,7 +324,7 @@ function nrSendHttpRequest(attr as Object) as Void m.num_http_request_counters.AddReplace(domain, 1) end if - nrSendCustomEvent("VideoCustomAction", "HTTP_REQUEST", attr) + nrSendCustomEvent("RokuSystem", "HTTP_REQUEST", attr) end function function nrSendHttpResponse(attr as Object) as Void @@ -347,7 +356,7 @@ function nrSendHttpResponse(attr as Object) as Void end if end if - nrSendCustomEvent("VideoCustomAction", "HTTP_RESPONSE", attr) + nrSendCustomEvent("RokuSystem", "HTTP_RESPONSE", attr) end function function nrEnableHttpEvents() as Void @@ -836,7 +845,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("VideoCustomAction", "HTTP_CONNECT", attr) + if m.http_events_enabled then nrSendCustomEvent("RokuSystem", "HTTP_CONNECT", attr) end function function nrSendHTTPComplete(info as Object) as Void @@ -863,7 +872,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("VideoCustomAction", "HTTP_COMPLETE", attr) + if m.http_events_enabled then nrSendCustomEvent("RokuSystem", "HTTP_COMPLETE", attr) domain = nrExtractDomainFromUrl(attr["origUrl"]) nrSendMetric("roku.http.complete.connectTime", attr["connectTime"], {"domain": domain}) @@ -877,7 +886,7 @@ function nrSendBandwidth(info as Object) as Void attr = { "bandwidth": info["bandwidth"] } - nrSendCustomEvent("VideoCustomAction", "BANDWIDTH_MINUTE", attr) + nrSendCustomEvent("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". @@ -1067,6 +1076,11 @@ function nrSendBackupVideoEnd() as Void m.nrPlaytimeSinceLastEvent = invalid end function +function nrAddVideoCustomActionAttributes(ev as Object) as object + 'Add custom attributes for VideoCustomAction + return ev +end function + function nrAddVideoAttributes(ev as Object) as Object ev.AddReplace("contentDuration", m.nrVideoObject.duration * 1000) ev.AddReplace("contentPlayhead", m.nrVideoObject.position * 1000) From f2cb9328532a5559e67de8a34474c9c968581bf1 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Mon, 2 Dec 2024 16:21:28 +0530 Subject: [PATCH 03/16] updated videoCustomAction function --- components/NewRelicAgent/NRAgent.brs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index 5cf511b..ed9f2a9 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -292,6 +292,8 @@ end function function nrSendVideoCustomActionEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void ev = nrCreateEvent("VideoCustomAction", actionName) + ev = nrAddVideoAttributes(ev) + ev = nrAddCustomAttributes(ev) ev = nrAddVideoCustomActionAttributes(ev) if type(attr) = "roAssociativeArray" ev.Append(attr) @@ -1078,6 +1080,7 @@ end function function nrAddVideoCustomActionAttributes(ev as Object) as object 'Add custom attributes for VideoCustomAction + return ev end function From a1fb67c00a6c22dd1549dc8140f2ddb7ee382f29 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Tue, 3 Dec 2024 11:31:39 +0530 Subject: [PATCH 04/16] added a condition for videoCustomAction --- components/NewRelicAgent/NRAgent.brs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index ed9f2a9..650a209 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -292,9 +292,24 @@ end function function nrSendVideoCustomActionEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void ev = nrCreateEvent("VideoCustomAction", actionName) - ev = nrAddVideoAttributes(ev) - ev = nrAddCustomAttributes(ev) ev = nrAddVideoCustomActionAttributes(ev) + ' 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 From 30d638961899140ba81d47fba973489c3c2e58e8 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Tue, 10 Dec 2024 11:15:42 +0530 Subject: [PATCH 05/16] switching attributes --- components/NewRelicAgent/NRAgent.brs | 121 +++++++++++++++++++++------ 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index 650a209..14955ac 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 '==========================' @@ -456,47 +457,47 @@ function nrTrackRAF(evtType = invalid as Dynamic, ctx = invalid as Dynamic) as V if GetInterface(evtType, "ifString") <> invalid and ctx <> invalid if evtType = "PodStart" nrResetRAFTimers() - nrSendRAFEvent("AD_BREAK_START", ctx) + nrSendRAFEvent("AD_BREAK_START", ctx,{"elapsedTime": nrCalculateElapsedTime("AD_BREAK_START")}) m.rafState.timeSinceAdBreakBegin = m.nrTimer.TotalMilliseconds() else if evtType = "PodComplete" 'Calc attributes for Ad break end timeSinceAdBreakBegin = m.nrTimer.TotalMilliseconds() - m.rafState.timeSinceAdBreakBegin nrAddToTotalAdPlaytime(timeSinceAdBreakBegin) - nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin}) + nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin, "elapsedTime": nrCalculateElapsedTime("AD_BREAK_END")}) else if evtType = "Impression" - nrSendRAFEvent("AD_REQUEST", ctx) + nrSendRAFEvent("AD_REQUEST", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_REQUEST")}) m.rafState.timeSinceAdRequested = m.nrTimer.TotalMilliseconds() else if evtType = "Start" m.rafState.numberOfAds = m.rafState.numberOfAds + 1 - nrSendRAFEvent("AD_START", ctx) + nrSendRAFEvent("AD_START", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_START")}) m.rafState.timeSinceAdStarted = m.nrTimer.TotalMilliseconds() else if evtType = "Complete" - nrSendRAFEvent("AD_END", ctx) + nrSendRAFEvent("AD_END", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_END")}) 'Reset attributes after END nrResetRAFState() m.rafState.timeSinceAdRequested = 0 m.rafState.timeSinceAdStarted = 0 else if evtType = "Pause" - nrSendRAFEvent("AD_PAUSE", ctx) + nrSendRAFEvent("AD_PAUSE", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_PAUSE")}) m.rafState.timeSinceAdPaused = m.nrTimer.TotalMilliseconds() else if evtType = "Resume" timeSinceAdPaused = m.nrTimer.TotalMilliseconds() - m.rafState.timeSinceAdPaused - nrSendRAFEvent("AD_RESUME", ctx, {"timeSinceAdPaused": timeSinceAdPaused}) + nrSendRAFEvent("AD_RESUME", ctx, {"timeSinceAdPaused": timeSinceAdPaused, "elapsedTime": nrCalculateElapsedTime("AD_RESUME")}) else if evtType = "Close" - nrSendRAFEvent("AD_SKIP", ctx) - nrSendRAFEvent("AD_END", ctx) + nrSendRAFEvent("AD_SKIP", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_SKIP")}) + nrSendRAFEvent("AD_END", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_END")}) 'Reset attributes after END nrResetRAFState() m.rafState.timeSinceAdRequested = 0 m.rafState.timeSinceAdStarted = 0 'Calc attributes for Ad break end timeSinceAdBreakBegin = m.nrTimer.TotalMilliseconds() - m.rafState.timeSinceAdBreakBegin - nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin}) + nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin, "elapsedTime": nrCalculateElapsedTime("AD_BREAK_END")}) 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) + 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 @@ -507,13 +508,13 @@ function nrTrackRAF(evtType = invalid as Dynamic, ctx = invalid as Dynamic) as V if ctx.time >= firstQuartile and ctx.time < secondQuartile and m.rafState.didFirstQuartile = false m.rafState.didFirstQuartile = true - nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 1}) + nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 1, "elapsedTime": nrCalculateElapsedTime("AD_QUARTILE")}) else if ctx.time >= secondQuartile and ctx.time < thirdQuartile and m.rafState.didSecondQuartile = false m.rafState.didSecondQuartile = true - nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 2}) + nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 2, "elapsedTime": nrCalculateElapsedTime("AD_QUARTILE")}) else if ctx.time >= thirdQuartile and m.rafState.didThirdQuartile = false m.rafState.didThirdQuartile = true - nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 3}) + nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 3, "elapsedTime": nrCalculateElapsedTime("AD_QUARTILE")}) end if end if end function @@ -759,8 +760,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()) @@ -781,7 +783,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()) @@ -836,6 +838,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"] @@ -946,6 +979,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") @@ -953,19 +1010,19 @@ end function function nrSendRequest() as Void m.nrTimeSinceRequested = m.nrTimer.TotalMilliseconds() - nrSendVideoEvent("CONTENT_REQUEST") + nrSendVideoEvent("CONTENT_REQUEST", {"elapsedTime": nrCalculateElapsedTime("CONTENT_REQUEST")}) end function function nrSendStart() as Void m.nrNumberOfErrors = 0 m.nrTimeSinceStarted = m.nrTimer.TotalMilliseconds() - nrSendVideoEvent("CONTENT_START") + nrSendVideoEvent("CONTENT_START", {"elapsedTime": nrCalculateElapsedTime("CONTENT_START")}) nrResumePlaytime() m.nrPlaytimeSinceLastEvent = CreateObject("roTimespan") end function function nrSendEnd() as Void - nrSendVideoEvent("CONTENT_END") + nrSendVideoEvent("CONTENT_END", {"elapsedTime": nrCalculateElapsedTime("CONTENT_END")}) m.nrVideoCounter = m.nrVideoCounter + 1 nrResetPlaytime() m.nrPlaytimeSinceLastEvent = invalid @@ -973,13 +1030,13 @@ end function function nrSendPause() as Void m.nrTimeSincePaused = m.nrTimer.TotalMilliseconds() - nrSendVideoEvent("CONTENT_PAUSE") + nrSendVideoEvent("CONTENT_PAUSE", {"elapsedTime": nrCalculateElapsedTime("CONTENT_PAUSE")}) nrPausePlaytime() m.nrPlaytimeSinceLastEvent = invalid end function function nrSendResume() as Void - nrSendVideoEvent("CONTENT_RESUME") + nrSendVideoEvent("CONTENT_RESUME", {"elapsedTime": nrCalculateElapsedTime("CONTENT_RESUME")}) nrResumePlaytime() m.nrPlaytimeSinceLastEvent = CreateObject("roTimespan") end function @@ -992,7 +1049,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, "elapsedTime": nrCalculateElapsedTime("CONTENT_BUFFER_START"), "bufferType": nrCalculateBufferType("CONTENT_BUFFER_START")}) nrPausePlaytime() m.nrPlaytimeSinceLastEvent = invalid end function @@ -1003,14 +1060,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, "elapsedTime": nrCalculateElapsedTime("CONTENT_BUFFER_END"), "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 @@ -1022,7 +1079,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, @@ -1103,6 +1160,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) @@ -1161,6 +1219,15 @@ function nrAddVideoAttributes(ev as Object) as Object if m.nrTotalAdPlaytime > 0 ev.AddReplace("totalAdPlaytime", m.nrTotalAdPlaytime) end if + 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 return ev end function From 6baf59421cf55661accf5eddfa4b9e8edc5ecad2 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Tue, 10 Dec 2024 14:04:10 +0530 Subject: [PATCH 06/16] minor fix --- components/NewRelicAgent/NRAgent.brs | 74 +++++++++++++--------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index 14955ac..d0c9805 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -244,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 @@ -261,10 +261,6 @@ 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("VideoAction", actionName) ev = nrAddVideoAttributes(ev) @@ -291,9 +287,8 @@ function nrSendErrorEvent(actionName as String, ctx as Dynamic, attr = invalid) nrRecordEvent(ev) end function -function nrSendVideoCustomActionEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void +function nrSendCustomEvent(actionName as String, ctx as Dynamic, attr = invalid) as Void ev = nrCreateEvent("VideoCustomAction", actionName) - ev = nrAddVideoCustomActionAttributes(ev) ' Check if ctx contains attributes other than adpartner with value "raf" hasOtherAdAttributes = false if ctx <> invalid and type(ctx) = "roAssociativeArray" @@ -342,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 @@ -374,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 @@ -457,42 +452,42 @@ function nrTrackRAF(evtType = invalid as Dynamic, ctx = invalid as Dynamic) as V if GetInterface(evtType, "ifString") <> invalid and ctx <> invalid if evtType = "PodStart" nrResetRAFTimers() - nrSendRAFEvent("AD_BREAK_START", ctx,{"elapsedTime": nrCalculateElapsedTime("AD_BREAK_START")}) + nrSendRAFEvent("AD_BREAK_START", ctx) m.rafState.timeSinceAdBreakBegin = m.nrTimer.TotalMilliseconds() else if evtType = "PodComplete" 'Calc attributes for Ad break end timeSinceAdBreakBegin = m.nrTimer.TotalMilliseconds() - m.rafState.timeSinceAdBreakBegin nrAddToTotalAdPlaytime(timeSinceAdBreakBegin) - nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin, "elapsedTime": nrCalculateElapsedTime("AD_BREAK_END")}) + nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin}) else if evtType = "Impression" - nrSendRAFEvent("AD_REQUEST", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_REQUEST")}) + nrSendRAFEvent("AD_REQUEST", ctx) m.rafState.timeSinceAdRequested = m.nrTimer.TotalMilliseconds() else if evtType = "Start" m.rafState.numberOfAds = m.rafState.numberOfAds + 1 - nrSendRAFEvent("AD_START", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_START")}) + nrSendRAFEvent("AD_START", ctx) m.rafState.timeSinceAdStarted = m.nrTimer.TotalMilliseconds() else if evtType = "Complete" - nrSendRAFEvent("AD_END", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_END")}) + nrSendRAFEvent("AD_END", ctx) 'Reset attributes after END nrResetRAFState() m.rafState.timeSinceAdRequested = 0 m.rafState.timeSinceAdStarted = 0 else if evtType = "Pause" - nrSendRAFEvent("AD_PAUSE", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_PAUSE")}) + nrSendRAFEvent("AD_PAUSE", ctx) m.rafState.timeSinceAdPaused = m.nrTimer.TotalMilliseconds() else if evtType = "Resume" timeSinceAdPaused = m.nrTimer.TotalMilliseconds() - m.rafState.timeSinceAdPaused - nrSendRAFEvent("AD_RESUME", ctx, {"timeSinceAdPaused": timeSinceAdPaused, "elapsedTime": nrCalculateElapsedTime("AD_RESUME")}) + nrSendRAFEvent("AD_RESUME", ctx, {"timeSinceAdPaused": timeSinceAdPaused}) else if evtType = "Close" - nrSendRAFEvent("AD_SKIP", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_SKIP")}) - nrSendRAFEvent("AD_END", ctx, {"elapsedTime": nrCalculateElapsedTime("AD_END")}) + nrSendRAFEvent("AD_SKIP", ctx) + nrSendRAFEvent("AD_END", ctx) 'Reset attributes after END nrResetRAFState() m.rafState.timeSinceAdRequested = 0 m.rafState.timeSinceAdStarted = 0 'Calc attributes for Ad break end timeSinceAdBreakBegin = m.nrTimer.TotalMilliseconds() - m.rafState.timeSinceAdBreakBegin - nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin, "elapsedTime": nrCalculateElapsedTime("AD_BREAK_END")}) + nrSendRAFEvent("AD_BREAK_END", ctx, {"timeSinceAdBreakBegin": timeSinceAdBreakBegin}) else if evtType = "Error" attr = {} if ctx.errType <> invalid then attr.AddReplace("adErrorType", ctx.errType) @@ -508,13 +503,13 @@ function nrTrackRAF(evtType = invalid as Dynamic, ctx = invalid as Dynamic) as V if ctx.time >= firstQuartile and ctx.time < secondQuartile and m.rafState.didFirstQuartile = false m.rafState.didFirstQuartile = true - nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 1, "elapsedTime": nrCalculateElapsedTime("AD_QUARTILE")}) + nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 1}) else if ctx.time >= secondQuartile and ctx.time < thirdQuartile and m.rafState.didSecondQuartile = false m.rafState.didSecondQuartile = true - nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 2, "elapsedTime": nrCalculateElapsedTime("AD_QUARTILE")}) + nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 2}) else if ctx.time >= thirdQuartile and m.rafState.didThirdQuartile = false m.rafState.didThirdQuartile = true - nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 3, "elapsedTime": nrCalculateElapsedTime("AD_QUARTILE")}) + nrSendRAFEvent("AD_QUARTILE", ctx, {"adQuartile": 3}) end if end if end function @@ -822,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 @@ -895,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 @@ -922,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}) @@ -936,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". @@ -1010,19 +1008,19 @@ end function function nrSendRequest() as Void m.nrTimeSinceRequested = m.nrTimer.TotalMilliseconds() - nrSendVideoEvent("CONTENT_REQUEST", {"elapsedTime": nrCalculateElapsedTime("CONTENT_REQUEST")}) + nrSendVideoEvent("CONTENT_REQUEST") end function function nrSendStart() as Void m.nrNumberOfErrors = 0 m.nrTimeSinceStarted = m.nrTimer.TotalMilliseconds() - nrSendVideoEvent("CONTENT_START", {"elapsedTime": nrCalculateElapsedTime("CONTENT_START")}) + nrSendVideoEvent("CONTENT_START") nrResumePlaytime() m.nrPlaytimeSinceLastEvent = CreateObject("roTimespan") end function function nrSendEnd() as Void - nrSendVideoEvent("CONTENT_END", {"elapsedTime": nrCalculateElapsedTime("CONTENT_END")}) + nrSendVideoEvent("CONTENT_END") m.nrVideoCounter = m.nrVideoCounter + 1 nrResetPlaytime() m.nrPlaytimeSinceLastEvent = invalid @@ -1030,13 +1028,13 @@ end function function nrSendPause() as Void m.nrTimeSincePaused = m.nrTimer.TotalMilliseconds() - nrSendVideoEvent("CONTENT_PAUSE", {"elapsedTime": nrCalculateElapsedTime("CONTENT_PAUSE")}) + nrSendVideoEvent("CONTENT_PAUSE") nrPausePlaytime() m.nrPlaytimeSinceLastEvent = invalid end function function nrSendResume() as Void - nrSendVideoEvent("CONTENT_RESUME", {"elapsedTime": nrCalculateElapsedTime("CONTENT_RESUME")}) + nrSendVideoEvent("CONTENT_RESUME") nrResumePlaytime() m.nrPlaytimeSinceLastEvent = CreateObject("roTimespan") end function @@ -1049,7 +1047,7 @@ function nrSendBufferStart() as Void else m.nrIsInitialBuffering = false end if - nrSendVideoEvent("CONTENT_BUFFER_START", {"isInitialBuffering": m.nrIsInitialBuffering, "elapsedTime": nrCalculateElapsedTime("CONTENT_BUFFER_START"), "bufferType": nrCalculateBufferType("CONTENT_BUFFER_START")}) + nrSendVideoEvent("CONTENT_BUFFER_START", {"isInitialBuffering": m.nrIsInitialBuffering, "bufferType": nrCalculateBufferType("CONTENT_BUFFER_START")}) nrPausePlaytime() m.nrPlaytimeSinceLastEvent = invalid end function @@ -1060,7 +1058,7 @@ function nrSendBufferEnd() as Void else m.nrIsInitialBuffering = false end if - nrSendVideoEvent("CONTENT_BUFFER_END", {"isInitialBuffering": m.nrIsInitialBuffering, "elapsedTime": nrCalculateElapsedTime("CONTENT_BUFFER_END"), "bufferType": nrCalculateBufferType("CONTENT_BUFFER_END")}) + nrSendVideoEvent("CONTENT_BUFFER_END", {"isInitialBuffering": m.nrIsInitialBuffering, "bufferType": nrCalculateBufferType("CONTENT_BUFFER_END")}) nrResumePlaytime() m.nrPlaytimeSinceLastEvent = CreateObject("roTimespan") end function @@ -1150,12 +1148,6 @@ function nrSendBackupVideoEnd() as Void m.nrPlaytimeSinceLastEvent = invalid end function -function nrAddVideoCustomActionAttributes(ev as Object) as object - 'Add custom attributes for VideoCustomAction - - return ev -end function - function nrAddVideoAttributes(ev as Object) as Object ev.AddReplace("contentDuration", m.nrVideoObject.duration * 1000) ev.AddReplace("contentPlayhead", m.nrVideoObject.position * 1000) From 63d64ad3eb13981a5f6810a06d5c68c11cab5dd0 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Fri, 13 Dec 2024 18:50:39 +0530 Subject: [PATCH 07/16] revert some changes for testing --- components/NewRelicAgent/NRAgent.brs | 74 +++++++++++++++------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index d0c9805..8aaf21c 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -244,15 +244,15 @@ function nrAppStarted(aa as Object) as Void "instantOnRunMode": aa["instant_on_run_mode"], "launchSource": aa["source"] } - nrSendSystemEvent("RokuSystem", "APP_STARTED", attr) + nrSendCustomEvent("RokuSystem", "APP_STARTED", attr) end function function nrSceneLoaded(sceneName as String) as Void - nrSendSystemEvent("RokuSystem", "SCENE_LOADED", {"sceneName": sceneName}) + nrSendCustomEvent("RokuSystem", "SCENE_LOADED", {"sceneName": sceneName}) end function -function nrSendSystemEvent(eventType as String, actionName as String, attr = invalid as Object) as Void - nrLog("nrSendSystemEvent") +function nrSendCustomEvent(eventType as String, actionName as String, attr = invalid as Object) as Void + nrLog("nrSendCustomEvent") ev = nrCreateEvent(eventType, actionName) ev = nrAddCustomAttributes(ev) if attr <> invalid @@ -261,6 +261,10 @@ function nrSendSystemEvent(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("VideoAction", actionName) ev = nrAddVideoAttributes(ev) @@ -287,30 +291,30 @@ function nrSendErrorEvent(actionName as String, ctx as Dynamic, attr = invalid) 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 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"]) @@ -337,7 +341,7 @@ function nrSendHttpRequest(attr as Object) as Void m.num_http_request_counters.AddReplace(domain, 1) end if - nrSendSystemEvent("RokuSystem", "HTTP_REQUEST", attr) + nrSendCustomEvent("RokuSystem", "HTTP_REQUEST", attr) end function function nrSendHttpResponse(attr as Object) as Void @@ -369,7 +373,7 @@ function nrSendHttpResponse(attr as Object) as Void end if end if - nrSendSystemEvent("RokuSystem", "HTTP_RESPONSE", attr) + nrSendCustomEvent("RokuSystem", "HTTP_RESPONSE", attr) end function function nrEnableHttpEvents() as Void @@ -893,7 +897,7 @@ function nrSendHTTPConnect(info as Object) as Void m.num_http_connect_counters.AddReplace(domain, 1) end if - if m.http_events_enabled then nrSendSystemEvent("RokuSystem", "HTTP_CONNECT", attr) + if m.http_events_enabled then nrSendCustomEvent("RokuSystem", "HTTP_CONNECT", attr) end function function nrSendHTTPComplete(info as Object) as Void @@ -920,7 +924,7 @@ function nrSendHTTPComplete(info as Object) as Void m.num_http_complete_counters.AddReplace(domain, 1) end if - if m.http_events_enabled then nrSendSystemEvent("RokuSystem", "HTTP_COMPLETE", attr) + if m.http_events_enabled then nrSendCustomEvent("RokuSystem", "HTTP_COMPLETE", attr) domain = nrExtractDomainFromUrl(attr["origUrl"]) nrSendMetric("roku.http.complete.connectTime", attr["connectTime"], {"domain": domain}) @@ -934,7 +938,7 @@ function nrSendBandwidth(info as Object) as Void attr = { "bandwidth": info["bandwidth"] } - nrSendSystemEvent("RokuSystem", "BANDWIDTH_MINUTE", attr) + nrSendCustomEvent("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". @@ -942,9 +946,9 @@ end function function nrEventApiUrl() as String if m.nrRegion = "US" - return "https://insights-collector.newrelic.com/v1/accounts/" + m.nrAccountNumber + "/events" + return "https://staging-insights-collector.newrelic.com/v1/accounts/" + m.nrAccountNumber + "/events" else if m.nrRegion = "EU" - return "https://insights-collector.eu01.nr-data.net/v1/accounts/" + m.nrAccountNumber + "/events" + return "https://staging-insights-collector.newrelic.com/v1/accounts/" + m.nrAccountNumber + "/events" else if m.nrRegion = "TEST" 'NOTE: set address hosting the test server return m.testServer + "/event" From 3e200fb1d066cbd4a03305d5f8c84f4fb277dc34 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Fri, 13 Dec 2024 19:15:37 +0530 Subject: [PATCH 08/16] minor fix --- components/NewRelicAgent/NRAgent.brs | 70 +++++++++++++--------------- components/NewRelicAgent/NRAgent.xml | 3 +- source/NewRelicAgent.brs | 14 +++--- 3 files changed, 42 insertions(+), 45 deletions(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index 8aaf21c..1567d5c 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -244,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 @@ -261,10 +261,6 @@ 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("VideoAction", actionName) ev = nrAddVideoAttributes(ev) @@ -291,30 +287,30 @@ function nrSendErrorEvent(actionName as String, ctx as Dynamic, attr = invalid) 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 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"]) @@ -341,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 @@ -373,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 @@ -897,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 @@ -924,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}) @@ -938,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". 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/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. From c900a00e4a7fa12437421f47673594dc8fe727d5 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Sat, 14 Dec 2024 11:55:32 +0530 Subject: [PATCH 09/16] adding capability of setting user id --- components/VideoScene.brs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/VideoScene.brs b/components/VideoScene.brs index c12d8d4..5e8cdab 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("123456") 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,"UserID", m.userID) +end function + +function setUserID(userID as String) as Void + m.userID = userID end function function setupSingleVideo() as void From 9dd805aa05536016dd32a94ffb73441a6824ba77 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Tue, 17 Dec 2024 11:16:18 +0530 Subject: [PATCH 10/16] renamed userId to enduser.id --- components/VideoScene.brs | 2 +- source/Main.brs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/VideoScene.brs b/components/VideoScene.brs index 5e8cdab..8662deb 100644 --- a/components/VideoScene.brs +++ b/components/VideoScene.brs @@ -47,7 +47,7 @@ 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,"UserID", m.userID) + nrSetCustomAttribute(m.nr,"Enduser.id", m.userID) end function function setUserID(userID as String) 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") From c1af25e0943429a122f8f078ee8790790f537a5f Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Tue, 17 Dec 2024 16:38:08 +0530 Subject: [PATCH 11/16] updated readme.md --- DATAMODEL.md | 311 +++++++++++++++++++++++++++ README.md | 269 +---------------------- components/NewRelicAgent/NRAgent.brs | 2 - 3 files changed, 322 insertions(+), 260 deletions(-) create mode 100644 DATAMODEL.md diff --git a/DATAMODEL.md b/DATAMODEL.md new file mode 100644 index 0000000..3369519 --- /dev/null +++ b/DATAMODEL.md @@ -0,0 +1,311 @@ +# 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 + +- **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. | +| 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. | +| 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. | + + + +#### 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. | +| contentIsLive | True if the video is live. | +| contentBitrate | Bitrate (in bits) of the video. | +| contentRenditionName | Name of the rendition (e.g., 1080p). | +| contentRenditionBitrate | Target Bitrate of the rendition. | +| contentRenditionHeight | Rendition actual Height (before re-scaling). | +| contentRenditionWidth | Rendition actual Width (before re-scaling). | +| contentDuration | Duration of the video, in ms. | +| contentPlayhead | Playhead (currentTime) of the video, in ms. | +| contentLanguage | Language of the video. We recommend using locale notation, e.g., en_US. | +| contentSrc | URL of the resource being played. | +| contentPlayrate | Playrate (speed) of the video, e.g., 1.0, 0.5, 1.25. | +| contentIsFullscreen | True if the video is currently fullscreen. | +| contentIsMuted | True if the video is currently muted. | +| contentCdn | The CDN serving the content. | +| contentIsAutoplayed | If the player was auto-played. | +| contentPreload | The player preload attribute. | +| contentFps | Current FPS (Frames per second). | +| isBackgroundEvent | If the player is hidden by another window. | +| 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. | +| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | +| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | +| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | +| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | +| 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. | + +#### List of possible Video Actions + +| Action Name | Definition | +| ------------------------ | ------------------------------------------------------------------------------------------------ | +| PLAYER_READY | The player is ready to start sending events. | +| DOWNLOAD | Downloading data. | +| 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_SEEK_START | Content video seek started. | +| CONTENT_SEEK_END | Content video seek ended. | +| 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. | +| CONTENT_RENDITION_CHANGE | Content video stream quality changed. | + +### 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. | +| adBitrate | Bitrate (in bits) of the video. | +| adRenditionName | Name of the rendition (e.g., 1080p). | +| adRenditionBitrate | Target Bitrate of the rendition. | +| adRenditionHeight | Rendition actual Height (before re-scaling). | +| adRenditionWidth | Rendition actual Width (before re-scaling). | +| adDuration | Duration of the video, in ms. | +| adPlayhead | Playhead (currentTime) of the video, in ms. | +| adLanguage | Language of the ad video. We recommend using locale notation, e.g., en_US. | +| adSrc | URL of the resource being played. | +| adCdn | The CDN serving the content. | +| adIsMuted | True if the video is currently muted. | +| adFps | Current FPS (Frames per second). | +| 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. | +| isBackgroundEvent | If the player is hidden by another window. | +| bufferType | When buffer starts, i.e., initial, seek, pause & connection. | +| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | +| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | +| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | +| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | +| 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. | + +#### 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_SEEK_START | Ad video seek started. | +| AD_SEEK_END | Ad video seek ended. | +| AD_BUFFER_START | Ad video buffering started. | +| AD_BUFFER_END | Ad video buffering ended. | +| AD_HEARTBEAT | Ad video heartbeat, an event that happens once every 30 seconds while the video is playing. | +| AD_RENDITION_CHANGE | Ad video stream quality changed. | +| AD_BREAK_START | Ad break (a block of ads) started. | +| AD_BREAK_END | Ad break ended. | +| AD_QUARTILE | Ad quartile happened. | +| AD_CLICK | Ad has been clicked. | + +### 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. | +| isBackgroundEvent | If the player is hidden by another window. | +| contentSrc | Content source URL. | +| contentCdn | Content CDN URL. | +| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | +| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | +| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | +| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | +| 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. | + +#### List of possible Video Error Actions + +| Action Name | Definition | +| ------------- | -------------------- | +| AD_ERROR | Ad video error. | +| ERROR | An error happened. | +| CRASH | App crash happened. | +| 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. | +| isBackgroundEvent | If the player is hidden by another window. | +| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | +| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | +| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | +| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | +| 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. | \ No newline at end of file diff --git a/README.md b/README.md index ff95218..0cc753e 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. @@ -800,216 +803,9 @@ Return: Example: nrSendSummaryMetric(m.nr, "test", 2000, 5, 1000, 100, 200) ``` - - ### Data Model - -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. +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 +910,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 1567d5c..ab9fc12 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -1220,8 +1220,6 @@ function nrAddVideoAttributes(ev as Object) as Object end if end if return ev - - return ev end function '==============' From 4586d3d00af5a71ea830f4687ecc2e094ab672cf Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Tue, 17 Dec 2024 18:01:53 +0530 Subject: [PATCH 12/16] update datamodel.md --- DATAMODEL.md | 62 +++++----------------------------------------------- 1 file changed, 6 insertions(+), 56 deletions(-) diff --git a/DATAMODEL.md b/DATAMODEL.md index 3369519..f6c1982 100644 --- a/DATAMODEL.md +++ b/DATAMODEL.md @@ -34,8 +34,9 @@ An Attribute is a piece of data associated with an event. Attributes provide add | 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. | +| deviceUuid | Roku Device UUID. | +| deviceName | Roku device name. | +| deviceSize | Always "xLarge". | | deviceGroup | Always “Roku”. | | deviceManufacturer | Always “Roku”. | | deviceModel | Roku model. | @@ -54,7 +55,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | 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. | +| contentRenditionName | Display mode. | | displayAspectRatio | Aspect ratio. | | videoMode | Video mode. | | graphicsPlatform | Graphics platform (OpenGL, etc). | @@ -128,31 +129,17 @@ An Attribute is a piece of data associated with an event. Attributes provide add | viewId | Trackers will generate unique IDs for every new video iteration. | | contentId | The ID of the video. | | contentTitle | The title of the video. | -| contentIsLive | True if the video is live. | | contentBitrate | Bitrate (in bits) of the video. | +| contentIsFullscreen | Always "true". | | contentRenditionName | Name of the rendition (e.g., 1080p). | -| contentRenditionBitrate | Target Bitrate of the rendition. | -| contentRenditionHeight | Rendition actual Height (before re-scaling). | -| contentRenditionWidth | Rendition actual Width (before re-scaling). | | contentDuration | Duration of the video, in ms. | | contentPlayhead | Playhead (currentTime) of the video, in ms. | -| contentLanguage | Language of the video. We recommend using locale notation, e.g., en_US. | | contentSrc | URL of the resource being played. | -| contentPlayrate | Playrate (speed) of the video, e.g., 1.0, 0.5, 1.25. | | contentIsFullscreen | True if the video is currently fullscreen. | | contentIsMuted | True if the video is currently muted. | -| contentCdn | The CDN serving the content. | -| contentIsAutoplayed | If the player was auto-played. | -| contentPreload | The player preload attribute. | -| contentFps | Current FPS (Frames per second). | -| isBackgroundEvent | If the player is hidden by another window. | | 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. | -| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | -| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | -| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | -| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | | 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. | @@ -163,18 +150,14 @@ An Attribute is a piece of data associated with an event. Attributes provide add | Action Name | Definition | | ------------------------ | ------------------------------------------------------------------------------------------------ | | PLAYER_READY | The player is ready to start sending events. | -| DOWNLOAD | Downloading data. | | 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_SEEK_START | Content video seek started. | -| CONTENT_SEEK_END | Content video seek ended. | | 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. | -| CONTENT_RENDITION_CHANGE | Content video stream quality changed. | ### VideoAdAction @@ -193,28 +176,14 @@ An Attribute is a piece of data associated with an event. Attributes provide add | viewId | Trackers will generate unique IDs for every new video iteration. | | adId | The ID of the video. | | adTitle | The title of the video. | -| adBitrate | Bitrate (in bits) of the video. | -| adRenditionName | Name of the rendition (e.g., 1080p). | -| adRenditionBitrate | Target Bitrate of the rendition. | -| adRenditionHeight | Rendition actual Height (before re-scaling). | -| adRenditionWidth | Rendition actual Width (before re-scaling). | | adDuration | Duration of the video, in ms. | | adPlayhead | Playhead (currentTime) of the video, in ms. | -| adLanguage | Language of the ad video. We recommend using locale notation, e.g., en_US. | | adSrc | URL of the resource being played. | -| adCdn | The CDN serving the content. | | adIsMuted | True if the video is currently muted. | -| adFps | Current FPS (Frames per second). | | 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. | -| isBackgroundEvent | If the player is hidden by another window. | -| bufferType | When buffer starts, i.e., initial, seek, pause & connection. | -| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | -| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | -| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | -| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | | 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. | @@ -230,16 +199,9 @@ An Attribute is a piece of data associated with an event. Attributes provide add | AD_END | Ad video ended. | | AD_PAUSE | Ad video paused. | | AD_RESUME | Ad video resumed. | -| AD_SEEK_START | Ad video seek started. | -| AD_SEEK_END | Ad video seek ended. | -| AD_BUFFER_START | Ad video buffering started. | -| AD_BUFFER_END | Ad video buffering ended. | -| AD_HEARTBEAT | Ad video heartbeat, an event that happens once every 30 seconds while the video is playing. | -| AD_RENDITION_CHANGE | Ad video stream quality changed. | | AD_BREAK_START | Ad break (a block of ads) started. | | AD_BREAK_END | Ad break ended. | | AD_QUARTILE | Ad quartile happened. | -| AD_CLICK | Ad has been clicked. | ### VideoErrorAction @@ -261,13 +223,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | errorName | Name of the error. | | errorCode | Error code if it's known. | | backTrace | Stack trace of the error. | -| isBackgroundEvent | If the player is hidden by another window. | | contentSrc | Content source URL. | -| contentCdn | Content CDN URL. | -| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | -| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | -| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | -| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | | 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. | @@ -279,8 +235,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | Action Name | Definition | | ------------- | -------------------- | | AD_ERROR | Ad video error. | -| ERROR | An error happened. | -| CRASH | App crash happened. | +| HTTP_ERROR | An HTTP error. | | CONTENT_ERROR | Content video error. | ### VideoCustomAction @@ -300,11 +255,6 @@ An Attribute is a piece of data associated with an event. Attributes provide add | viewId | Trackers will generate unique IDs for every new video iteration. | | contentId | The ID of the video. | | contentTitle | The title of the video. | -| isBackgroundEvent | If the player is hidden by another window. | -| asn | Autonomous System Number: a unique number identifying a group of IP networks that serves the content to the end user. | -| asnLatitude | The latitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's latitude. | -| asnLongitude | The longitude of the geographic center of the postal code where the Autonomous System Network is registered. This is not the end user's longitude. | -| asnOrganization | The organization that owns the Autonomous System Number. Often an ISP, sometimes a private company or institution. | | 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. | From cf08555ee313d7f43e04457ce59c9a158a501677 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Wed, 18 Dec 2024 12:10:44 +0530 Subject: [PATCH 13/16] adding userId in readme.md --- DATAMODEL.md | 7 ++++++- README.md | 19 +++++++++++++++++++ components/VideoScene.brs | 8 ++++---- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/DATAMODEL.md b/DATAMODEL.md index f6c1982..807ffb1 100644 --- a/DATAMODEL.md +++ b/DATAMODEL.md @@ -93,6 +93,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | transferIdentity | HTTP request identificator. | | sceneName | Identifier of the scene. | | timeSinceHttpRequest | Time since `HTTP_REQUEST` in milliseconds. | +| enduser.id | User ID. | @@ -144,6 +145,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | 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 @@ -189,6 +191,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | 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 @@ -229,6 +232,7 @@ An Attribute is a piece of data associated with an event. Attributes provide add | 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 @@ -258,4 +262,5 @@ An Attribute is a piece of data associated with an event. Attributes provide add | 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. | \ No newline at end of file +| instrumentation.version | Agent’s version. | +| enduser.id | User ID. | \ No newline at end of file diff --git a/README.md b/README.md index 0cc753e..1a8b5d6 100644 --- a/README.md +++ b/README.md @@ -803,6 +803,25 @@ Return: Example: nrSendSummaryMetric(m.nr, "test", 2000, 5, 1000, 100, 200) ``` + +**setUserId** + +``` +setUserId(userId as String) as Void + +Description: + Sets userId. + +Arguments: + userId: attribute + +Return: + Nothing. + +Example: + setUserId("TEST_USER") +``` + ### 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). diff --git a/components/VideoScene.brs b/components/VideoScene.brs index 8662deb..5f81a80 100644 --- a/components/VideoScene.brs +++ b/components/VideoScene.brs @@ -4,7 +4,7 @@ sub init() print "INIT VideoScene" m.top.setFocus(true) 'Uncomment the following line to set a user ID - 'setUserID("123456") + 'setUserId("TEST_USER") end sub function nrRefUpdated() @@ -47,11 +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) + nrSetCustomAttribute(m.nr,"enduser.id", m.userId) end function -function setUserID(userID as String) as Void - m.userID = userID +function setUserId(userId as String) as Void + m.userId = userId end function function setupSingleVideo() as void From 479d77ec108da4d6cba8b2a69546709962f247ab Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Wed, 18 Dec 2024 16:18:10 +0530 Subject: [PATCH 14/16] minor fix --- components/NewRelicAgent/NRAgent.brs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index ab9fc12..7b5d986 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -942,9 +942,9 @@ end function function nrEventApiUrl() as String if m.nrRegion = "US" - return "https://staging-insights-collector.newrelic.com/v1/accounts/" + m.nrAccountNumber + "/events" + return "https://insights-collector.newrelic.com/v1/accounts/" + m.nrAccountNumber + "/events" else if m.nrRegion = "EU" - return "https://staging-insights-collector.newrelic.com/v1/accounts/" + m.nrAccountNumber + "/events" + return "https://insights-collector.eu01.nr-data.net/v1/accounts/" + m.nrAccountNumber + "/events" else if m.nrRegion = "TEST" 'NOTE: set address hosting the test server return m.testServer + "/event" From 6e5eb1321169b9c9b80b4a1ad9d7affc3a216b8c Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Thu, 19 Dec 2024 14:33:14 +0530 Subject: [PATCH 15/16] update datamodel.md --- DATAMODEL.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/DATAMODEL.md b/DATAMODEL.md index 807ffb1..646a2e2 100644 --- a/DATAMODEL.md +++ b/DATAMODEL.md @@ -8,6 +8,7 @@ This section defines the key terms used in the context of New Relic Media monito ## 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. @@ -263,4 +264,27 @@ An Attribute is a piece of data associated with an event. Attributes provide add | instrumentation.provider | Player/agent name. | | instrumentation.name | Name of the instrumentation collecting the data. | | instrumentation.version | Agent’s version. | -| enduser.id | User ID. | \ No newline at end of file +| 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 From 3af34aa090d15a26941eb5666d379527199b1ad7 Mon Sep 17 00:00:00 2001 From: bmendiratta Date: Mon, 23 Dec 2024 19:19:03 +0530 Subject: [PATCH 16/16] minor fix --- components/NewRelicAgent/NRAgent.brs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/NewRelicAgent/NRAgent.brs b/components/NewRelicAgent/NRAgent.brs index 7b5d986..f23457d 100644 --- a/components/NewRelicAgent/NRAgent.brs +++ b/components/NewRelicAgent/NRAgent.brs @@ -1152,7 +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") + ev.AddReplace("contentIsFullscreen","true") streamUrl = nrGenerateStreamUrl() ev.AddReplace("contentSrc", streamUrl) 'Generate Id from Src (hashing it)