feat(tracker-fetch): capture headers
This commit is contained in:
parent
577d15dab8
commit
d6914cc3c8
4 changed files with 68 additions and 17 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
# OpenReplay Tracker Fetch plugin
|
# OpenReplay Tracker Fetch plugin
|
||||||
|
|
||||||
Tracker plugin to support tracking of the `fetch` requests payload.
|
Tracker plugin to support tracking of the `fetch` requests payload.
|
||||||
Additionally it populates the requests with `sessionID` header for backend logging.
|
Additionally it populates the requests with `sessionToken` header for backend logging.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -23,13 +23,24 @@ const tracker = new Tracker({
|
||||||
});
|
});
|
||||||
tracker.start();
|
tracker.start();
|
||||||
|
|
||||||
export const fetch = tracker.use(trackerFetch({
|
export const fetch = tracker.use(trackerFetch({ /* options here*/ }));
|
||||||
sessionTokenHeader: 'X-Session-ID', // optional
|
|
||||||
failuresOnly: true //optional
|
|
||||||
}));
|
|
||||||
|
|
||||||
fetch('https://my.api.io/resource').then(response => response.json()).then(body => console.log(body));
|
fetch('https://my.api.io/resource').then(response => response.json()).then(body => console.log(body));
|
||||||
```
|
```
|
||||||
In case you use OpenReplay integrations (sentry, bugsnag or others), you can use `sessionTokenHeader` option to specify the header name. This header will be appended automatically to the each fetch request and will contain OpenReplay session identificator value.
|
|
||||||
|
|
||||||
Set `failuresOnly` option to `true` if you want to record only requests with the status code >= 400.
|
Options:
|
||||||
|
```ts
|
||||||
|
{
|
||||||
|
failuresOnly: boolean, // default false
|
||||||
|
sessionTokenHeader: string | undefined, // default undefined
|
||||||
|
ingoreHeaders: Array<string> | boolean, // default [ 'Cookie', 'Set-Cookie', 'Authorization' ]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Set `failuresOnly` option to `true` if you want to record only requests with the status code >= 400.
|
||||||
|
|
||||||
|
In case you use [OpenReplay integrations (sentry, bugsnag or others)](https://docs.openreplay.com/integrations), you can use `sessionTokenHeader` option to specify the header name. This header will be appended automatically to the each fetch request and will contain OpenReplay session identificator value.
|
||||||
|
|
||||||
|
You can define list of headers that you don't want to capture with the `ingoreHeaders` options. Set its value to `false` if you want to catch them all (`true` if opposite). By default plugin ignores the list of headers that might be sensetive such as `[ 'Cookie', 'Set-Cookie', 'Authorization' ]`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@openreplay/tracker-fetch",
|
"name": "@openreplay/tracker-fetch",
|
||||||
"description": "Tracker plugin for fetch requests recording ",
|
"description": "Tracker plugin for fetch requests recording ",
|
||||||
"version": "3.0.0",
|
"version": "3.4.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"fetch",
|
"fetch",
|
||||||
"logging",
|
"logging",
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,17 @@ import { App, Messages } from '@openreplay/tracker';
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
sessionTokenHeader?: string;
|
sessionTokenHeader?: string;
|
||||||
failuresOnly?: boolean;
|
replaceDefault: boolean; // overrideDefault ?
|
||||||
|
failuresOnly: boolean;
|
||||||
|
ingoreHeaders: Array<string> | boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(opts: Partial<Options> = {}) {
|
export default function(opts: Partial<Options> = {}) {
|
||||||
const options: Options = Object.assign(
|
const options: Options = Object.assign(
|
||||||
{
|
{
|
||||||
|
replaceDefault: false,
|
||||||
failuresOnly: false,
|
failuresOnly: false,
|
||||||
|
ingoreHeaders: [ 'Cookie', 'Set-Cookie', 'Authorization' ],
|
||||||
},
|
},
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
|
|
@ -18,7 +22,12 @@ export default function(opts: Partial<Options> = {}) {
|
||||||
return window.fetch;
|
return window.fetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
return async (input: RequestInfo, init: RequestInit = {}) => {
|
const ihOpt = options.ingoreHeaders
|
||||||
|
const isHIgnoring = Array.isArray(ihOpt)
|
||||||
|
? name => ihOpt.includes(name)
|
||||||
|
: () => ihOpt
|
||||||
|
|
||||||
|
const fetch = async (input: RequestInfo, init: RequestInit = {}) => {
|
||||||
if (typeof input !== 'string') {
|
if (typeof input !== 'string') {
|
||||||
return window.fetch(input, init);
|
return window.fetch(input, init);
|
||||||
}
|
}
|
||||||
|
|
@ -44,20 +53,50 @@ export default function(opts: Partial<Options> = {}) {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
const r = response.clone();
|
const r = response.clone();
|
||||||
r.text().then(text =>
|
|
||||||
|
r.text().then(text => {
|
||||||
|
const reqHs: Record<string, string> = {}
|
||||||
|
const resHs: Record<string, string> = {}
|
||||||
|
if (ihOpt !== true) {
|
||||||
|
function writeReqHeader([n, v]) {
|
||||||
|
if (!isHIgnoring(n)) { reqHs[n] = v }
|
||||||
|
}
|
||||||
|
if (init.headers instanceof Headers) {
|
||||||
|
init.headers.forEach((v, n) => writeReqHeader([n, v]))
|
||||||
|
} else if (Array.isArray(init.headers)) {
|
||||||
|
init.headers.forEach(writeReqHeader);
|
||||||
|
} else if (typeof init.headers === 'object') {
|
||||||
|
Object.entries(init.headers).forEach(writeReqHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.headers.forEach((v, n) => { if (!isHIgnoring(n)) resHs[n] = v })
|
||||||
|
}
|
||||||
|
const req = JSON.stringify({
|
||||||
|
headers: reqHs,
|
||||||
|
body: typeof init.body === 'string' ? init.body : '',
|
||||||
|
})
|
||||||
|
const res = JSON.stringify({
|
||||||
|
headers: resHs,
|
||||||
|
body: text,
|
||||||
|
})
|
||||||
app.send(
|
app.send(
|
||||||
Messages.Fetch(
|
Messages.Fetch(
|
||||||
typeof init.method === 'string' ? init.method : 'GET',
|
typeof init.method === 'string' ? init.method.toUpperCase() : 'GET',
|
||||||
input,
|
input,
|
||||||
typeof init.body === 'string' ? init.body : '',
|
req,
|
||||||
text,
|
res,
|
||||||
r.status,
|
r.status,
|
||||||
startTime + performance.timing.navigationStart,
|
startTime + performance.timing.navigationStart,
|
||||||
duration,
|
duration,
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
);
|
});
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
if (options.replaceDefault) {
|
||||||
|
window.fetch = fetch
|
||||||
|
}
|
||||||
|
return fetch;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"module": "es6",
|
"module": "es6",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"outDir": "./lib"
|
"outDir": "./lib",
|
||||||
|
"lib": ["es6", "dom", "es2017"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue