Skip to content

Commit 036a26e

Browse files
✨[PANA-3818] Add telemetry for DOM serialization performance (#3792)
1 parent e9aacc4 commit 036a26e

15 files changed

+128
-32
lines changed

packages/rum/src/domain/record/serialization/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,5 @@ export type { SerializationContext } from './serialization.types'
1010
export { serializeDocument } from './serializeDocument'
1111
export { serializeNodeWithId } from './serializeNode'
1212
export { serializeAttribute } from './serializeAttribute'
13-
export {
14-
createSerializationStats,
15-
updateCssTextSerializationStats,
16-
aggregateSerializationStats,
17-
} from './serializationStats'
13+
export { createSerializationStats, updateSerializationStats, aggregateSerializationStats } from './serializationStats'
1814
export type { SerializationMetric, SerializationStats } from './serializationStats'
Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,59 @@
1-
import {
2-
aggregateSerializationStats,
3-
createSerializationStats,
4-
updateCssTextSerializationStats,
5-
} from './serializationStats'
1+
import { aggregateSerializationStats, createSerializationStats, updateSerializationStats } from './serializationStats'
62

73
describe('serializationStats', () => {
84
describe('stats for _cssText', () => {
95
it('can be updated', () => {
106
const stats = createSerializationStats()
117

12-
updateCssTextSerializationStats(stats, 'div { color: blue; }'.length)
8+
updateSerializationStats(stats, 'cssText', 'div { color: blue; }'.length)
139
expect(stats.cssText.count).toBe(1)
1410
expect(stats.cssText.max).toBe(20)
1511
expect(stats.cssText.sum).toBe(20)
1612

17-
updateCssTextSerializationStats(stats, 'span { background-color: red; }'.length)
13+
updateSerializationStats(stats, 'cssText', 'span { background-color: red; }'.length)
1814
expect(stats.cssText.count).toBe(2)
1915
expect(stats.cssText.max).toBe(31)
2016
expect(stats.cssText.sum).toBe(51)
2117
})
2218
})
2319

20+
describe('stats for serialization duration', () => {
21+
it('can be updated', () => {
22+
const stats = createSerializationStats()
23+
24+
updateSerializationStats(stats, 'serializationDuration', 30)
25+
expect(stats.serializationDuration.count).toBe(1)
26+
expect(stats.serializationDuration.max).toBe(30)
27+
expect(stats.serializationDuration.sum).toBe(30)
28+
29+
updateSerializationStats(stats, 'serializationDuration', 60)
30+
expect(stats.serializationDuration.count).toBe(2)
31+
expect(stats.serializationDuration.max).toBe(60)
32+
expect(stats.serializationDuration.sum).toBe(90)
33+
})
34+
})
35+
2436
it('can be aggregated', () => {
2537
const aggregateStats = createSerializationStats()
2638

2739
const stats1 = createSerializationStats()
28-
updateCssTextSerializationStats(stats1, 'div { color: blue; }'.length)
29-
updateCssTextSerializationStats(stats1, 'span { background-color: red; }'.length)
40+
updateSerializationStats(stats1, 'cssText', 'div { color: blue; }'.length)
41+
updateSerializationStats(stats1, 'serializationDuration', 16)
42+
updateSerializationStats(stats1, 'cssText', 'span { background-color: red; }'.length)
43+
updateSerializationStats(stats1, 'serializationDuration', 32)
3044
aggregateSerializationStats(aggregateStats, stats1)
3145

3246
const stats2 = createSerializationStats()
33-
updateCssTextSerializationStats(stats2, 'p { width: 100%; }'.length)
47+
updateSerializationStats(stats2, 'cssText', 'p { width: 100%; }'.length)
48+
updateSerializationStats(stats2, 'serializationDuration', 18)
49+
updateSerializationStats(stats2, 'serializationDuration', 9)
3450
aggregateSerializationStats(aggregateStats, stats2)
3551

3652
expect(aggregateStats.cssText.count).toBe(3)
3753
expect(aggregateStats.cssText.max).toBe(31)
3854
expect(aggregateStats.cssText.sum).toBe(69)
55+
expect(aggregateStats.serializationDuration.count).toBe(4)
56+
expect(aggregateStats.serializationDuration.max).toBe(32)
57+
expect(aggregateStats.serializationDuration.sum).toBe(75)
3958
})
4059
})

packages/rum/src/domain/record/serialization/serializationStats.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface SerializationMetric {
66

77
export interface SerializationStats {
88
cssText: SerializationMetric
9+
serializationDuration: SerializationMetric
910
}
1011

1112
export function createSerializationStats(): SerializationStats {
@@ -15,17 +16,28 @@ export function createSerializationStats(): SerializationStats {
1516
max: 0,
1617
sum: 0,
1718
},
19+
serializationDuration: {
20+
count: 0,
21+
max: 0,
22+
sum: 0,
23+
},
1824
}
1925
}
2026

21-
export function updateCssTextSerializationStats(stats: SerializationStats, value: number): void {
22-
stats.cssText.count += 1
23-
stats.cssText.max = Math.max(stats.cssText.max, value)
24-
stats.cssText.sum += value
27+
export function updateSerializationStats(
28+
stats: SerializationStats,
29+
metric: keyof SerializationStats,
30+
value: number
31+
): void {
32+
stats[metric].count += 1
33+
stats[metric].max = Math.max(stats[metric].max, value)
34+
stats[metric].sum += value
2535
}
2636

2737
export function aggregateSerializationStats(aggregateStats: SerializationStats, stats: SerializationStats) {
28-
aggregateStats.cssText.count += stats.cssText.count
29-
aggregateStats.cssText.max = Math.max(aggregateStats.cssText.max, stats.cssText.max)
30-
aggregateStats.cssText.sum += stats.cssText.sum
38+
for (const metric of ['cssText', 'serializationDuration'] as const) {
39+
aggregateStats[metric].count += stats[metric].count
40+
aggregateStats[metric].max = Math.max(aggregateStats[metric].max, stats[metric].max)
41+
aggregateStats[metric].sum += stats[metric].sum
42+
}
3143
}

packages/rum/src/domain/record/serialization/serializeAttributes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getElementInputValue, switchToAbsoluteUrl, getValidTagName } from './se
44
import type { SerializeOptions } from './serialization.types'
55
import { SerializationContextStatus } from './serialization.types'
66
import { serializeAttribute } from './serializeAttribute'
7-
import { updateCssTextSerializationStats } from './serializationStats'
7+
import { updateSerializationStats } from './serializationStats'
88

99
export function serializeAttributes(
1010
element: Element,
@@ -53,7 +53,7 @@ export function serializeAttributes(
5353
const stylesheet = Array.from(doc.styleSheets).find((s) => s.href === (element as HTMLLinkElement).href)
5454
const cssText = getCssRulesString(stylesheet)
5555
if (cssText && stylesheet) {
56-
updateCssTextSerializationStats(options.serializationContext.serializationStats, cssText.length)
56+
updateSerializationStats(options.serializationContext.serializationStats, 'cssText', cssText.length)
5757
safeAttrs._cssText = cssText
5858
}
5959
}
@@ -62,7 +62,7 @@ export function serializeAttributes(
6262
if (tagName === 'style' && (element as HTMLStyleElement).sheet) {
6363
const cssText = getCssRulesString((element as HTMLStyleElement).sheet)
6464
if (cssText) {
65-
updateCssTextSerializationStats(options.serializationContext.serializationStats, cssText.length)
65+
updateSerializationStats(options.serializationContext.serializationStats, 'cssText', cssText.length)
6666
safeAttrs._cssText = cssText
6767
}
6868
}
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1+
import { elapsed, timeStampNow } from '@datadog/browser-core'
12
import type { RumConfiguration } from '@datadog/browser-rum-core'
23
import type { SerializedNodeWithId } from '../../../types'
34
import type { SerializationContext } from './serialization.types'
45
import { serializeNodeWithId } from './serializeNode'
6+
import { updateSerializationStats } from './serializationStats'
57

68
export function serializeDocument(
79
document: Document,
810
configuration: RumConfiguration,
911
serializationContext: SerializationContext
1012
): SerializedNodeWithId {
11-
// We are sure that Documents are never ignored, so this function never returns null
12-
return serializeNodeWithId(document, {
13+
const serializationStart = timeStampNow()
14+
const serializedNode = serializeNodeWithId(document, {
1315
serializationContext,
1416
parentNodePrivacyLevel: configuration.defaultPrivacyLevel,
1517
configuration,
16-
})!
18+
})
19+
updateSerializationStats(
20+
serializationContext.serializationStats,
21+
'serializationDuration',
22+
elapsed(serializationStart, timeStampNow())
23+
)
24+
25+
// We are sure that Documents are never ignored, so this function never returns null
26+
return serializedNode!
1727
}

packages/rum/src/domain/record/serialization/serializeNode.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ describe('serializeNodeWithId', () => {
550550
})
551551
expect(options.serializationContext.serializationStats).toEqual({
552552
cssText: { count: 1, max: 21, sum: 21 },
553+
serializationDuration: jasmine.anything(),
553554
})
554555
})
555556

@@ -567,6 +568,7 @@ describe('serializeNodeWithId', () => {
567568
})
568569
expect(options.serializationContext.serializationStats).toEqual({
569570
cssText: { count: 1, max: 21, sum: 21 },
571+
serializationDuration: jasmine.anything(),
570572
})
571573
})
572574

