rusty-replay
Version:
Lightweight error tracking and replay system for React apps using rrweb and Rust-powered backend integration.
166 lines (126 loc) β’ 3.92 kB
Markdown
# π¦ rusty-replay
μΉ ν΄λΌμ΄μΈνΈμμ λ°μν μ€λ₯λ₯Ό μμ§νκ³ , μ§μ μ¬μ©μ νλμ 리νλ μ΄ ννλ‘ ν¨κ» μ μ‘νλ κ²½λ μ€λ₯ μΆμ λꡬμ
λλ€.
`rrweb` κΈ°λ°μ μ¬μ©μ νλ 리νλ μ΄ κΈ°λ₯κ³Ό `axios` μλ¬ μλ μ μ‘κΉμ§ μ§μν©λλ€.
## π¦ μ€μΉ
```bash
npm install rusty-replay
```
## βοΈ μ΄κΈ°ν
Next.jsμμ `rusty-replay`λ μΌλ°μ μΌλ‘ `app/providers.tsx` λλ `layout.tsx`μμ μ΄κΈ°νν©λλ€.
```ts
import { init } from 'rusty-replay';
init({
endpoint: 'https://your-api.com/batch-events',
apiKey: 'YOUR_PUBLIC_API_KEY',
flushIntervalMs: 10000, // λ²νΌκ° μ°° λκΉμ§ μ΅λ λκΈ° μκ° (ms)
maxBufferSize: 2000000, // μ μ‘ μ μ΅λ λ²νΌ μ¬μ΄μ¦ (bytes)
beforeErrorSec: 10, // μλ¬ λ°μ μ λͺ μ΄κ°μ μ΄λ²€νΈλ₯Ό 리νλ μ΄λ‘ λ¨κΈΈμ§
});
```
## π§ κΈλ‘λ² μλ¬ μλ μΊ‘μ²
```ts
import { setupGlobalErrorHandler } from 'rusty-replay';
setupGlobalErrorHandler();
```
- `window.onerror`
- `window.onunhandledrejection`
μ μλ κ°μ§νμ¬ μ€λ₯λ₯Ό μλ²λ‘ μ μ‘ν©λλ€.
## π§ Axios μλ¬ μλ μ μ‘
```ts
import axios from 'axios';
import { captureException, AdditionalInfo } from 'rusty-replay';
axios.interceptors.response.use(
(res) => res,
(error) => {
if (axios.isAxiosError(error)) {
const additionalInfo: Partial<AdditionalInfo> = {
pageUrl: window.location.href,
request: {
url: error.config?.url ?? '',
method: error.config?.method ?? '',
headers: error.config?.headers ?? {},
},
response: {
status: error.response?.status ?? 0,
statusText: error.response?.statusText ?? '',
data: {
message: error.response?.data?.message ?? '',
errorCode: error.response?.data?.errorCode ?? '',
},
},
};
captureException(
error instanceof Error ? error : new Error('API μμ² μ€ν¨'),
additionalInfo
);
}
return Promise.reject(error);
}
);
```
## π₯οΈ React Error Boundary ν΅ν©
```tsx
import { ErrorBoundary } from 'react-error-boundary';
import { captureException } from 'rusty-replay';
<ErrorBoundaryFallbackComponent={() => <div>μ€λ₯κ° λ°μνμ΅λλ€.</div>}
onError={(error, info) => {
captureException(error);
}}
>
<App />
</ErrorBoundary>
```
## π 리νλ μ΄ μ¬μ (rrweb-player)
```tsx
import { decompressFromBase64 } from 'rusty-replay';
import 'rrweb-player/dist/style.css';
const events = decompressFromBase64(error.replay);
new Player({
target: document.getElementById('player')!,
props: {
events,
width: 1000,
height: 600,
autoPlay: false,
showController: true,
skipInactive: true,
},
});
```
## π μλ²λ‘ μ μ‘λλ λ°μ΄ν° Payload
```ts
{
message: 'Uncaught TypeError: ...',
stacktrace: 'TypeError: ...',
replay: 'compressedBase64Data',
environment: 'production',
browser: 'Chrome 123.0',
os: 'macOS 14',
userAgent: '...',
appVersion: '1.0.0',
apiKey: 'YOUR_API_KEY',
additionalInfo: {
request: {...},
response: {...},
pageUrl: 'https://your.site/path'
},
userId: 123
}
```
## π API μ°Έκ³
### `init(options: InitOptions)`
μ΅μ
μ€λͺ
:
| μ΅μ
| νμ
| μ€λͺ
|
| ----------------- | ------ | -------------------------------------- |
| `endpoint` | string | μλ¬ μμ§ μλ²μ μλν¬μΈνΈ |
| `apiKey` | string | νλ‘μ νΈ μλ³μ© API Key |
| `flushIntervalMs` | number | μλ¬ μ μ‘ κ°κ²© (κΈ°λ³Έ: 10μ΄) |
| `maxBufferSize` | number | μ΅λ μ μ‘ λ²νΌ ν¬κΈ° |
| `beforeErrorSec` | number | 리νλ μ΄ μμ§ κ΅¬κ° (κΈ°λ³Έ: 10μ΄ μ κΉμ§) |