@@ -48,90 +48,122 @@ Plugin being used - RT, AutoXHR, Continuity, NavigationTiming, ResourceTiming
4848 }
4949 } ;
5050 var initedBoomr = false ;
51+
52+ // device traces need information over multiple beacons. We accumulate the data and send it as a single trace, and then reset it.
53+ var deviceTrace = { } ;
54+ deviceTrace . type = "device" ;
55+ deviceTrace . apm_metrics = { } ;
56+
5157 /**
52- * Initialize Boomerang
53- * @param {Object } BOOMR - Boomerang object
58+ *
59+ * @param {Object } beaconData - Boomerang beacon data
5460 */
55- function initBoomerang ( BOOMR ) {
56- if ( BOOMR && ! initedBoomr ) {
57- BOOMR . subscribe ( "before_beacon" , function ( beaconData ) {
58- self . _internals . log ( "[INFO]" , "Boomerang, before_beacon:" , JSON . stringify ( beaconData , null , 2 ) ) ;
59- var trace = { } ;
60- if ( beaconData [ "rt.start" ] !== "manual" && ! beaconData [ "http.initiator" ] && beaconData [ "rt.quit" ] !== "" ) {
61- trace . type = "device" ;
62- trace . apm_metrics = { } ;
63- if ( typeof beaconData [ "pt.fp" ] !== "undefined" ) {
64- trace . apm_metrics . first_paint = beaconData [ "pt.fp" ] ;
65- }
66- else if ( typeof beaconData . nt_first_paint !== "undefined" ) {
67- trace . apm_metrics . first_paint = beaconData . nt_first_paint - beaconData [ "rt.tstart" ] ;
68- }
69- if ( typeof beaconData [ "pt.fcp" ] !== "undefined" ) {
70- trace . apm_metrics . first_contentful_paint = beaconData [ "pt.fcp" ] ;
71- }
72- if ( typeof beaconData . nt_domint !== "undefined" ) {
73- trace . apm_metrics . dom_interactive = beaconData . nt_domint - beaconData [ "rt.tstart" ] ;
74- }
75- if ( typeof beaconData . nt_domcontloaded_st !== "undefined" && typeof beaconData . nt_domcontloaded_end !== "undefined" ) {
76- trace . apm_metrics . dom_content_loaded_event_end = beaconData . nt_domcontloaded_end - beaconData . nt_domcontloaded_st ;
77- }
78- if ( typeof beaconData . nt_load_st !== "undefined" && typeof beaconData . nt_load_end !== "undefined" ) {
79- trace . apm_metrics . load_event_end = beaconData . nt_load_end - beaconData . nt_load_st ;
80- }
81- if ( typeof beaconData [ "c.fid" ] !== "undefined" ) {
82- trace . apm_metrics . first_input_delay = beaconData [ "c.fid" ] ;
83- }
84- }
85- else if ( beaconData [ "http.initiator" ] && [ "xhr" , "spa" , "spa_hard" ] . indexOf ( beaconData [ "http.initiator" ] ) !== - 1 ) {
86- var responseTime ; var responsePayloadSize ; var requestPayloadSize ; var
87- responseCode ;
88- responseTime = beaconData . t_resp ;
89- // t_resp - Time taken from the user initiating the request to the first byte of the response. - Added by RT
90- responseCode = ( typeof beaconData [ "http.errno" ] !== "undefined" ) ? beaconData [ "http.errno" ] : 200 ;
61+ function sendDeviceTrace ( beaconData ) {
62+ self . _internals . log ( "[INFO]" , "[Boomerang], collecting device trace info" ) ;
9163
92- try {
93- var restiming = JSON . parse ( beaconData . restiming ) ;
94- var ResourceTimingDecompression = window . ResourceTimingDecompression ;
95- if ( ResourceTimingDecompression && restiming ) {
96- // restiming contains information regarging all the resources that are loaded in any
97- // spa, spa_hard or xhr requests.
98- // xhr requests should ideally have only one entry in the array which is the one for
99- // which the beacon is being sent.
100- // But for spa_hard requests it can contain multiple entries, one for each resource
101- // that is loaded in the application. Example - all images, scripts etc.
102- // ResourceTimingDecompression is not included in the official boomerang library.
103- ResourceTimingDecompression . HOSTNAMES_REVERSED = false ;
104- var decompressedData = ResourceTimingDecompression . decompressResources ( restiming ) ;
105- var currentBeacon = decompressedData . filter ( function ( resource ) {
106- return resource . name === beaconData . u ;
107- } ) ;
64+ // Currently we rely on this information to appear before the paint information. Otherwise device trace will not include these.
65+ if ( typeof beaconData . nt_domint !== "undefined" ) {
66+ deviceTrace . apm_metrics . dom_interactive = beaconData . nt_domint - beaconData [ "rt.tstart" ] ;
67+ }
68+ if ( typeof beaconData . nt_domcontloaded_st !== "undefined" && typeof beaconData . nt_domcontloaded_end !== "undefined" ) {
69+ deviceTrace . apm_metrics . dom_content_loaded_event_end = beaconData . nt_domcontloaded_end - beaconData . nt_domcontloaded_st ;
70+ }
71+ if ( typeof beaconData . nt_load_st !== "undefined" && typeof beaconData . nt_load_end !== "undefined" ) {
72+ deviceTrace . apm_metrics . load_event_end = beaconData . nt_load_end - beaconData . nt_load_st ;
73+ }
74+ if ( typeof beaconData [ "c.fid" ] !== "undefined" ) {
75+ deviceTrace . apm_metrics . first_input_delay = beaconData [ "c.fid" ] ;
76+ }
10877
109- if ( currentBeacon . length ) {
110- responsePayloadSize = currentBeacon [ 0 ] . decodedBodySize ;
111- responseTime = currentBeacon [ 0 ] . duration ? currentBeacon [ 0 ] . duration : responseTime ;
112- // duration - Returns the difference between the resource's responseEnd timestamp and its startTime timestamp - ResourceTiming API
113- }
114- }
115- }
116- catch ( e ) {
117- self . _internals . log ( "[ERROR]" , "Boomerang, Error while using resource timing data decompression" , config ) ;
118- }
78+ // paint timings
79+ if ( typeof beaconData [ "pt.fp" ] !== "undefined" ) {
80+ deviceTrace . apm_metrics . first_paint = beaconData [ "pt.fp" ] ;
81+ }
82+ else if ( typeof beaconData . nt_first_paint !== "undefined" ) {
83+ deviceTrace . apm_metrics . first_paint = beaconData . nt_first_paint - beaconData [ "rt.tstart" ] ;
84+ }
85+ if ( typeof beaconData [ "pt.fcp" ] !== "undefined" ) {
86+ deviceTrace . apm_metrics . first_contentful_paint = beaconData [ "pt.fcp" ] ;
87+ }
11988
120- trace . type = "network" ;
121- trace . apm_metrics = {
122- response_time : responseTime ,
123- response_payload_size : responsePayloadSize ,
124- request_payload_size : requestPayloadSize ,
125- response_code : responseCode
126- } ;
127- }
89+ // first_paint and first_contentful_paint are the two metrics that are MANDATORY! to send the device traces to the server.
90+ if ( deviceTrace . apm_metrics . first_paint && deviceTrace . apm_metrics . first_contentful_paint ) {
91+ self . _internals . log ( "[DEBUG]" , "[Boomerang], Found all the required metrics to send device trace. Recording the trace." ) ;
92+ deviceTrace . name = ( beaconData . u + "" ) . split ( "//" ) . pop ( ) . split ( "?" ) [ 0 ] ;
93+ deviceTrace . stz = beaconData [ "rt.tstart" ] ;
94+ deviceTrace . etz = beaconData [ "rt.end" ] ;
95+ self . report_trace ( deviceTrace ) ;
96+ deviceTrace . apm_metrics = { } ; // reset the device trace
97+ }
98+ }
12899
129- if ( trace . type ) {
130- trace . name = ( beaconData . u + "" ) . split ( "//" ) . pop ( ) . split ( "?" ) [ 0 ] ;
131- trace . stz = beaconData [ "rt.tstart" ] ;
132- trace . etz = beaconData [ "rt.end" ] ;
133- self . report_trace ( trace ) ;
100+ /**
101+ *
102+ * @param {Object } beaconData - Boomerang beacon data
103+ */
104+ function sendNetworkTrace ( beaconData ) {
105+ self . _internals . log ( "[INFO]" , "[Boomerang], collecting network trace info" ) ;
106+ var trace = { } ;
107+ if ( beaconData [ "http.initiator" ] && [ "xhr" , "spa" , "spa_hard" ] . indexOf ( beaconData [ "http.initiator" ] ) !== - 1 ) {
108+ var responseTime ; var responsePayloadSize ; var requestPayloadSize ; var
109+ responseCode ;
110+ responseTime = beaconData . t_resp ;
111+ // t_resp - Time taken from the user initiating the request to the first byte of the response. - Added by RT
112+ responseCode = ( typeof beaconData [ "http.errno" ] !== "undefined" ) ? beaconData [ "http.errno" ] : 200 ;
113+
114+ try {
115+ var restiming = JSON . parse ( beaconData . restiming ) ;
116+ var ResourceTimingDecompression = window . ResourceTimingDecompression ;
117+ if ( ResourceTimingDecompression && restiming ) {
118+ // restiming contains information regarging all the resources that are loaded in any
119+ // spa, spa_hard or xhr requests.
120+ // xhr requests should ideally have only one entry in the array which is the one for
121+ // which the beacon is being sent.
122+ // But for spa_hard requests it can contain multiple entries, one for each resource
123+ // that is loaded in the application. Example - all images, scripts etc.
124+ // ResourceTimingDecompression is not included in the official boomerang library.
125+ ResourceTimingDecompression . HOSTNAMES_REVERSED = false ;
126+ var decompressedData = ResourceTimingDecompression . decompressResources ( restiming ) ;
127+ var currentBeacon = decompressedData . filter ( function ( resource ) {
128+ return resource . name === beaconData . u ;
129+ } ) ;
130+
131+ if ( currentBeacon . length ) {
132+ responsePayloadSize = currentBeacon [ 0 ] . decodedBodySize ;
133+ responseTime = currentBeacon [ 0 ] . duration ? currentBeacon [ 0 ] . duration : responseTime ;
134+ // duration - Returns the difference between the resource's responseEnd timestamp and its startTime timestamp - ResourceTiming API
135+ }
134136 }
137+ }
138+ catch ( e ) {
139+ self . _internals . log ( "[ERROR]" , "[Boomerang], Error while using resource timing data decompression" , config ) ;
140+ }
141+
142+ trace . type = "network" ;
143+ trace . apm_metrics = {
144+ response_time : responseTime ,
145+ response_payload_size : responsePayloadSize ,
146+ request_payload_size : requestPayloadSize ,
147+ response_code : responseCode
148+ } ;
149+ trace . name = ( beaconData . u + "" ) . split ( "//" ) . pop ( ) . split ( "?" ) [ 0 ] ;
150+ trace . stz = beaconData [ "rt.tstart" ] ;
151+ trace . etz = beaconData [ "rt.end" ] ;
152+ self . _internals . log ( "[DEBUG]" , "[Boomerang], Found all the required metrics to send network trace. Recording the trace." ) ;
153+ self . report_trace ( trace ) ;
154+ }
155+ }
156+
157+ /**
158+ * Initialize Boomerang
159+ * @param {Object } BOOMR - Boomerang object
160+ */
161+ function initBoomerang ( BOOMR ) {
162+ if ( BOOMR && ! initedBoomr ) {
163+ BOOMR . subscribe ( "before_beacon" , function ( beaconData ) {
164+ self . _internals . log ( "[INFO]" , "[Boomerang], before_beacon:" , JSON . stringify ( beaconData , null , 2 ) ) ;
165+ sendDeviceTrace ( beaconData ) ;
166+ sendNetworkTrace ( beaconData ) ;
135167 } ) ;
136168
137169 BOOMR . xhr_excludes = BOOMR . xhr_excludes || { } ;
@@ -143,17 +175,17 @@ Plugin being used - RT, AutoXHR, Continuity, NavigationTiming, ResourceTiming
143175 BOOMR . t_end = new Date ( ) . getTime ( ) ;
144176 Countly . BOOMR = BOOMR ;
145177 initedBoomr = true ;
146- self . _internals . log ( "[INFO]" , "Boomerang initiated:" , config ) ;
178+ self . _internals . log ( "[INFO]" , "[ Boomerang] inited with config:[" + JSON . stringify ( config ) + "]" ) ;
147179 }
148180 else {
149- self . _internals . log ( "[WARNING]" , "Boomerang called without its instance or was already initialized" ) ;
181+ self . _internals . log ( "[WARNING]" , "[ Boomerang] called without its instance or was already initialized" ) ;
150182 }
151183 }
152184 if ( window . BOOMR ) {
153185 initBoomerang ( window . BOOMR ) ;
154186 }
155187 else {
156- self . _internals . log ( "[WARNING]" , "Boomerang not yet loaded, waiting for it to load" ) ;
188+ self . _internals . log ( "[WARNING]" , "[ Boomerang] not yet loaded, waiting for it to load" ) ;
157189 // Modern browsers
158190 if ( document . addEventListener ) {
159191 document . addEventListener ( "onBoomerangLoaded" , function ( e ) {
0 commit comments