@@ -585,6 +587,7 @@ describe('serializeNodeWithId', () => {
585587
})
586588
expect(options.serializationContext.serializationStats).toEqual({
587589
cssText: { count: 1, max: 41, sum: 41 },
590+
serializationDuration: jasmine.anything(),
588591
})
589592
})
590593

@@ -625,6 +628,7 @@ describe('serializeNodeWithId', () => {
625628
})
626629
expect(options.serializationContext.serializationStats).toEqual({
627630
cssText: { count: 2, max: 33, sum: 54 },
631+
serializationDuration: jasmine.anything(),
628632
})
629633
})
630634
})
@@ -652,6 +656,7 @@ describe('serializeNodeWithId', () => {
652656
})
653657
expect(options.serializationContext.serializationStats).toEqual({
654658
cssText: { count: 0, max: 0, sum: 0 },
659+
serializationDuration: jasmine.anything(),
655660
})
656661
})
657662

@@ -685,6 +690,7 @@ describe('serializeNodeWithId', () => {
685690
})
686691
expect(options.serializationContext.serializationStats).toEqual({
687692
cssText: { count: 1, max: 21, sum: 21 },
693+
serializationDuration: jasmine.anything(),
688694
})
689695
})
690696

@@ -720,6 +726,7 @@ describe('serializeNodeWithId', () => {
720726
})
721727
expect(options.serializationContext.serializationStats).toEqual({
722728
cssText: { count: 0, max: 0, sum: 0 },
729+
serializationDuration: jasmine.anything(),
723730
})
724731
})
725732
})

