Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 902465e

Browse files
committed
Refactoring
1 parent e5cc8b1 commit 902465e

6 files changed

Lines changed: 388 additions & 405 deletions

File tree

packages/opencensus-web-instrumentation-zone/src/monkey-patching.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { XHRWithUrl } from './zone-types';
17+
import { XhrWithUrl } from './zone-types';
1818

1919
export function doPatching() {
2020
patchXmlHttpRequestOpen();
@@ -38,6 +38,6 @@ function patchXmlHttpRequestOpen() {
3838
} else {
3939
open.call(this, method, url, true, null, null);
4040
}
41-
(this as XHRWithUrl)._ocweb_method = method;
41+
(this as XhrWithUrl)._ocweb_method = method;
4242
};
4343
}

packages/opencensus-web-instrumentation-zone/src/perf-resource-timing-selector.ts

Lines changed: 67 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,12 @@ import { XhrPerformanceResourceTiming } from './zone-types';
1919

2020
/**
2121
* Get Browser's performance resource timing data associated to a XHR.
22-
* For this case, some XHR might have two or one performance resource
23-
* timings as one of them is CORS pre-flight request and the second is related
24-
* to the actual HTTP request.
25-
* The algorithm to select performance resource timings related to that xhr is
26-
* is composed in general by three steps:
27-
*
28-
* 1. Filter the Performance Resource Timings by the name (it should match the
29-
* XHR URL), additionally, the start/end timings of every performance entry
30-
* should fit within the span start/end timings. These filtered performance
31-
* resource entries are considered as possible entries associated to the xhr.
32-
* Those are possible as there might be more than two entries that pass the
33-
* filter.
34-
*
35-
* 2. As the XHR could cause a CORS pre-flight, we have to look for either
36-
* possible pairs of performance resource timings or a single performance
37-
* resource entry (a possible pair is when a resource timing entry does not
38-
* overlap timings with other resource timing entry. Also, a possible single
39-
* resource timing is when that resource timing entry is not paired with any
40-
* other entry). Thus, for this step traverse the array of possible resource
41-
* entries and for every entry try to pair it with the other possible entries.
42-
*
43-
* 3. Pick the best performance resource timing for the XHR: Using the possible
44-
* performance resource timing entries from previous step, the best entry will
45-
* be the one with the minimum gap to the span start/end timings. That is the
46-
* substraction between the entry `respondeEnd` value and the span
47-
* `endPerfTime` plus the substraction between the entry `startTime` and span
48-
* `startPerfTime`. In case it is a tuple, the `startTime` corresponds to the
49-
* first entry and the `responseEnd` is from second entry.
50-
* The performance resource timing entry with the minimum gap to the span
51-
* start/end timings points out that entry is the best fit for the span.
52-
*
22+
* Some XHRs might have two or one performance resource timings as one of them
23+
* is the CORS pre-flight request and the second is related to the actual HTTP
24+
* request.
25+
* In overall, the algorithm to get this data takes the best fit for the span,
26+
* this means the closest performance resource timing to the span start/end
27+
* performance times is the returned value.
5328
* @param xhrUrl
5429
* @param span
5530
*/
@@ -63,9 +38,17 @@ export function getXhrPerfomanceData(
6338
return bestEntry;
6439
}
6540

66-
// Get Performance Resource Timings and filter them by matching the XHR url
67-
// with perfomance entry name. Additionally, the entry's start/end
68-
// timings must fit with in the span's start/end timings.
41+
/**
42+
* First step for the algorithm. Filter the Performance Resource Timings by the
43+
* name (it should match the XHR URL), additionally, the start/end timings of
44+
* every performance entry should fit within the span start/end timings.
45+
* These filtered performance resource entries are considered as possible
46+
* entries associated to the xhr.
47+
* Those are possible because there might be more than two entries that pass the
48+
* filter.
49+
* @param xhrUrl
50+
* @param span
51+
*/
6952
export function getPerfResourceEntries(
7053
xhrUrl: string,
7154
span: Span
@@ -77,64 +60,86 @@ export function getPerfResourceEntries(
7760
) as PerformanceResourceTiming[];
7861
}
7962

63+
/**
64+
* Second step for the 'Performance resource timings selector algorithm'.
65+
* As the XHR could cause a CORS pre-flight request, we have to look for
66+
* possible performance entries either containing cors preflight timings or not.
67+
* A possible entry with cors data is when a resource timing entry does not
68+
* overlap timings with other resource timing entry. Also, a possible entry
69+
* without cors resource timing is when that resource timing entry is not
70+
* 'paired' with any other entry.
71+
* Thus, for this step traverse the array of resource entries and for every
72+
* entry check if it is a possible performance resource entry.
73+
* @param perfEntries
74+
*/
8075
export function getPossiblePerfResourceEntries(
8176
perfEntries: PerformanceResourceTiming[]
8277
): XhrPerformanceResourceTiming[] {
8378
const possiblePerfEntries = new Array<XhrPerformanceResourceTiming>();
8479
const pairedEntries = new Set<PerformanceResourceTiming>();
85-
let perfEntry1: PerformanceResourceTiming;
86-
let perfEntry2: PerformanceResourceTiming;
80+
let entryI: PerformanceResourceTiming;
81+
let entryJ: PerformanceResourceTiming;
8782
// As this part of the algorithm traverses the array twice, although,
88-
// this array is not big as the performance resource entries is cleared
89-
// when there are no more running XHRs.
83+
// this array is not large as the performance resource entries buffer is
84+
// cleared when there are no more running XHRs.
9085
for (let i = 0; i < perfEntries.length; i++) {
91-
perfEntry1 = perfEntries[i];
86+
entryI = perfEntries[i];
9287
// Compare every performance entry with its consecutive perfomance entries.
9388
// That way to avoid comparing twice the entries.
9489
for (let j = i + 1; j < perfEntries.length; j++) {
95-
perfEntry2 = perfEntries[j];
96-
if (!overlappingPerfResourceTimings(perfEntry1, perfEntry2)) {
90+
entryJ = perfEntries[j];
91+
if (!overlappingPerfResourceTimings(entryI, entryJ)) {
9792
// As the entries are not overlapping, that means those timings
9893
// are possible perfomance timings related to the XHR.
99-
possiblePerfEntries.push([perfEntry1, perfEntry2]);
100-
pairedEntries.add(perfEntry1);
101-
pairedEntries.add(perfEntry2);
94+
possiblePerfEntries.push({
95+
corsPreFlightRequest: entryI,
96+
mainRequest: entryJ,
97+
});
98+
pairedEntries.add(entryI);
99+
pairedEntries.add(entryJ);
102100
}
103101
}
104-
// If the entry1 couldn't be paired with any other resource timing,
105-
// add it as a single resource timing. This is possible because this
106-
// single entry might be better that the other possible entries.
107-
if (!pairedEntries.has(perfEntry1)) {
108-
possiblePerfEntries.push(perfEntry1 as PerformanceResourceTiming);
102+
// If the entry couldn't be paired with any other resource timing,
103+
// add it as a possible resource timing without cors preflight data.
104+
// This is possible because this entry might be better than the other
105+
// possible entries.
106+
if (!pairedEntries.has(entryI)) {
107+
possiblePerfEntries.push({ mainRequest: entryI });
109108
}
110109
}
111110
return possiblePerfEntries;
112111
}
113112

114-
// The best Performance Resource Timing Entry is considered the one with the
115-
// minimum gap the span end/start timings. That way we think that it fits
116-
// better to the XHR as it is the closest data to the actual XHR.
113+
// Pick the best performance resource timing for the XHR: Using the possible
114+
// performance resource timing entries from previous step, the best entry will
115+
// be the one with the minimum gap to the span start/end timings.
116+
// The performance resource timing entry with the minimum gap to the span
117+
// start/end timings points out that entry is the best fit for the span.
117118
function getBestPerfResourceTiming(
118119
perfEntries: XhrPerformanceResourceTiming[],
119120
span: Span
120121
): XhrPerformanceResourceTiming | undefined {
121122
let minimumGapToSpan = Number.MAX_VALUE;
122123
let bestPerfEntry: XhrPerformanceResourceTiming | undefined = undefined;
123-
let sumGapsToSpan: number;
124124
for (const perfEntry of perfEntries) {
125-
// As a Tuple is in the end an Array, check that perfEntry is instance of
126-
// Array is enough to know if this value refers to a Tuple.
127-
if (perfEntry instanceof Array) {
128-
sumGapsToSpan = Math.abs(perfEntry[0].startTime - span.startPerfTime);
129-
sumGapsToSpan += Math.abs(perfEntry[1].responseEnd - span.endPerfTime);
125+
let gapToSpan = Math.abs(
126+
perfEntry.mainRequest.responseEnd - span.endPerfTime
127+
);
128+
// If the current entry has cors preflight data use its `startTime` to
129+
// calculate the gap to the span.
130+
if (perfEntry.corsPreFlightRequest) {
131+
gapToSpan += Math.abs(
132+
perfEntry.corsPreFlightRequest.startTime - span.startPerfTime
133+
);
130134
} else {
131-
sumGapsToSpan = Math.abs(perfEntry.responseEnd - span.endPerfTime);
132-
sumGapsToSpan += Math.abs(perfEntry.startTime - span.startPerfTime);
135+
gapToSpan += Math.abs(
136+
perfEntry.mainRequest.startTime - span.startPerfTime
137+
);
133138
}
134139
// If there is a new minimum gap to the span, update the minimum and pick
135140
// the current performance entry as the best at this point.
136-
if (sumGapsToSpan < minimumGapToSpan) {
137-
minimumGapToSpan = sumGapsToSpan;
141+
if (gapToSpan < minimumGapToSpan) {
142+
minimumGapToSpan = gapToSpan;
138143
bestPerfEntry = perfEntry;
139144
}
140145
}

packages/opencensus-web-instrumentation-zone/src/xhr-interceptor.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { AsyncTask, XHRWithUrl } from './zone-types';
17+
import { AsyncTask, XhrWithUrl } from './zone-types';
1818
import {
1919
RootSpan,
2020
Span,
@@ -33,9 +33,11 @@ import {
3333
getResourceSpan,
3434
} from '@opencensus/web-instrumentation-perf';
3535

36+
const TRACEPARENT_HEADER = 'traceparent';
37+
3638
// Map intended to keep track of current XHR objects
3739
// associated to a span.
38-
const xhrSpans = new Map<XHRWithUrl, Span>();
40+
const xhrSpans = new Map<XhrWithUrl, Span>();
3941

4042
// Keeps track of the current xhr tasks that are running. This is
4143
// useful to clear the Performance Resource Timing entries when no
@@ -54,7 +56,7 @@ export function interceptXhrTask(task: AsyncTask) {
5456
if (!isTrackedTask(task)) return;
5557
if (!(task.target instanceof XMLHttpRequest)) return;
5658

57-
const xhr = task.target as XHRWithUrl;
59+
const xhr = task.target as XhrWithUrl;
5860
if (xhr.readyState === XMLHttpRequest.OPENED) {
5961
incrementXhrTaskCount();
6062
const rootSpan: RootSpan = task.zone.get('data').rootSpan;
@@ -66,7 +68,7 @@ export function interceptXhrTask(task: AsyncTask) {
6668
}
6769

6870
function setTraceparentContextHeader(
69-
xhr: XHRWithUrl,
71+
xhr: XhrWithUrl,
7072
rootSpan: RootSpan
7173
): void {
7274
// `__zone_symbol__xhrURL` is set by the Zone monkey-path.
@@ -80,7 +82,7 @@ function setTraceparentContextHeader(
8082
xhrSpans.set(xhr, childSpan);
8183
if (traceOriginMatchesOrSameOrigin(xhrUrl)) {
8284
xhr.setRequestHeader(
83-
'traceparent',
85+
TRACEPARENT_HEADER,
8486
spanContextToTraceParent({
8587
traceId: rootSpan.traceId,
8688
spanId: childSpan.id,
@@ -89,7 +91,7 @@ function setTraceparentContextHeader(
8991
}
9092
}
9193

92-
function endXhrSpan(xhr: XHRWithUrl): void {
94+
function endXhrSpan(xhr: XhrWithUrl): void {
9395
const span = xhrSpans.get(xhr);
9496
if (span) {
9597
// TODO: Investigate more to send the the status code a `number` rather
@@ -109,24 +111,15 @@ function maybeClearPerfResourceBuffer(): void {
109111
if (xhrTasksCount === 0) performance.clearResourceTimings();
110112
}
111113

112-
function joinPerfResourceDataToSpan(xhr: XHRWithUrl, span: Span) {
113-
const perfResourceTimings = getXhrPerfomanceData(xhr.responseURL, span);
114-
if (perfResourceTimings instanceof Array) {
115-
// This case is true when the resource timings data associates two entries
116-
// to the span, where the first entry is the CORS pre-flight request and
117-
// the second is the actual HTTP request. Create a child span which is
118-
// related to the CORS pre-flight and use the second entry to add
119-
// annotations to the span.
120-
const corsPerfTiming = perfResourceTimings[0] as PerformanceResourceTimingExtended;
121-
const actualXhrPerfTiming = perfResourceTimings[1] as PerformanceResourceTimingExtended;
122-
setCorsPerfTimingAsChildSpan(corsPerfTiming, span);
123-
span.annotations = annotationsForPerfTimeFields(
124-
actualXhrPerfTiming,
125-
PERFORMANCE_ENTRY_EVENTS
126-
);
127-
} else if (perfResourceTimings) {
114+
function joinPerfResourceDataToSpan(xhr: XhrWithUrl, span: Span) {
115+
const xhrPerfResourceTiming = getXhrPerfomanceData(xhr.responseURL, span);
116+
if (xhrPerfResourceTiming) {
117+
if (xhrPerfResourceTiming.corsPreFlightRequest) {
118+
const corsPerfTiming = xhrPerfResourceTiming.corsPreFlightRequest as PerformanceResourceTimingExtended;
119+
setCorsPerfTimingAsChildSpan(corsPerfTiming, span);
120+
}
128121
span.annotations = annotationsForPerfTimeFields(
129-
perfResourceTimings as PerformanceResourceTimingExtended,
122+
xhrPerfResourceTiming.mainRequest as PerformanceResourceTimingExtended,
130123
PERFORMANCE_ENTRY_EVENTS
131124
);
132125
}
@@ -137,7 +130,7 @@ function setCorsPerfTimingAsChildSpan(
137130
span: Span
138131
): void {
139132
const corsSpan = getResourceSpan(performanceTiming, span.traceId, span.id);
140-
corsSpan.name = 'CORS';
133+
corsSpan.name = 'CORS Preflight';
141134
span.spans.push(corsSpan);
142135
}
143136

packages/opencensus-web-instrumentation-zone/src/zone-types.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export interface OnPageInteractionData {
4343
* `HTMLElement` is necessary when the xhr is captured from the tasks target
4444
* as the Zone monkey-patch parses xhrs as `HTMLElement & XMLHttpRequest`.
4545
*/
46-
export type XHRWithUrl = HTMLElement &
46+
export type XhrWithUrl = HTMLElement &
4747
XMLHttpRequest & {
4848
__zone_symbol__xhrURL: string;
4949
_ocweb_method: string;
@@ -71,20 +71,10 @@ export declare interface WindowWithOcwGlobals extends Window {
7171
/**
7272
* Allows to keep track of performance entries related to a XHR.
7373
* As some XHRs might generate a CORS pre-flight request, the XHR
74-
* might have either two performance resource entries or a single
75-
* performance resource entry.
74+
* might have a cors preflight performance resource timing entry or only the
75+
* main request performance resource timing.
7676
*/
77-
export type XhrPerformanceResourceTiming =
78-
| PerformanceResourceTimingTuple
79-
| PerformanceResourceTiming;
80-
81-
/**
82-
* Tuple type to associate two `PerformanceResourceTiming` objects as a pair.
83-
* Used to select performance resource timing data associated to an XHR. In
84-
* general, the first value points out it is a CORS pre-flight request data and
85-
* the second value corresponds to the actual HTTP request.
86-
*/
87-
type PerformanceResourceTimingTuple = [
88-
PerformanceResourceTiming,
89-
PerformanceResourceTiming
90-
];
77+
export interface XhrPerformanceResourceTiming {
78+
corsPreFlightRequest?: PerformanceResourceTiming;
79+
mainRequest: PerformanceResourceTiming;
80+
}

0 commit comments

Comments
 (0)