Skip to content

Commit a1b5412

Browse files
authored
Merge 5d5262c into bd2a005
2 parents bd2a005 + 5d5262c commit a1b5412

File tree

5 files changed

+77
-28
lines changed

5 files changed

+77
-28
lines changed

packages/opentelemetry-core/src/utils/url.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function urlMatches(url: string, urlToMatch: string | RegExp): boolean {
1717
if (typeof urlToMatch === 'string') {
1818
return url === urlToMatch;
1919
} else {
20-
return !!url.match(urlToMatch);
20+
return urlToMatch.test(url);
2121
}
2222
}
2323
/**

packages/opentelemetry-instrumentation-fetch/src/fetch.ts

+1-11
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,6 @@ import { VERSION } from './version';
3434
// safe enough
3535
const OBSERVER_WAIT_TIME_MS = 300;
3636

37-
// Used to normalize relative URLs
38-
let a: HTMLAnchorElement | undefined;
39-
const getUrlNormalizingAnchor = () => {
40-
if (!a) {
41-
a = document.createElement('a');
42-
}
43-
44-
return a;
45-
};
46-
4737
export interface FetchCustomAttributeFunction {
4838
(
4939
span: api.Span,
@@ -438,7 +428,7 @@ export class FetchInstrumentation extends InstrumentationBase<
438428

439429
const observer: PerformanceObserver = new PerformanceObserver(list => {
440430
const perfObsEntries = list.getEntries() as PerformanceResourceTiming[];
441-
const urlNormalizingAnchor = getUrlNormalizingAnchor();
431+
const urlNormalizingAnchor = web.getUrlNormalizingAnchor();
442432
urlNormalizingAnchor.href = spanUrl;
443433
perfObsEntries.forEach(entry => {
444434
if (

packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
parseUrl,
3030
PerformanceTimingNames as PTN,
3131
shouldPropagateTraceHeaders,
32+
getUrlNormalizingAnchor
3233
} from '@opentelemetry/web';
3334
import { EventNames } from './enums/EventNames';
3435
import {
@@ -216,10 +217,13 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase<XMLHttpRe
216217
xhrMem.createdResources = {
217218
observer: new PerformanceObserver(list => {
218219
const entries = list.getEntries() as PerformanceResourceTiming[];
220+
const urlNormalizingAnchor = getUrlNormalizingAnchor();
221+
urlNormalizingAnchor.href = spanUrl;
222+
219223
entries.forEach(entry => {
220224
if (
221225
entry.initiatorType === 'xmlhttprequest' &&
222-
entry.name === spanUrl
226+
entry.name === urlNormalizingAnchor.href
223227
) {
224228
if (xhrMem.createdResources) {
225229
xhrMem.createdResources.entries.push(entry);

packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts

+67-12
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,36 @@ function createMainResource(resource = {}): PerformanceResourceTiming {
125125
return mainResource;
126126
}
127127

128+
function createFakePerformanceObs(url: string) {
129+
class FakePerfObs implements PerformanceObserver {
130+
constructor(private readonly cb: PerformanceObserverCallback) {}
131+
observe() {
132+
const absoluteUrl = url.startsWith('http') ? url : location.origin + url;
133+
const resources: PerformanceObserverEntryList = {
134+
getEntries(): PerformanceEntryList {
135+
return [
136+
createResource({ name: absoluteUrl }) as any,
137+
createMainResource({ name: absoluteUrl }) as any,
138+
];
139+
},
140+
getEntriesByName(): PerformanceEntryList {
141+
return [];
142+
},
143+
getEntriesByType(): PerformanceEntryList {
144+
return [];
145+
},
146+
};
147+
this.cb(resources, this);
148+
}
149+
disconnect() {}
150+
takeRecords(): PerformanceEntryList {
151+
return [];
152+
}
153+
}
154+
155+
return FakePerfObs;
156+
}
157+
128158
describe('xhr', () => {
129159
const asyncTests = [{ async: true }, { async: false }];
130160
asyncTests.forEach(test => {
@@ -200,6 +230,11 @@ describe('xhr', () => {
200230
'getEntriesByType'
201231
);
202232
spyEntries.withArgs('resource').returns(resources);
233+
234+
sinon
235+
.stub(window, 'PerformanceObserver')
236+
.value(createFakePerformanceObs(fileUrl));
237+
203238
xmlHttpRequestInstrumentation = new XMLHttpRequestInstrumentation(
204239
config
205240
);
@@ -221,7 +256,7 @@ describe('xhr', () => {
221256

222257
rootSpan = webTracerWithZone.startSpan('root');
223258
api.context.with(api.trace.setSpan(api.context.active(), rootSpan), () => {
224-
getData(
259+
void getData(
225260
new XMLHttpRequest(),
226261
fileUrl,
227262
() => {
@@ -635,20 +670,11 @@ describe('xhr', () => {
635670

636671
beforeEach(done => {
637672
requests = [];
638-
const resources: PerformanceResourceTiming[] = [];
639-
resources.push(
640-
createResource({
641-
name: firstUrl,
642-
}),
643-
createResource({
644-
name: secondUrl,
645-
})
646-
);
647673
const reusableReq = new XMLHttpRequest();
648674
api.context.with(
649675
api.trace.setSpan(api.context.active(), rootSpan),
650676
() => {
651-
getData(
677+
void getData(
652678
reusableReq,
653679
firstUrl,
654680
() => {
@@ -665,7 +691,7 @@ describe('xhr', () => {
665691
api.context.with(
666692
api.trace.setSpan(api.context.active(), rootSpan),
667693
() => {
668-
getData(
694+
void getData(
669695
reusableReq,
670696
secondUrl,
671697
() => {
@@ -728,6 +754,35 @@ describe('xhr', () => {
728754
assert.ok(attributes['xhr-custom-attribute'] === 'bar');
729755
});
730756
});
757+
758+
describe('when using relative url', () => {
759+
beforeEach(done => {
760+
clearData();
761+
const propagateTraceHeaderCorsUrls = [window.location.origin];
762+
prepareData(done, '/get', { propagateTraceHeaderCorsUrls });
763+
});
764+
765+
it('should create correct span with events', () => {
766+
// no prefetch span because mock observer uses location.origin as url when relative
767+
// and prefetch span finding compares url origins
768+
const span: tracing.ReadableSpan = exportSpy.args[0][0][0];
769+
const events = span.events;
770+
771+
assert.strictEqual(
772+
exportSpy.args.length,
773+
1,
774+
`Wrong number of spans: ${exportSpy.args.length}`
775+
);
776+
777+
assert.strictEqual(events.length, 12, `number of events is wrong: ${events.length}`);
778+
assert.strictEqual(
779+
events[8].name,
780+
PTN.REQUEST_START,
781+
`event ${PTN.REQUEST_START} is not defined`
782+
);
783+
});
784+
});
785+
731786
});
732787

733788
describe('when request is NOT successful', () => {

packages/opentelemetry-web/src/utils.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
3030

3131
// Used to normalize relative URLs
3232
let a: HTMLAnchorElement | undefined;
33-
const getUrlNormalizingAnchor = () => {
33+
export function getUrlNormalizingAnchor(): HTMLAnchorElement {
3434
if (!a) {
3535
a = document.createElement('a');
3636
}
3737

3838
return a;
39-
};
39+
}
4040

4141
/**
4242
* Helper function to be able to use enum as typed key in type and in interface when using forEach
@@ -155,7 +155,7 @@ export function getResource(
155155
mainRequest: filteredResources[0],
156156
};
157157
}
158-
const sorted = sortResources(filteredResources.slice());
158+
const sorted = sortResources(filteredResources);
159159

160160
const parsedSpanUrl = parseUrl(spanUrl);
161161
if (parsedSpanUrl.origin !== window.location.origin && sorted.length > 1) {

0 commit comments

Comments
 (0)