rusty-replay 1.0.10 β 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -22
- package/package.json +1 -1
package/README.md
CHANGED
@@ -1,26 +1,168 @@
|
|
1
|
+
# π¦ rusty-replay
|
2
|
+
|
3
|
+
μΉ ν΄λΌμ΄μΈνΈμμ λ°μν μ€λ₯λ₯Ό μμ§νκ³ , μ§μ μ¬μ©μ νλμ 리νλ μ΄ ννλ‘ ν¨κ» μ μ‘νλ κ²½λ μ€λ₯ μΆμ λꡬμ
λλ€.
|
4
|
+
|
5
|
+
`rrweb` κΈ°λ°μ μ¬μ©μ νλ 리νλ μ΄ κΈ°λ₯κ³Ό `axios` μλ¬ μλ μ μ‘κΉμ§ μ§μν©λλ€.
|
6
|
+
|
7
|
+
## π¦ μ€μΉ
|
8
|
+
|
9
|
+
```bash
|
10
|
+
npm install rusty-replay
|
11
|
+
```
|
12
|
+
|
13
|
+
---
|
14
|
+
|
15
|
+
## βοΈ μ΄κΈ°ν
|
16
|
+
|
17
|
+
Next.jsμμ `rusty-replay`λ μΌλ°μ μΌλ‘ `app/providers.tsx` λλ `layout.tsx`μμ μ΄κΈ°νν©λλ€.
|
18
|
+
|
19
|
+
```ts
|
20
|
+
import { init } from 'rusty-replay';
|
21
|
+
|
22
|
+
init({
|
23
|
+
endpoint: 'https://your-api.com/batch-events',
|
24
|
+
apiKey: 'YOUR_PUBLIC_API_KEY',
|
25
|
+
flushIntervalMs: 10000, // λ²νΌκ° μ°° λκΉμ§ μ΅λ λκΈ° μκ° (ms)
|
26
|
+
maxBufferSize: 2000000, // μ μ‘ μ μ΅λ λ²νΌ μ¬μ΄μ¦ (bytes)
|
27
|
+
beforeErrorSec: 10, // μλ¬ λ°μ μ λͺ μ΄κ°μ μ΄λ²€νΈλ₯Ό 리νλ μ΄λ‘ λ¨κΈΈμ§
|
28
|
+
});
|
29
|
+
```
|
30
|
+
|
31
|
+
---
|
32
|
+
|
33
|
+
## π§ κΈλ‘λ² μλ¬ μλ μΊ‘μ²
|
34
|
+
|
35
|
+
```
|
36
|
+
ts
|
37
|
+
볡μ¬νΈμ§
|
38
|
+
import { setupGlobalErrorHandler } from 'rusty-replay';
|
39
|
+
|
40
|
+
setupGlobalErrorHandler();
|
41
|
+
|
42
|
+
```
|
43
|
+
|
44
|
+
- `window.onerror`
|
45
|
+
- `window.onunhandledrejection`
|
46
|
+
μ μλ κ°μ§νμ¬ μ€λ₯λ₯Ό μλ²λ‘ μ μ‘ν©λλ€.
|
47
|
+
|
48
|
+
---
|
49
|
+
|
50
|
+
## π§ Axios μλ¬ μλ μ μ‘
|
51
|
+
|
52
|
+
```ts
|
53
|
+
import axios from 'axios';
|
54
|
+
import { captureException, AdditionalInfo } from 'rusty-replay';
|
55
|
+
|
56
|
+
axios.interceptors.response.use(
|
57
|
+
(res) => res,
|
58
|
+
(error) => {
|
59
|
+
if (axios.isAxiosError(error)) {
|
60
|
+
const additionalInfo: Partial<AdditionalInfo> = {
|
61
|
+
pageUrl: window.location.href,
|
62
|
+
request: {
|
63
|
+
url: error.config?.url ?? '',
|
64
|
+
method: error.config?.method ?? '',
|
65
|
+
headers: error.config?.headers ?? {},
|
66
|
+
},
|
67
|
+
response: {
|
68
|
+
status: error.response?.status ?? 0,
|
69
|
+
statusText: error.response?.statusText ?? '',
|
70
|
+
data: {
|
71
|
+
message: error.response?.data?.message ?? '',
|
72
|
+
errorCode: error.response?.data?.errorCode ?? '',
|
73
|
+
},
|
74
|
+
},
|
75
|
+
};
|
76
|
+
|
77
|
+
captureException(
|
78
|
+
error instanceof Error ? error : new Error('API μμ² μ€ν¨'),
|
79
|
+
additionalInfo
|
80
|
+
);
|
81
|
+
}
|
82
|
+
|
83
|
+
return Promise.reject(error);
|
84
|
+
}
|
85
|
+
);
|
86
|
+
```
|
87
|
+
|
88
|
+
---
|
89
|
+
|
90
|
+
## π₯οΈ React Error Boundary ν΅ν©
|
91
|
+
|
1
92
|
```tsx
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
maxBufferSize: 200, // νμ μ΅λ 200건κΉμ§
|
9
|
-
beforeErrorSec: 30, // μλ¬ μ 30μ΄ λ¦¬νλ μ΄ μ΄λ²€νΈ ν¬ν¨
|
10
|
-
});
|
11
|
-
}, []);
|
12
|
-
|
13
|
-
<button
|
14
|
-
onClick={() => {
|
15
|
-
captureException(new Error('μλμ μΌλ‘ λ°μμν¨ μλ μλ¬'), {
|
16
|
-
additionalInfo: {
|
17
|
-
context: 'μλ μλ¬ λ¦¬ν¬ν
ν
μ€νΈ',
|
18
|
-
button: 'μλ μλ¬ λ²νΌ',
|
19
|
-
},
|
20
|
-
});
|
93
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
94
|
+
import { captureException } from 'rusty-replay';
|
95
|
+
|
96
|
+
<ErrorBoundaryFallbackComponent={() => <div>μ€λ₯κ° λ°μνμ΅λλ€.</div>}
|
97
|
+
onError={(error, info) => {
|
98
|
+
captureException(error);
|
21
99
|
}}
|
22
|
-
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
23
100
|
>
|
24
|
-
|
25
|
-
</
|
101
|
+
<App />
|
102
|
+
</ErrorBoundary>
|
103
|
+
|
104
|
+
```
|
105
|
+
|
106
|
+
---
|
107
|
+
|
108
|
+
## π 리νλ μ΄ μ¬μ (rrweb-player)
|
109
|
+
|
110
|
+
```tsx
|
111
|
+
import { decompressFromBase64 } from 'rusty-replay';
|
112
|
+
import 'rrweb-player/dist/style.css';
|
113
|
+
|
114
|
+
const events = decompressFromBase64(error.replay);
|
115
|
+
|
116
|
+
new Player({
|
117
|
+
target: document.getElementById('player')!,
|
118
|
+
props: {
|
119
|
+
events,
|
120
|
+
width: 1000,
|
121
|
+
height: 600,
|
122
|
+
autoPlay: false,
|
123
|
+
showController: true,
|
124
|
+
skipInactive: true,
|
125
|
+
},
|
126
|
+
});
|
127
|
+
```
|
128
|
+
|
129
|
+
---
|
130
|
+
|
131
|
+
## π μλ²λ‘ μ μ‘λλ λ°μ΄ν° Payload
|
132
|
+
|
133
|
+
```ts
|
134
|
+
{
|
135
|
+
message: 'Uncaught TypeError: ...',
|
136
|
+
stacktrace: 'TypeError: ...',
|
137
|
+
replay: 'compressedBase64Data',
|
138
|
+
environment: 'production',
|
139
|
+
browser: 'Chrome 123.0',
|
140
|
+
os: 'macOS 14',
|
141
|
+
userAgent: '...',
|
142
|
+
appVersion: '1.0.0',
|
143
|
+
apiKey: 'YOUR_API_KEY',
|
144
|
+
additionalInfo: {
|
145
|
+
request: {...},
|
146
|
+
response: {...},
|
147
|
+
pageUrl: 'https://your.site/path'
|
148
|
+
},
|
149
|
+
userId: 123
|
150
|
+
}
|
151
|
+
|
26
152
|
```
|
153
|
+
|
154
|
+
---
|
155
|
+
|
156
|
+
## π API μ°Έκ³
|
157
|
+
|
158
|
+
### `init(options: InitOptions)`
|
159
|
+
|
160
|
+
μ΅μ
μ€λͺ
:
|
161
|
+
|
162
|
+
| μ΅μ
| νμ
| μ€λͺ
|
|
163
|
+
| ----------------- | ------ | -------------------------------------- |
|
164
|
+
| `endpoint` | string | μλ¬ μμ§ μλ²μ μλν¬μΈνΈ |
|
165
|
+
| `apiKey` | string | νλ‘μ νΈ μλ³μ© API Key |
|
166
|
+
| `flushIntervalMs` | number | μλ¬ μ μ‘ κ°κ²© (κΈ°λ³Έ: 10μ΄) |
|
167
|
+
| `maxBufferSize` | number | μ΅λ μ μ‘ λ²νΌ ν¬κΈ° |
|
168
|
+
| `beforeErrorSec` | number | 리νλ μ΄ μμ§ κ΅¬κ° (κΈ°λ³Έ: 10μ΄ μ κΉμ§) |
|