Skip to content

Commit 1fdcbdc

Browse files
committed
feat: network plugin (xhr/fetch)
1 parent fd9d274 commit 1fdcbdc

File tree

20 files changed

+1170
-1
lines changed

20 files changed

+1170
-1
lines changed

.changeset/config.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"@rrweb/rrweb-plugin-sequential-id-record",
2323
"@rrweb/rrweb-plugin-sequential-id-replay",
2424
"@rrweb/rrweb-plugin-canvas-webrtc-record",
25-
"@rrweb/rrweb-plugin-canvas-webrtc-replay"
25+
"@rrweb/rrweb-plugin-canvas-webrtc-replay",
26+
"@rrweb/rrweb-plugin-network-record",
27+
"@rrweb/rrweb-plugin-network-replay"
2628
]
2729
],
2830
"linked": [],

.changeset/six-lions-guess.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@rrweb/rrweb-plugin-network-record": patch
3+
"@rrweb/rrweb-plugin-network-replay": patch
4+
---
5+
6+
network-plugin

docs/recipes/network.md

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# network recorder and replayer
2+
3+
Starting from v2.0.0, we add the plugin to record network output.
4+
This feature aims to provide developers with more information about the bug scene. There are some options for recording and replaying network output.
5+
6+
### Enable recording network
7+
8+
You can enable using default option like this:
9+
10+
```js
11+
import rrweb from 'rrweb';
12+
import { getRecordNetworkPlugin } from '@rrweb/rrweb-plugin-network-record';
13+
14+
rrweb.record({
15+
emit: function emit(event) {
16+
events.push(event);
17+
},
18+
// to use default record option
19+
plugins: [getRecordNetworkPlugin()],
20+
});
21+
```
22+
23+
You can also customize the behavior of logger like this:
24+
25+
```js
26+
import rrweb from 'rrweb';
27+
import { getRecordNetworkPlugin } from '@rrweb/rrweb-plugin-network-record';
28+
29+
rrweb.record({
30+
emit: function emit(event) {
31+
fetch('https://api.my-server.com/events', {
32+
method: 'POST',
33+
headers: {
34+
'Content-Type': 'application/json',
35+
},
36+
body: JSON.stringify({
37+
events: [event],
38+
}),
39+
});
40+
},
41+
// customized record options
42+
plugins: [
43+
getRecordNetworkPlugin({
44+
initiatorTypes: ['fetch', 'xmlhttprequest'],
45+
// block recording event for request to upload events to server
46+
ignoreRequestFn: (request) => {
47+
if (request.url === 'https://api.my-server.com/events') {
48+
return true;
49+
}
50+
return false;
51+
},
52+
recordHeaders: true,
53+
recordBody: true,
54+
recordInitialRequests: false,
55+
}),
56+
],
57+
});
58+
```
59+
60+
**alert**: If you are uploading events to a server, you should always use `ignoreRequestFn` to block recording events for these requests or else you will cause a nasty loop.
61+
62+
All options are described below:
63+
64+
| key | default | description |
65+
| ---------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66+
| initiatorTypes | `['fetch','xmlhttprequest','img',...]` | Default value contains names of all [initiator types](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/initiatorType). You can override it by setting the types you need. |
67+
| ignoreRequestFn | `() => false` | Block recording events for specific requests |
68+
| recordHeaders | `false` | Record the request & response headers for `fetch` and `xmlhttprequest` requests |
69+
| recordBody | `false` | Record the request & response bodies for `fetch` and `xmlhttprequest` requests |
70+
| recordInitialRequests | `false` | Record an event for all requests prior to rrweb.record() being called |
71+
72+
## replay network
73+
74+
It is up to you to decide how to best replay your network events using the `onNetworkData` callback.
75+
76+
```js
77+
import rrweb from 'rrweb';
78+
import { getReplayNetworkPlugin } from '@rrweb/rrweb-plugin-network-replay';
79+
80+
const replayer = new rrweb.Replayer(events, {
81+
plugins: [
82+
getReplayNetworkPlugin({
83+
onNetworkData: ({ requests }) => {
84+
for (const request of requests) {
85+
const url = request.url;
86+
const method = request.method;
87+
const status = request.status;
88+
console.log(`${method} ${url} ${status}`);
89+
}
90+
},
91+
}),
92+
],
93+
});
94+
replayer.play();
95+
```
96+
97+
Description of replay option is as follows:
98+
99+
| key | default | description |
100+
| ------------- | ----------- | ------------------------------------------------------------------------------------------ |
101+
| onNetworkData | `undefined` | You could use this interface to replay the network requests in a simulated browser console |
102+
103+
## technical implementation
104+
105+
This implementation records [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) by patching their object & methods. We record document navigation using [`PerformanceNavigationTiming`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming) and we use [`PerformanceResourceTiming`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming) for recording everything else (script, img, link etc.) via [`PerformanceObserver`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver) API.
106+
107+
For more information please see [[network-plugin] Feat: Capture network events #1105](https://github.com/rrweb-io/rrweb/pull/1105) PR.

docs/recipes/plugin.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The plugin API is designed to extend the function of rrweb without bump the size
1010
- [@rrweb/rrweb-plugin-sequential-id-replay](packages/plugins/rrweb-plugin-sequential-id-replay): A plugin for replaying sequential IDs.
1111
- [@rrweb/rrweb-plugin-canvas-webrtc-record](packages/plugins/rrweb-plugin-canvas-webrtc-record): A plugin for stream `<canvas>` via WebRTC.
1212
- [@rrweb/rrweb-plugin-canvas-webrtc-replay](packages/plugins/rrweb-plugin-canvas-webrtc-replay): A plugin for playing streamed `<canvas>` via WebRTC.
13+
- [@rrweb/rrweb-plugin-network-record](packages/plugins/rrweb-plugin-network-record): A plugin for recording network requests (xhr/fetch).
14+
- [@rrweb/rrweb-plugin-network-replay](packages/plugins/rrweb-plugin-network-replay): A plugin for replaying network requests (xhr/fetch).
1315

1416
## Interface
1517

docs/recipes/plugin.zh_CN.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
- [@rrweb/rrweb-plugin-sequential-id-replay](packages/plugins/rrweb-plugin-sequential-id-replay):一个用于回放顺序 ID 的插件。
1111
- [@rrweb/rrweb-plugin-canvas-webrtc-record](packages/plugins/rrweb-plugin-canvas-webrtc-record):一个用于通过 WebRTC 流式传输 `<canvas>` 的插件。
1212
- [@rrweb/rrweb-plugin-canvas-webrtc-replay](packages/plugins/rrweb-plugin-canvas-webrtc-replay):一个用于通过 WebRTC 播放流式 `<canvas>` 的插件。
13+
- [@rrweb/rrweb-plugin-network-record](packages/plugins/rrweb-plugin-network-record): 一个用于记录网络请求的插件 (xhr/fetch)。
14+
- [@rrweb/rrweb-plugin-network-replay](packages/plugins/rrweb-plugin-network-replay): 一个用于回放网络请求的插件 (xhr/fetch)。
1315

1416
## 接口
1517

guide.md

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Besides the `rrweb` and `@rrweb/record` packages, rrweb also provides other pack
5656
- [@rrweb/rrweb-plugin-sequential-id-replay](packages/plugins/rrweb-plugin-sequential-id-replay): A plugin for replaying sequential IDs.
5757
- [@rrweb/rrweb-plugin-canvas-webrtc-record](packages/plugins/rrweb-plugin-canvas-webrtc-record): A plugin for stream `<canvas>` via WebRTC.
5858
- [@rrweb/rrweb-plugin-canvas-webrtc-replay](packages/plugins/rrweb-plugin-canvas-webrtc-replay): A plugin for playing streamed `<canvas>` via WebRTC.
59+
- [@rrweb/rrweb-plugin-network-record](packages/plugins/rrweb-plugin-network-record): A plugin for recording network requests (xhr/fetch).
60+
- [@rrweb/rrweb-plugin-network-replay](packages/plugins/rrweb-plugin-network-replay): A plugin for replaying network requests (xhr/fetch).
5961

6062
### NPM
6163

guide.zh_CN.md

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ rrweb 代码分为录制和回放两部分,大多数时候用户在被录制
5353
- [@rrweb/rrweb-plugin-sequential-id-replay](packages/plugins/rrweb-plugin-sequential-id-replay):一个用于回放顺序 ID 的插件。
5454
- [@rrweb/rrweb-plugin-canvas-webrtc-record](packages/plugins/rrweb-plugin-canvas-webrtc-record):一个用于通过 WebRTC 流式传输 `<canvas>` 的插件。
5555
- [@rrweb/rrweb-plugin-canvas-webrtc-replay](packages/plugins/rrweb-plugin-canvas-webrtc-replay):一个用于通过 WebRTC 播放流式 `<canvas>` 的插件。
56+
- [@rrweb/rrweb-plugin-network-record](packages/plugins/rrweb-plugin-network-record): 一个用于记录网络请求的插件 (xhr/fetch)。
57+
- [@rrweb/rrweb-plugin-network-replay](packages/plugins/rrweb-plugin-network-replay): 一个用于回放网络请求的插件 (xhr/fetch)。
5658

5759
### 通过 npm 引入
5860

packages/plugins/rrweb-plugin-network-record/CHANGELOG.md

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# @rrweb/rrweb-plugin-network-record
2+
3+
Please refer to the [network recipe](../../../docs/recipes/network.md) on how to use this plugin.
4+
See the [guide](../../../guide.md) for more info on rrweb.
5+
6+
## Sponsors
7+
8+
[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site.
9+
10+
### Gold Sponsors 🥇
11+
12+
<div dir="auto">
13+
14+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/0/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
15+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/1/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
16+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/2/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
17+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/3/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
18+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/4/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
19+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/5/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
20+
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/6/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
21+
22+
</div>
23+
24+
### Silver Sponsors 🥈
25+
26+
<div dir="auto">
27+
28+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/0/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
29+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/1/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
30+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/2/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
31+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/3/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
32+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/4/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
33+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/5/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
34+
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/6/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
35+
36+
</div>
37+
38+
### Bronze Sponsors 🥉
39+
40+
<div dir="auto">
41+
42+
<a href="https://opencollective.com/rrweb/tiers/sponsors/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/0/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
43+
<a href="https://opencollective.com/rrweb/tiers/sponsors/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/1/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
44+
<a href="https://opencollective.com/rrweb/tiers/sponsors/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/2/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
45+
<a href="https://opencollective.com/rrweb/tiers/sponsors/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/3/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
46+
<a href="https://opencollective.com/rrweb/tiers/sponsors/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/4/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
47+
<a href="https://opencollective.com/rrweb/tiers/sponsors/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/5/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
48+
<a href="https://opencollective.com/rrweb/tiers/sponsors/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/6/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
49+
<a href="https://opencollective.com/rrweb/tiers/sponsors/7/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/7/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
50+
<a href="https://opencollective.com/rrweb/tiers/sponsors/8/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/8/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
51+
52+
</div>
53+
54+
### Backers
55+
56+
<a href="https://opencollective.com/rrweb#sponsor" rel="nofollow"><img src="https://opencollective.com/rrweb/tiers/backers.svg?avatarHeight=36"></a>
57+
58+
## Core Team Members
59+
60+
<table>
61+
<tr>
62+
<td align="center">
63+
<a href="https://github.com/Yuyz0112">
64+
<img
65+
src="https://avatars.githubusercontent.com/u/13651389?s=100"
66+
width="100px;"
67+
alt=""
68+
/>
69+
<br /><sub><b>Yuyz0112</b></sub>
70+
<br /><br />
71+
</a>
72+
</td>
73+
<td align="center">
74+
<a href="https://github.com/YunFeng0817">
75+
<img
76+
src="https://avatars.githubusercontent.com/u/27533910?s=100"
77+
width="100px;"
78+
alt=""
79+
/>
80+
<br /><sub><b>Yun Feng</b></sub>
81+
<br /><br />
82+
</a>
83+
</td>
84+
<td align="center">
85+
<a href="https://github.com/eoghanmurray">
86+
<img
87+
src="https://avatars.githubusercontent.com/u/156780?s=100"
88+
width="100px;"
89+
alt=""
90+
/>
91+
<br /><sub><b>eoghanmurray</b></sub>
92+
<br /><br />
93+
</a>
94+
</td>
95+
<td align="center">
96+
<a href="https://github.com/Juice10">
97+
<img
98+
src="https://avatars.githubusercontent.com/u/4106?s=100"
99+
width="100px;"
100+
alt=""
101+
/>
102+
<br /><sub><b>Juice10</b></sub>
103+
<br /><sub>open for rrweb consulting</sub>
104+
</a>
105+
</td>
106+
</tr>
107+
</table>
108+
109+
## Who's using rrweb?
110+
111+
<table>
112+
<tr>
113+
<td align="center">
114+
<a href="http://www.smartx.com/" target="_blank">
115+
<img width="195px" src="https://www.rrweb.io/logos/smartx.png">
116+
</a>
117+
</td>
118+
<td align="center">
119+
<a href="https://posthog.com?utm_source=rrweb&utm_medium=sponsorship&utm_campaign=open-source-sponsorship" target="_blank">
120+
<img width="195px" src="https://www.rrweb.io/logos/posthog.png">
121+
</a>
122+
</td>
123+
<td align="center">
124+
<a href="https://statcounter.com/session-replay/" target="_blank">
125+
<img width="195px" src="https://statcounter.com/images/logo-statcounter-arc-blue.svg">
126+
</a>
127+
</td>
128+
<td align="center">
129+
<a href="https://recordonce.com/" target="_blank">
130+
<img width="195px" alt="Smart screen recording for SaaS" src="https://uploads-ssl.webflow.com/5f3d133183156245630d4446/5f3d1940abe8db8612c23521_Record-Once-logo-554x80px.svg">
131+
</a>
132+
</td>
133+
</tr>
134+
<tr>
135+
<td align="center">
136+
<a href="https://cux.io" target="_blank">
137+
<img style="padding: 8px" alt="The first ever UX automation tool" width="195px" src="https://cux.io/cux-logo.svg">
138+
</a>
139+
</td>
140+
<td align="center">
141+
<a href="https://remsupp.com" target="_blank">
142+
<img style="padding: 8px" alt="Remote Access & Co-Browsing" width="195px" src="https://remsupp.com/images/logo.png">
143+
</a>
144+
</td>
145+
<td align="center">
146+
<a href="https://highlight.io" target="_blank">
147+
<img style="padding: 8px" alt="The open source, fullstack Monitoring Platform." width="195px" src="https://github.com/highlight/highlight/raw/main/highlight.io/public/images/logo.png">
148+
</a>
149+
</td>
150+
<td align="center">
151+
<a href="https://analyzee.io" target="_blank">
152+
<img style="padding: 8px" alt="Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions." width="195px" src="https://cdn.analyzee.io/assets/analyzee-logo.png">
153+
</a>
154+
</td>
155+
</tr>
156+
<tr>
157+
<td align="center">
158+
<a href="https://requestly.io" target="_blank">
159+
<img style="padding: 8px" alt="Intercept, Modify, Record & Replay HTTP Requests." width="195px" src="https://github.com/requestly/requestly/assets/16779465/652552db-c867-44cb-9bb5-94a2026e04ca">
160+
</a>
161+
</td>
162+
<td align="center">
163+
<a href="https://gleap.io" target="_blank">
164+
<img style="padding: 8px" alt="In-app bug reporting & customer feedback platform." width="195px" src="https://assets-global.website-files.com/6506f3f29c68b1724807619d/6506f56010237164c6306591_GleapLogo.svg">
165+
</a>
166+
</td>
167+
<td align="center">
168+
<a href="https://uxwizz.com" target="_blank">
169+
<img style="padding: 8px" alt="Self-hosted website analytics with heatmaps and session recordings." width="195px" src="https://github.com/UXWizz/public-files/raw/main/assets/logo.png">
170+
</a>
171+
</td>
172+
<td align="center">
173+
<a href="https://www.howdygo.com" target="_blank">
174+
<img style="padding: 8px" alt="Interactive product demos for small marketing teams" width="195px" src="https://assets-global.website-files.com/650afb446f1dd5bd410f00cc/650b2cec6188ff54dd9b01e1_Logo.svg">
175+
</a>
176+
</td>
177+
</tr>
178+
</table>

0 commit comments

Comments
 (0)