An important data in front-end monitoring is request data
First, we need to be clear about what information we typically need for a request
The following figure describes what we typically need for request data
So how can we do non-intrusive data collection of business code?
The approach is clear: re-native apis and a layer of proxies over key methods
Now, let’s analyze how to implement it step by step from the perspective of these two apis
xmlHttpRequest
The general method of encapsulation is as follows
// First save a copy of the original properties of the object that need to be encapsulated for later invocation by the proxy
let xhr_open = XMLHttpRequest.prototype.open;
let xhr_send = XMLHttpRequest.prototype.send;
// Step 2, replace the original object properties with proxy objects
XMLHttpRequest.prototype.open = function (. args) {
// Here we add our own fetch logic
xhr_open.apply(this, args);
};
XMLHttpRequest.prototype.send = function (data) {
// Here we add our own fetch logic
xhr_send.apply(this, args);
}
Copy the code
So what about the onReadyStatechange event
Note: This event must be listened on before the SEND method is sent
We can listen for this event when encapsulating the SEND method
XMLHttpRequest.prototype.send = function (data) {
// Add the readyStatechange event
this.addEventListener('readystatechange'.function () {
// The result of the request is processed in response
});
xhr_send.call(this, data);
};
Copy the code
Now that the encapsulation method is complete, how do we capture the information described in the first diagram?
(1) the request information
The open method has a fixed list of arguments: Method, URL, Async, username, password
In the open proxy process, you can get it
XMLHttpRequest.prototype.open = function (. args) {
this.__monitor_xhr = {
method: args[0].url: args[1]
}
xhr_open.apply(this, args);
};
Copy the code
In the code above, we write a new attribute on the current XHR object to hold the information we get. Request the body data, which we get in the analysis below
2. Reponse and Timeline
At this point, we need to process the request results to get the data we want
XMLHttpRequest.prototype.send = function (data) {
// Record the start time of the request for time calculation
const startTime = new Date(a);// Add the readyStatechange event
this.addEventListener('readystatechange'.function () {
try {
if (this.readyState === XMLHttpRequest.DONE) {
// Request end time
const endTime = new Date(a);// Request time
this.__monitor.duration = (endTime - startTime) ;
/ / request body
this.__monitor.req_body = data;
// Get response header, body and other information}}catch (err) {
}
}
});
xhr_send.call(this, data);
};
Copy the code
The response header information above can be passed
xml.getAllResponseHeaders()
Copy the code
The body information can be obtained in the following ways
function getBody (xhrObject) {
var body = void 0;
// IE 11 sometimes throws when trying to access a large responses:
// https://connect.microsoft.com/IE/Feedback/Details/1053110
// gte IE10 will support responseType
try {
switch (xhrObject.responseType) {
case 'json':
case 'arraybuffer':
case 'blob':
{
body = xhrObject.response;
break;
}
case 'document':
{
body = xhrObject.responseXML;
break;
}
case 'text':
case ' ':
{
body = xhrObject.responseText;
break;
}
default:
{
body = ' '; }}// When requesting binary data, IE6-9 will throw an exception
// on any attempt to access responseText (#11426)
if(! body &&typeof xhrObject.responseText === "string") { body = xhrObject.responseText; }}catch (err) {
body = 'monitor: Error accessing response.';
}
return body
}
Copy the code
fetch
Fetch because the API and XHR are quite different, FETCH returns a promise object, a encapsulation of this situation
First, we still need to add an agent to fetch, similar to XHR
// First save the original fetch reference
let origFetch = window.fetch
window.fetch =function(fn, t) {
// Perform our data collection on this side
return origFetch.apply(this, args)
};
Copy the code
Get request and Response information
window.fetch =function(fn, t) {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
var p = null;
// Since the parameter list of FETCH is more flexible, the corresponding processing is required
if (typeofRequest ! = ='undefined' && args[0] instanceof Request) {
p = args[0].clone().text().then(function (body) {
return utils.extendsObjects({}, pluckFetchFields(args[0]) {body: body
});
});
} else {
p = Promise.resolve(utils.extendsObjects({}, pluckFetchFields(args[1]) {url: ' ' + args[0].body: (args[1] || {}).body
}));
}
var fetchData = {
method: ' '.url: ' '.status_code: null.start_time: new Date().getTime(),
request: {headers: {},
body: ' '
},
response: {headers: {},
body: ' '
},
timeline: {dns:0.connect:0.response:0.request: 0.duration: 0}};// Then is added by default to collect results
return origFetch.apply(this, args).then(function(response) {
fetchData.status_code = response.status;
fetchData.duration = new Date().getTime() - fetchData.start_time
fetchData.timeline.duration = fetchData.duration
p.then(function(req) {
fetchData.method = req.method
fetchData.url = req.url
utils.objectMerge(fetchData.request, {mode: req.mode, referrer: req.referrer, credentials: req.credentials, headers: req.headers, body: req.body})
var clonedText = null;
try {
clonedText = response.clone().text();
} catch (err) {
// safari has a bug where cloning can fail
clonedText = Promise.resolve('Monitor fetch error: ' + err.message);
}
clonedText.then(function(body) {
fetchData.response.body = body
fetchData.response.headers = makeObjectFromHeaders(response.headers)
// Send data to the server
_reportToServer(fetchData)
})
})
return response;
});
};
Copy the code
The missing step is the timeline data in the first figure, which will be analyzed along with the performance data
Second, what do we do with this data
1. Request data reflects a portion of the user’s trajectory
2. Requesting information can help us locate problems
3. Locate server problems by analyzing the success rate of requests
Third, pay attention
-
Security, we should be involved in user privacy data desensitization transmission or not transmission
-
For the data of response, we should intercept the data as needed instead of transferring it all at once to prevent too much invalid data
The above content is just a general sharing of browser request data collection and usage. In addition to browsers, some, such as applets request fetch, are shared in applets information collection
Read the original