@grafana/faro-web-sdk
Version:
Faro instrumentations, metas, transports for web.
119 lines • 4.11 kB
JavaScript
import { genShortID, Observable } from '@grafana/faro-core';
import { getUrlFromResource, isUrlIgnored } from '../../../utils/url';
import { MESSAGE_TYPE_HTTP_REQUEST_END, MESSAGE_TYPE_HTTP_REQUEST_START } from './const';
const apiTypeFetch = 'fetch';
const apiTypeXhr = 'xhr';
/**
* Monitors if any http requests are in progress.
*/
let httpRequestObservable;
let isInstrumented = false;
let originalXhrOpen;
let originalFetchFn;
export function monitorHttpRequests() {
if (httpRequestObservable) {
return httpRequestObservable;
}
httpRequestObservable = new Observable();
function emitStartMessage(requestProps) {
httpRequestObservable.notify({
type: MESSAGE_TYPE_HTTP_REQUEST_START,
request: requestProps,
});
}
function emitEndMessage(requestProps) {
httpRequestObservable.notify({
type: MESSAGE_TYPE_HTTP_REQUEST_END,
request: requestProps,
});
}
if (!isInstrumented) {
monitorFetch({
onRequestStart: emitStartMessage,
onRequestEnd: emitEndMessage,
});
monitorXhr({
onRequestStart: emitStartMessage,
onRequestEnd: emitEndMessage,
});
isInstrumented = true;
}
return httpRequestObservable;
}
function monitorXhr({ onRequestStart, onRequestEnd, }) {
if (!originalXhrOpen) {
originalXhrOpen = XMLHttpRequest.prototype.open;
}
XMLHttpRequest.prototype.open = function () {
const url = arguments[1];
const isIgnoredUrl = isUrlIgnored(url);
const method = arguments[0];
const requestId = genShortID();
// request has started to load data.
this.addEventListener('loadstart', function () {
if (!isIgnoredUrl) {
onRequestStart({ url, method, requestId, apiType: apiTypeXhr });
}
});
// transaction completes successfully.
this.addEventListener('load', function () {
if (!isIgnoredUrl) {
onRequestEnd({ url, method, requestId, apiType: apiTypeXhr });
}
});
this.addEventListener('error', function () {
if (!isIgnoredUrl) {
onRequestEnd({ url, method, requestId, apiType: apiTypeXhr });
}
});
this.addEventListener('abort', function () {
if (!isIgnoredUrl) {
onRequestEnd({ url, method, requestId, apiType: apiTypeXhr });
}
});
originalXhrOpen.apply(this, arguments);
};
}
function monitorFetch({ onRequestEnd, onRequestStart, }) {
if (!originalFetchFn) {
originalFetchFn = window.fetch;
}
window.fetch = function () {
var _a, _b;
const url = (_a = getUrlFromResource(arguments[0])) !== null && _a !== void 0 ? _a : '';
const isIgnoredUrl = isUrlIgnored(url);
const method = ((_b = arguments[1]) !== null && _b !== void 0 ? _b : {}).method;
const requestId = genShortID();
if (!isIgnoredUrl) {
onRequestStart({ url, method, requestId, apiType: apiTypeFetch });
}
return originalFetchFn
.apply(this, arguments)
.then((response) => {
if (!isIgnoredUrl) {
onRequestEnd({ url, method, requestId, apiType: apiTypeFetch });
}
return response;
})
.catch((error) => {
if (!isIgnoredUrl) {
onRequestEnd({ url, method, requestId, apiType: apiTypeFetch });
}
throw error;
});
};
}
// Test-only utility to reset instrumentation and singleton between tests
export function __resetHttpRequestMonitorForTests() {
if (originalXhrOpen) {
XMLHttpRequest.prototype.open = originalXhrOpen;
}
if (originalFetchFn) {
window.fetch = originalFetchFn;
}
httpRequestObservable = undefined;
isInstrumented = false;
originalXhrOpen = undefined;
originalFetchFn = undefined;
}
//# sourceMappingURL=httpRequestMonitor.js.map