Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove window and document dependencies in web packages #2689

Merged
merged 6 commits into from
Dec 24, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { AttributeNames } from './enums/AttributeNames';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { FetchError, FetchResponse, SpanData } from './types';
import { VERSION } from './version';
import { _globalThis } from '@opentelemetry/core';

// how long to wait for observer to collect information about resources
// this is needed as event "load" is called before observer
Expand Down Expand Up @@ -288,13 +289,14 @@ export class FetchInstrumentation extends InstrumentationBase<
/**
* Patches the constructor of fetch
*/
private _patchConstructor(): (original: Window['fetch']) => Window['fetch'] {
private _patchConstructor(): (original: typeof fetch) => typeof fetch {
return original => {
const plugin = this;
return function patchConstructor(
this: Window,
...args: Parameters<Window['fetch']>
this: typeof globalThis,
...args: Parameters<typeof fetch>
): Promise<Response> {
const self = this;
const url = args[0] instanceof Request ? args[0].url : args[0];
const options = args[0] instanceof Request ? args[0] : args[1] || {};
const createdSpan = plugin._createSpan(url, options);
Expand Down Expand Up @@ -378,10 +380,10 @@ export class FetchInstrumentation extends InstrumentationBase<
plugin._addHeaders(options, url);
plugin._tasksCount++;
return original
.apply(this, options instanceof Request ? [options] : [url, options])
.apply(self, options instanceof Request ? [options] : [url, options])
dyladan marked this conversation as resolved.
Show resolved Hide resolved
.then(
onSuccess.bind(this, createdSpan, resolve),
onError.bind(this, createdSpan, reject)
onSuccess.bind(self, createdSpan, resolve),
onError.bind(self, createdSpan, reject)
);
}
);
Expand Down Expand Up @@ -420,18 +422,17 @@ export class FetchInstrumentation extends InstrumentationBase<
private _prepareSpanData(spanUrl: string): SpanData {
const startTime = core.hrTime();
const entries: PerformanceResourceTiming[] = [];
if (typeof window.PerformanceObserver === 'undefined') {
if (typeof PerformanceObserver === 'undefined') {
dyladan marked this conversation as resolved.
Show resolved Hide resolved
return { entries, startTime, spanUrl };
}

const observer: PerformanceObserver = new PerformanceObserver(list => {
const perfObsEntries = list.getEntries() as PerformanceResourceTiming[];
const urlNormalizingAnchor = web.getUrlNormalizingAnchor();
urlNormalizingAnchor.href = spanUrl;
const parsedUrl = web.parseUrl(spanUrl);
perfObsEntries.forEach(entry => {
if (
entry.initiatorType === 'fetch' &&
entry.name === urlNormalizingAnchor.href
entry.name === parsedUrl.href
) {
entries.push(entry);
}
Expand All @@ -447,18 +448,18 @@ export class FetchInstrumentation extends InstrumentationBase<
* implements enable function
*/
override enable(): void {
if (isWrapped(window.fetch)) {
this._unwrap(window, 'fetch');
if (isWrapped(fetch)) {
this._unwrap(_globalThis, 'fetch');
this._diag.debug('removing previous patch for constructor');
}
this._wrap(window, 'fetch', this._patchConstructor());
this._wrap(_globalThis, 'fetch', this._patchConstructor());
}

/**
* implements unpatch function
*/
override disable(): void {
this._unwrap(window, 'fetch');
this._unwrap(_globalThis, 'fetch');
this._usedResources = new WeakSet<PerformanceResourceTiming>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
parseUrl,
PerformanceTimingNames as PTN,
shouldPropagateTraceHeaders,
getUrlNormalizingAnchor
} from '@opentelemetry/sdk-trace-web';
import { EventNames } from './enums/EventNames';
import {
Expand Down Expand Up @@ -209,21 +208,20 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase<XMLHttpRe
const xhrMem = this._xhrMem.get(xhr);
if (
!xhrMem ||
typeof window.PerformanceObserver === 'undefined' ||
typeof window.PerformanceResourceTiming === 'undefined'
typeof PerformanceObserver === 'undefined' ||
typeof PerformanceResourceTiming === 'undefined'
dyladan marked this conversation as resolved.
Show resolved Hide resolved
) {
return;
}
xhrMem.createdResources = {
observer: new PerformanceObserver(list => {
const entries = list.getEntries() as PerformanceResourceTiming[];
const urlNormalizingAnchor = getUrlNormalizingAnchor();
urlNormalizingAnchor.href = spanUrl;
const parsedUrl = parseUrl(spanUrl);

entries.forEach(entry => {
if (
entry.initiatorType === 'xmlhttprequest' &&
entry.name === urlNormalizingAnchor.href
entry.name === parsedUrl.href
) {
if (xhrMem.createdResources) {
xhrMem.createdResources.entries.push(entry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function sendWithXhr(
urlStr: string,
xhrHeaders: Record<string, string> = {}
) {
const xhr = new window.XMLHttpRequest();
const xhr = new XMLHttpRequest();
xhr.open('POST', urlStr);
Object.entries(xhrHeaders).forEach(([k, v]) => {
xhr.setRequestHeader(k, v);
Expand Down
47 changes: 39 additions & 8 deletions packages/opentelemetry-sdk-trace-web/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { SemanticAttributes } from '@opentelemetry/semantic-conventions';

// Used to normalize relative URLs
let urlNormalizingAnchor: HTMLAnchorElement | undefined;
export function getUrlNormalizingAnchor(): HTMLAnchorElement {
function getUrlNormalizingAnchor(): HTMLAnchorElement {
if (!urlNormalizingAnchor) {
urlNormalizingAnchor = document.createElement('a');
}
Expand Down Expand Up @@ -140,9 +140,8 @@ export function getResource(
initiatorType?: string
): PerformanceResourceTimingInfo {
// de-relativize the URL before usage (does no harm to absolute URLs)
const urlNormalizingAnchor = getUrlNormalizingAnchor();
urlNormalizingAnchor.href = spanUrl;
spanUrl = urlNormalizingAnchor.href;
const parsedSpanUrl = parseUrl(spanUrl);
spanUrl = parsedSpanUrl.toString();

const filteredResources = filterResourcesForSpan(
spanUrl,
Expand All @@ -165,7 +164,6 @@ export function getResource(
}
const sorted = sortResources(filteredResources);

const parsedSpanUrl = parseUrl(spanUrl);
if (parsedSpanUrl.origin !== window.location.origin && sorted.length > 1) {
let corsPreFlightRequest: PerformanceResourceTiming | undefined = sorted[0];
let mainRequest: PerformanceResourceTiming = findMainRequest(
Expand Down Expand Up @@ -280,15 +278,48 @@ function filterResourcesForSpan(
}

/**
* Parses url using anchor element
* The URLLike interface represents an URL and HTMLAnchorElement compatible fields.
*/
export interface URLLike {
hash: string;
host: string;
hostname: string;
href: string;
readonly origin: string;
password: string;
pathname: string;
port: string;
protocol: string;
search: string;
username: string;
}

/**
* Parses url using URL constructor or fallback to anchor element.
* @param url
*/
export function parseUrl(url: string): HTMLAnchorElement {
const element = document.createElement('a');
export function parseUrl(url: string): URLLike {
if (typeof URL === 'function') {
return new URL(url);
}
const element = getUrlNormalizingAnchor();
legendecas marked this conversation as resolved.
Show resolved Hide resolved
element.href = url;
return element;
}

/**
* Parses url using URL constructor or fallback to anchor element and serialize
* it to a string.
*
* Performs the steps described in https://html.spec.whatwg.org/multipage/urls-and-fetching.html#parse-a-url
*
* @param url
*/
export function normalizeUrl(url: string): string {
const urlLike = parseUrl(url);
return urlLike.href;
}

/**
* Get element XPath
* @param target - target element
Expand Down