packages/rum/src/domain/record/startFullSnapshots.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ describe('startFullSnapshots', () => {
108108
const fullSnapshotEmits = emitCallback.calls.allArgs().filter((args) => args[0].type === RecordType.FullSnapshot)
109109
expect(fullSnapshotEmits[0][1]).toEqual({
110110
cssText: { count: 1, max: 21, sum: 21 },
111+
serializationDuration: jasmine.anything(),
111112
})
112113
})
113114
})

packages/rum/src/domain/record/trackers/trackMutation.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ describe('trackMutation', () => {
119119

120120
expect(mutationCallbackSpy.calls.mostRecent().args[1]).toEqual({
121121
cssText: { count: 1, max: 21, sum: 21 },
122+
serializationDuration: jasmine.anything(),
122123
})
123124
})
124125

packages/rum/src/domain/record/trackers/trackMutation.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { monitor, noop } from '@datadog/browser-core'
1+
import { elapsed, monitor, noop, timeStampNow } from '@datadog/browser-core'
22
import type {
33
RumConfiguration,
44
NodePrivacyLevelCache,
@@ -35,6 +35,7 @@ import {
3535
SerializationContextStatus,
3636
serializeAttribute,
3737
createSerializationStats,
38+
updateSerializationStats,
3839
} from '../serialization'
3940
import { createMutationBatch } from '../mutationBatch'
4041
import type { ShadowRootCallBack, ShadowRootsController } from '../shadowRootsController'
@@ -233,12 +234,14 @@ function processChildListMutations(
233234
continue
234235
}
235236

237+
const serializationStart = timeStampNow()
236238
const serializedNode = serializeNodeWithId(node, {
237239
serializedNodeIds,
238240
parentNodePrivacyLevel,
239241
serializationContext,
240242
configuration,
241243
})
244+
updateSerializationStats(serializationStats, 'serializationDuration', elapsed(serializationStart, timeStampNow()))
242245
if (!serializedNode) {
243246
continue
244247
}

packages/rum/src/domain/segmentCollection/buildReplayPayload.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ describe('buildReplayPayload', () => {
2525
max: 1000,
2626
sum: 1500,
2727
},
28+
serializationDuration: {
29+
count: 3,
30+
max: 76,
31+
sum: 103,
32+
},
2833
}
2934
const METADATA_AND_SEGMENT_SIZES = {
3035
...METADATA,
@@ -63,5 +68,6 @@ describe('buildReplayPayload', () => {
6368
expect(payload.isFullSnapshot).toBe(false)
6469
expect(payload.rawSize).toBe(13)
6570
expect(payload.recordCount).toBe(10)
71+
expect(payload.serializationDuration).toEqual({ count: 3, max: 76, sum: 103 })
6672
})
6773
})

0 commit comments

Comments
 (0)