Skip to content

Commit 253083b

Browse files
authored
Revert "feat!: capture all W3C fields in ResourceEvents (#489)" (#617)
1 parent ade3e4d commit 253083b

File tree

15 files changed

+431
-426
lines changed

15 files changed

+431
-426
lines changed

src/__integ__/customEvents.test.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ fixture('Custom Events API & Plugin').page(
1818
const removeUnwantedEvents = (json: any) => {
1919
const newEventsList = json.RumEvents.filter(
2020
(e) =>
21-
/custom_event_api/.test(e.type) ||
22-
/custom_event_plugin/.test(e.type)
21+
/(custom_event_api)/.test(e.type) ||
22+
/(custom_event_plugin)/.test(e.type)
2323
);
2424

2525
json.RumEvents = newEventsList;
@@ -136,7 +136,6 @@ test('when a plugin calls recordEvent x times then event is recorded x times', a
136136
}
137137
await t
138138
.click(dispatch)
139-
.wait(100)
140139
.expect(REQUEST_BODY.textContent)
141140
.contains('BatchId');
142141

@@ -168,14 +167,12 @@ test('when plugin recordEvent has empty event_data then RumEvent details is empt
168167
await t
169168
.click(pluginRecordEmptyEvent)
170169
.click(dispatch)
171-
.wait(100)
172170
.expect(REQUEST_BODY.textContent)
173171
.contains('BatchId');
174172

175173
const json = removeUnwantedEvents(
176174
JSON.parse(await REQUEST_BODY.textContent)
177175
);
178-
179176
await t
180177
.expect(json.RumEvents.length)
181178
.eql(1)

src/event-cache/EventCache.ts

-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ export class EventCache {
9494
* If the session is not being recorded, the event will not be recorded.
9595
*
9696
* @param type The event schema.
97-
* @param eventData The RUM Event to be dispatched to PutRumEvents
9897
*/
9998
public recordEvent = (type: string, eventData: object) => {
10099
if (!this.enabled) {
@@ -210,7 +209,6 @@ export class EventCache {
210209
* Add an event to the cache.
211210
*
212211
* @param type The event schema.
213-
* @param eventData The RUM Event to be dispatched to PutRumEvents
214212
*/
215213
private addRecordToCache = (type: string, eventData: object) => {
216214
if (!this.enabled) {
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,88 @@
11
{
2-
"$id": "com.amazon.rum.performance_resource_timing",
2+
"$id": "com.amazon.rum.performance_resource_event",
33
"$schema": "https://json-schema.org/draft/2020-12/schema",
4-
"title": "PerformanceResourceTimingEvent",
4+
"title": "ResourceEvent",
55
"type": "object",
66
"properties": {
7-
"name": {
7+
"version": {
8+
"const": "1.0.0",
9+
"type": "string",
10+
"description": "Schema version."
11+
},
12+
"targetUrl": {
13+
"description": "Page URL",
814
"type": "string"
915
},
10-
"entryType": {
11-
"const": "resource",
16+
"initiatorType": {
1217
"type": "string"
1318
},
1419
"startTime": {
1520
"type": "number"
1621
},
17-
"duration": {
22+
"redirectStart": {
1823
"type": "number"
1924
},
20-
"connectStart": {
25+
"redirectTime": {
2126
"type": "number"
2227
},
23-
"connectEnd": {
28+
"workerStart": {
2429
"type": "number"
2530
},
26-
"decodedBodySize": {
31+
"workerTime": {
2732
"type": "number"
2833
},
29-
"domainLookupEnd": {
34+
"fetchStart": {
3035
"type": "number"
3136
},
3237
"domainLookupStart": {
3338
"type": "number"
3439
},
35-
"encodedBodySize": {
40+
"dns": {
3641
"type": "number"
3742
},
38-
"fetchStart": {
39-
"type": "number"
40-
},
41-
"initiatorType": {
42-
"type": "string",
43-
"enum": [
44-
"audio",
45-
"beacon",
46-
"body",
47-
"css",
48-
"early-hint",
49-
"embed",
50-
"fetch",
51-
"frame",
52-
"iframe",
53-
"icon",
54-
"image",
55-
"img",
56-
"input",
57-
"link",
58-
"navigation",
59-
"object",
60-
"ping",
61-
"script",
62-
"track",
63-
"video",
64-
"xmlhttprequest",
65-
"other"
66-
]
67-
},
6843
"nextHopProtocol": {
6944
"type": "string"
7045
},
71-
"redirectEnd": {
46+
"connectStart": {
47+
"type": "number"
48+
},
49+
"connect": {
7250
"type": "number"
7351
},
74-
"redirectStart": {
52+
"secureConnectionStart": {
7553
"type": "number"
7654
},
77-
"renderBlockingStatus": {
78-
"type": "string"
55+
"tlsTime": {
56+
"type": "number"
7957
},
8058
"requestStart": {
8159
"type": "number"
8260
},
83-
"responseEnd": {
61+
"timeToFirstByte": {
8462
"type": "number"
8563
},
8664
"responseStart": {
8765
"type": "number"
8866
},
89-
"secureConnectionStart": {
67+
"responseTime": {
68+
"type": "number"
69+
},
70+
"duration": {
71+
"type": "number"
72+
},
73+
"headerSize": {
9074
"type": "number"
9175
},
9276
"transferSize": {
9377
"type": "number"
9478
},
95-
"workerStart": {
79+
"compressionRatio": {
9680
"type": "number"
81+
},
82+
"fileType": {
83+
"type": "string"
9784
}
9885
},
9986
"additionalProperties": false,
100-
"required": ["duration", "entryType", "startTime"]
87+
"required": ["version", "initiatorType", "duration", "fileType"]
10188
}

src/orchestration/__tests__/Orchestration.test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ jest.mock('../../utils/common-utils', () => {
2929
__esModule: true,
3030
...originalModule,
3131
isLCPSupported: jest.fn().mockReturnValue(true),
32-
isNavigationSupported: jest.fn().mockReturnValue(true),
33-
isResourceSupported: jest.fn().mockReturnValue(true)
32+
isNavigationSupported: jest.fn().mockReturnValue(true)
3433
};
3534
});
3635

src/plugins/event-plugins/ResourcePlugin.ts

+81-72
Original file line numberDiff line numberDiff line change
@@ -2,119 +2,128 @@ import { InternalPlugin } from '../InternalPlugin';
22
import {
33
getResourceFileType,
44
isPutRumEventsCall,
5-
isResourceSupported
5+
shuffle
66
} from '../../utils/common-utils';
7+
import { ResourceEvent } from '../../events/resource-event';
78
import { PERFORMANCE_RESOURCE_EVENT_TYPE } from '../utils/constant';
8-
import { PerformanceResourceTimingEvent } from '../../events/performance-resource-timing';
99
import {
1010
defaultPerformancePluginConfig,
11-
PerformancePluginConfig,
12-
PerformanceResourceTimingPolyfill
11+
PerformancePluginConfig
1312
} from '../utils/performance-utils';
1413

1514
export const RESOURCE_EVENT_PLUGIN_ID = 'resource';
15+
1616
const RESOURCE = 'resource';
1717

1818
/**
1919
* This plugin records resource performance timing events generated during every page load/re-load.
2020
*/
2121
export class ResourcePlugin extends InternalPlugin {
2222
private config: PerformancePluginConfig;
23-
private resourceObserver?: PerformanceObserver;
24-
private sampleCount: number;
23+
private resourceObserver: PerformanceObserver;
24+
private eventCount: number;
2525

2626
constructor(config?: Partial<PerformancePluginConfig>) {
2727
super(RESOURCE_EVENT_PLUGIN_ID);
2828
this.config = { ...defaultPerformancePluginConfig, ...config };
29-
this.sampleCount = 0;
30-
this.resourceObserver = isResourceSupported()
31-
? new PerformanceObserver(this.performanceEntryHandler)
32-
: undefined;
29+
this.eventCount = 0;
30+
this.resourceObserver = new PerformanceObserver(
31+
this.performanceEntryHandler
32+
);
3333
}
3434

3535
enable(): void {
3636
if (this.enabled) {
3737
return;
3838
}
3939
this.enabled = true;
40-
this.observe();
40+
this.resourceObserver.observe({
41+
type: RESOURCE,
42+
buffered: true
43+
});
4144
}
4245

4346
disable(): void {
4447
if (!this.enabled) {
4548
return;
4649
}
4750
this.enabled = false;
48-
this.resourceObserver?.disconnect();
49-
}
50-
51-
private observe() {
52-
// We need to set `buffered: true`, so the observer also records past
53-
// resource entries. However, there is a limited buffer size, so we may
54-
// not be able to collect all resource entries.
55-
this.resourceObserver?.observe({
56-
type: RESOURCE,
57-
buffered: true
58-
});
51+
this.resourceObserver.disconnect();
5952
}
6053

6154
performanceEntryHandler = (list: PerformanceObserverEntryList): void => {
62-
for (const entry of list.getEntries()) {
63-
const e = entry as PerformanceResourceTimingPolyfill;
64-
if (
65-
this.config.ignore(e) ||
66-
// Ignore calls to PutRumEvents (i.e., the CloudWatch RUM data
67-
// plane), otherwise we end up in an infinite loop of recording
68-
// PutRumEvents.
69-
isPutRumEventsCall(e.name, this.context.config.endpointUrl.host)
70-
) {
71-
continue;
72-
}
55+
this.recordPerformanceEntries(list.getEntries());
56+
};
7357

74-
// Sampling logic
75-
const fileType = getResourceFileType(e.initiatorType);
76-
if (this.config.recordAllTypes.includes(fileType)) {
77-
// Always record
78-
this.recordResourceEvent(e);
79-
} else if (
80-
this.sampleCount < this.config.eventLimit &&
81-
this.config.sampleTypes.includes(fileType)
82-
) {
83-
// Only sample first N
84-
this.recordResourceEvent(e);
85-
this.sampleCount++;
86-
}
58+
recordPerformanceEntries = (list: PerformanceEntryList) => {
59+
const recordAll: PerformanceEntry[] = [];
60+
const sample: PerformanceEntry[] = [];
61+
62+
list.filter((e) => e.entryType === RESOURCE)
63+
.filter((e) => !this.config.ignore(e))
64+
.forEach((event) => {
65+
const { name, initiatorType } =
66+
event as PerformanceResourceTiming;
67+
const type = getResourceFileType(name, initiatorType);
68+
if (this.config.recordAllTypes.includes(type)) {
69+
recordAll.push(event);
70+
} else if (this.config.sampleTypes.includes(type)) {
71+
sample.push(event);
72+
}
73+
});
74+
75+
// Record all events for resources in recordAllTypes
76+
recordAll.forEach((r) =>
77+
this.recordResourceEvent(r as PerformanceResourceTiming)
78+
);
79+
80+
// Record events from resources in sample until we hit the resource limit
81+
shuffle(sample);
82+
while (sample.length > 0 && this.eventCount < this.config.eventLimit) {
83+
this.recordResourceEvent(sample.pop() as PerformanceResourceTiming);
84+
this.eventCount++;
8785
}
8886
};
8987

90-
recordResourceEvent = (e: PerformanceResourceTimingPolyfill): void => {
91-
this.context?.record(PERFORMANCE_RESOURCE_EVENT_TYPE, {
92-
name: this.context.config.recordResourceUrl ? e.name : undefined,
93-
entryType: RESOURCE,
94-
startTime: e.startTime,
95-
duration: e.duration,
96-
connectStart: e.connectStart,
97-
connectEnd: e.connectEnd,
98-
decodedBodySize: e.decodedBodySize,
99-
domainLookupEnd: e.domainLookupEnd,
100-
domainLookupStart: e.domainLookupStart,
101-
fetchStart: e.fetchStart,
102-
encodedBodySize: e.encodedBodySize,
103-
initiatorType: e.initiatorType,
104-
nextHopProtocol: e.nextHopProtocol,
105-
redirectEnd: e.redirectEnd,
106-
redirectStart: e.redirectStart,
107-
renderBlockingStatus: e.renderBlockingStatus,
108-
requestStart: e.requestStart,
109-
responseEnd: e.responseEnd,
110-
responseStart: e.responseStart,
111-
secureConnectionStart: e.secureConnectionStart,
112-
transferSize: e.transferSize,
113-
workerStart: e.workerStart
114-
} as PerformanceResourceTimingEvent);
88+
recordResourceEvent = ({
89+
name,
90+
startTime,
91+
initiatorType,
92+
duration,
93+
transferSize
94+
}: PerformanceResourceTiming): void => {
95+
if (
96+
isPutRumEventsCall(name, this.context.config.endpointUrl.hostname)
97+
) {
98+
// Ignore calls to PutRumEvents (i.e., the CloudWatch RUM data
99+
// plane), otherwise we end up in an infinite loop of recording
100+
// PutRumEvents.
101+
return;
102+
}
103+
104+
if (this.context?.record) {
105+
const eventData: ResourceEvent = {
106+
version: '1.0.0',
107+
initiatorType,
108+
startTime,
109+
duration,
110+
fileType: getResourceFileType(name, initiatorType),
111+
transferSize
112+
};
113+
if (this.context.config.recordResourceUrl) {
114+
eventData.targetUrl = name;
115+
}
116+
this.context.record(PERFORMANCE_RESOURCE_EVENT_TYPE, eventData);
117+
}
115118
};
116119

117120
protected onload(): void {
118-
this.observe();
121+
// We need to set `buffered: true`, so the observer also records past
122+
// resource entries. However, there is a limited buffer size, so we may
123+
// not be able to collect all resource entries.
124+
this.resourceObserver.observe({
125+
type: RESOURCE,
126+
buffered: true
127+
});
119128
}
120129
}

0 commit comments

Comments
 (0)