Skip to content

Commit 19a6369

Browse files
[web vitals] add _page_url field to web vital logs and report all performance timing entries (#898)
* add url to web vitals * add to changelog * strip query param and hash * report entries as array * update changelog * more changelog
1 parent ae30761 commit 19a6369

File tree

5 files changed

+23
-53
lines changed

5 files changed

+23
-53
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121

2222
**Added**
2323

24-
- Nothing yet!
24+
- Added the `_page_url` field to WebVital events sent from the WebView bridge to provide more context about the page associated with the metric.
2525

2626
**Changed**
2727

28-
- Nothing yet!
28+
- Return all performance timing entries for WebVital events instead of just the first.
2929

3030
**Fixed**
3131

platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/webview/WebViewBridgeMessageHandler.kt

Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ internal class WebViewBridgeMessageHandler(
186186
metric.id?.let { put("_metric_id", it) }
187187
metric.navigationType?.let { put("_navigation_type", it) }
188188
parentSpanId?.let { put("_span_parent_id", it) }
189+
msg.url?.let { put("_page_url", it) }
189190
put("_source", "webview")
190191
}
191192

@@ -222,13 +223,9 @@ internal class WebViewBridgeMessageHandler(
222223
val fields = commonFields.toMutableMap()
223224
fields["_metric"] = "LCP"
224225

225-
// Extract LCP-specific entry data if available
226-
metric.entries?.firstOrNull()?.let { entry ->
227-
entry.element?.let { fields["_element"] = it }
228-
entry.url?.let { fields["_url"] = it }
229-
entry.size?.let { fields["_size"] = it.toString() }
230-
entry.renderTime?.let { fields["_render_time"] = it.toString() }
231-
entry.loadTime?.let { fields["_load_time"] = it.toString() }
226+
// Include entries array as JSON
227+
metric.entries?.takeIf { it.isNotEmpty() }?.let { entries ->
228+
fields["_entries"] = gson.toJson(entries)
232229
}
233230

234231
logWebVitalDurationSpan(timestamp, value, level, fields, parentSpanId)
@@ -250,11 +247,9 @@ internal class WebViewBridgeMessageHandler(
250247
val fields = commonFields.toMutableMap()
251248
fields["_metric"] = "FCP"
252249

253-
// Extract FCP-specific entry data if available (PerformancePaintTiming)
254-
metric.entries?.firstOrNull()?.let { entry ->
255-
entry.name?.let { fields["_paint_type"] = it }
256-
entry.startTime?.let { fields["_start_time"] = it.toString() }
257-
entry.entryType?.let { fields["_entry_type"] = it }
250+
// Include entries array as JSON
251+
metric.entries?.takeIf { it.isNotEmpty() }?.let { entries ->
252+
fields["_entries"] = gson.toJson(entries)
258253
}
259254

260255
logWebVitalDurationSpan(timestamp, value, level, fields, parentSpanId)
@@ -276,15 +271,9 @@ internal class WebViewBridgeMessageHandler(
276271
val fields = commonFields.toMutableMap()
277272
fields["_metric"] = "TTFB"
278273

279-
// Extract TTFB-specific entry data if available (PerformanceNavigationTiming)
280-
metric.entries?.firstOrNull()?.let { entry ->
281-
entry.domainLookupStart?.let { fields["_dns_start"] = it.toString() }
282-
entry.domainLookupEnd?.let { fields["_dns_end"] = it.toString() }
283-
entry.connectStart?.let { fields["_connect_start"] = it.toString() }
284-
entry.connectEnd?.let { fields["_connect_end"] = it.toString() }
285-
entry.secureConnectionStart?.let { fields["_tls_start"] = it.toString() }
286-
entry.requestStart?.let { fields["_request_start"] = it.toString() }
287-
entry.responseStart?.let { fields["_response_start"] = it.toString() }
274+
// Include entries array as JSON
275+
metric.entries?.takeIf { it.isNotEmpty() }?.let { entries ->
276+
fields["_entries"] = gson.toJson(entries)
288277
}
289278

290279
logWebVitalDurationSpan(timestamp, value, level, fields, parentSpanId)
@@ -306,14 +295,9 @@ internal class WebViewBridgeMessageHandler(
306295
val fields = commonFields.toMutableMap()
307296
fields["_metric"] = "INP"
308297

309-
// Extract INP-specific entry data if available
310-
metric.entries?.firstOrNull()?.let { entry ->
311-
entry.name?.let { fields["_event_type"] = it }
312-
entry.startTime?.let { fields["_interaction_time"] = it.toString() }
313-
entry.processingStart?.let { fields["_processing_start"] = it.toString() }
314-
entry.processingEnd?.let { fields["_processing_end"] = it.toString() }
315-
entry.duration?.let { fields["_duration"] = it.toString() }
316-
entry.interactionId?.let { fields["_interaction_id"] = it.toString() }
298+
// Include entries array as JSON
299+
metric.entries?.takeIf { it.isNotEmpty() }?.let { entries ->
300+
fields["_entries"] = gson.toJson(entries)
317301
}
318302

319303
logWebVitalDurationSpan(timestamp, value, level, fields, parentSpanId)
@@ -332,27 +316,9 @@ internal class WebViewBridgeMessageHandler(
332316
val fields = commonFields.toMutableMap()
333317
fields["_metric"] = "CLS"
334318

335-
// Extract CLS-specific data from entries
336-
val entries = metric.entries
337-
if (!entries.isNullOrEmpty()) {
338-
// Find the largest shift
339-
var largestShiftValue = 0.0
340-
var largestShiftTime = 0.0
341-
342-
for (entry in entries) {
343-
val shiftValue = entry.value ?: 0.0
344-
if (shiftValue > largestShiftValue) {
345-
largestShiftValue = shiftValue
346-
largestShiftTime = entry.startTime ?: 0.0
347-
}
348-
}
349-
350-
if (largestShiftValue > 0) {
351-
fields["_largest_shift_value"] = largestShiftValue.toString()
352-
fields["_largest_shift_time"] = largestShiftTime.toString()
353-
}
354-
355-
fields["_shift_count"] = entries.size.toString()
319+
// Include entries array as JSON
320+
metric.entries?.takeIf { it.isNotEmpty() }?.let { entries ->
321+
fields["_entries"] = gson.toJson(entries)
356322
}
357323

358324
logger.logInternal(LogType.UX, level, fields.toFields()) {

0 commit comments

Comments
 (0)