@djangocfg/monitor 2.1.216 → 2.1.217
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 +46 -254
- package/dist/client.cjs +12 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +9 -1
- package/dist/client.d.ts +9 -1
- package/dist/client.mjs +12 -0
- package/dist/client.mjs.map +1 -1
- package/dist/index.cjs +880 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +867 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -2
- package/src/client/MonitorProvider.tsx +41 -0
- package/src/client/index.ts +2 -0
- package/src/index.ts +3 -2
- package/src/types/index.ts +2 -1
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @djangocfg/monitor
|
|
2
2
|
|
|
3
|
-
Browser
|
|
4
|
-
|
|
5
|
-
**Part of [DjangoCFG](https://djangocfg.com)** — modern Django framework for production-ready SaaS applications.
|
|
3
|
+
Browser + server error monitoring SDK for [DjangoCFG](https://djangocfg.com) backends.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -10,112 +8,27 @@ Browser error and event monitoring SDK for django-cfg backends.
|
|
|
10
8
|
pnpm add @djangocfg/monitor
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
## What's Inside
|
|
14
|
-
|
|
15
|
-
- **JS Error Capture** — `window.onerror` + `unhandledrejection` with SHA-256 fingerprinting
|
|
16
|
-
- **Console Capture** — intercepts `console.warn` / `console.error` (consola reporter or native patch)
|
|
17
|
-
- **Network Monitoring** — `monitoredFetch` wrapper reports non-2xx responses
|
|
18
|
-
- **Zod Validation Errors** — listens to `zod-validation-error` custom events (auto-integrates with `@djangocfg/api`)
|
|
19
|
-
- **Session Tracking** — anonymous `fm_session_id` UUID in localStorage + cookie
|
|
20
|
-
- **Server-side Capture** — Node.js / Edge Runtime safe, no browser APIs
|
|
21
|
-
- **Auto-flush** — batched delivery via generated API client; `KeepAliveFetchAdapter` survives page unload
|
|
22
|
-
- **Type-safe** — types generated from Django OpenAPI schema via `make gen`
|
|
23
|
-
|
|
24
11
|
## Entry Points
|
|
25
12
|
|
|
26
|
-
| Entry |
|
|
27
|
-
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
## Package Structure
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
src/
|
|
36
|
-
├── _api/
|
|
37
|
-
│ ├── BaseClient.ts # monitorApi singleton (MemoryStorageAdapter)
|
|
38
|
-
│ ├── index.ts # Re-exports types, enums, monitorApi
|
|
39
|
-
│ └── generated/
|
|
40
|
-
│ └── cfg_monitor/ # Auto-generated — do not edit
|
|
41
|
-
│ ├── http.ts # FetchAdapter, KeepAliveFetchAdapter
|
|
42
|
-
│ └── ...
|
|
43
|
-
├── types/
|
|
44
|
-
│ ├── events.ts # EventType, EventLevel, MonitorEvent
|
|
45
|
-
│ └── config.ts # MonitorConfig, ServerMonitorConfig
|
|
46
|
-
├── client/
|
|
47
|
-
│ ├── index.ts # FrontendMonitor, monitoredFetch, getSessionId
|
|
48
|
-
│ ├── capture/
|
|
49
|
-
│ │ ├── js-errors.ts # window.onerror + unhandledrejection
|
|
50
|
-
│ │ ├── console.ts # consola reporter / console.* patch
|
|
51
|
-
│ │ ├── network.ts # monitoredFetch wrapper
|
|
52
|
-
│ │ ├── validation.ts # zod-validation-error CustomEvent listener
|
|
53
|
-
│ │ ├── session.ts # UUID session management
|
|
54
|
-
│ │ └── fingerprint.ts # SHA-256 deduplication fingerprint
|
|
55
|
-
│ ├── store/
|
|
56
|
-
│ │ └── index.ts # zustand vanilla event buffer
|
|
57
|
-
│ └── transport/
|
|
58
|
-
│ └── ingest.ts # sendBatch via monitorApi / KeepAliveFetchAdapter
|
|
59
|
-
├── server/
|
|
60
|
-
│ └── index.ts # serverMonitor singleton
|
|
61
|
-
└── index.ts # Types-only re-export
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## How It Works
|
|
65
|
-
|
|
66
|
-
All transport uses the **generated API client** (`monitorApi`) — no hardcoded URLs or raw `fetch` calls.
|
|
67
|
-
|
|
68
|
-
- **Normal flush** → `monitorApi.monitor.ingestCreate(batch)` (standard `FetchAdapter`)
|
|
69
|
-
- **Page unload flush** → separate `monitorApiBeacon` instance with `KeepAliveFetchAdapter` — sets `keepalive: true` on every request, so the browser completes delivery even after navigation
|
|
70
|
-
|
|
71
|
-
**Automatic error collection** (no extra setup after `FrontendMonitor.init()`):
|
|
72
|
-
|
|
73
|
-
| Source | Mechanism |
|
|
74
|
-
|--------|-----------|
|
|
75
|
-
| JS exceptions | `window.onerror` + `unhandledrejection` |
|
|
76
|
-
| Console warnings/errors | consola reporter or `console.*` patch |
|
|
77
|
-
| `@djangocfg/api` Zod failures | `zod-validation-error` CustomEvent |
|
|
78
|
-
| Network errors | `monitoredFetch` wrapper |
|
|
79
|
-
| Server exceptions | `serverMonitor.captureError()` |
|
|
80
|
-
|
|
81
|
-
## Client Usage (React / Next.js)
|
|
13
|
+
| Entry | Use |
|
|
14
|
+
|-------|-----|
|
|
15
|
+
| `@djangocfg/monitor` | Types only — server-safe |
|
|
16
|
+
| `@djangocfg/monitor/client` | Browser SDK (`'use client'`) |
|
|
17
|
+
| `@djangocfg/monitor/server` | Node.js / Edge Runtime |
|
|
82
18
|
|
|
83
|
-
|
|
19
|
+
## Client (React / Next.js)
|
|
84
20
|
|
|
85
|
-
|
|
86
|
-
// components/MonitorProvider.tsx
|
|
87
|
-
'use client'
|
|
88
|
-
|
|
89
|
-
import { useEffect } from 'react'
|
|
90
|
-
import { FrontendMonitor } from '@djangocfg/monitor/client'
|
|
91
|
-
|
|
92
|
-
export function MonitorProvider() {
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
FrontendMonitor.init({
|
|
95
|
-
project: process.env.NEXT_PUBLIC_PROJECT_NAME ?? 'my-app',
|
|
96
|
-
environment: process.env.NODE_ENV,
|
|
97
|
-
// baseUrl: 'https://api.myapp.com', // default: same origin
|
|
98
|
-
// flushInterval: 5000, // default: 5s
|
|
99
|
-
// captureJsErrors: true, // default: true
|
|
100
|
-
// captureConsole: true, // default: true
|
|
101
|
-
// debug: false,
|
|
102
|
-
})
|
|
103
|
-
return () => FrontendMonitor.destroy()
|
|
104
|
-
}, [])
|
|
105
|
-
|
|
106
|
-
return null
|
|
107
|
-
}
|
|
108
|
-
```
|
|
21
|
+
Drop `MonitorProvider` into your root layout — that's all:
|
|
109
22
|
|
|
110
23
|
```tsx
|
|
111
24
|
// app/layout.tsx
|
|
112
|
-
import { MonitorProvider } from '
|
|
25
|
+
import { MonitorProvider } from '@djangocfg/monitor/client'
|
|
113
26
|
|
|
114
27
|
export default function RootLayout({ children }) {
|
|
115
28
|
return (
|
|
116
|
-
<html
|
|
29
|
+
<html>
|
|
117
30
|
<body>
|
|
118
|
-
<MonitorProvider />
|
|
31
|
+
<MonitorProvider project="my-app" environment={process.env.NODE_ENV} />
|
|
119
32
|
{children}
|
|
120
33
|
</body>
|
|
121
34
|
</html>
|
|
@@ -123,18 +36,18 @@ export default function RootLayout({ children }) {
|
|
|
123
36
|
}
|
|
124
37
|
```
|
|
125
38
|
|
|
126
|
-
|
|
39
|
+
`MonitorProvider` initialises `FrontendMonitor` on mount and tears it down on unmount.
|
|
127
40
|
|
|
128
|
-
|
|
129
|
-
import { monitoredFetch } from '@djangocfg/monitor/client'
|
|
41
|
+
**What gets captured automatically** (no extra setup):
|
|
130
42
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
43
|
+
| Source | Mechanism |
|
|
44
|
+
|--------|-----------|
|
|
45
|
+
| JS exceptions | `window.onerror` + `unhandledrejection` |
|
|
46
|
+
| Console warnings/errors | consola reporter or `console.*` patch |
|
|
47
|
+
| `@djangocfg/api` Zod failures | `zod-validation-error` CustomEvent |
|
|
48
|
+
| Network errors | `monitoredFetch` wrapper |
|
|
136
49
|
|
|
137
|
-
### Manual
|
|
50
|
+
### Manual capture
|
|
138
51
|
|
|
139
52
|
```typescript
|
|
140
53
|
import { FrontendMonitor } from '@djangocfg/monitor/client'
|
|
@@ -149,40 +62,41 @@ FrontendMonitor.capture({
|
|
|
149
62
|
})
|
|
150
63
|
```
|
|
151
64
|
|
|
152
|
-
###
|
|
65
|
+
### Network wrapper
|
|
153
66
|
|
|
154
67
|
```typescript
|
|
155
|
-
import {
|
|
68
|
+
import { monitoredFetch } from '@djangocfg/monitor/client'
|
|
156
69
|
|
|
157
|
-
const
|
|
70
|
+
const res = await monitoredFetch('/api/orders', { method: 'POST', body: JSON.stringify(order) })
|
|
158
71
|
```
|
|
159
72
|
|
|
160
|
-
|
|
73
|
+
### Config options
|
|
74
|
+
|
|
75
|
+
| Option | Type | Default | Description |
|
|
76
|
+
|--------|------|---------|-------------|
|
|
77
|
+
| `project` | `string` | `''` | Project name sent with every event |
|
|
78
|
+
| `environment` | `string` | `''` | `production` / `staging` / `development` |
|
|
79
|
+
| `baseUrl` | `string` | same origin | Base URL of the django-cfg backend |
|
|
80
|
+
| `flushInterval` | `number` | `5000` | Buffer flush interval (ms) |
|
|
81
|
+
| `maxBufferSize` | `number` | `20` | Max events before immediate flush |
|
|
82
|
+
| `captureJsErrors` | `boolean` | `true` | Capture `window.onerror` + unhandled rejections |
|
|
83
|
+
| `captureConsole` | `boolean` | `true` | Intercept `console.warn` / `console.error` |
|
|
84
|
+
| `debug` | `boolean` | `false` | Log init info to console |
|
|
161
85
|
|
|
162
|
-
|
|
86
|
+
## Server (Next.js Route Handlers)
|
|
163
87
|
|
|
164
88
|
```typescript
|
|
165
89
|
// lib/monitor.ts
|
|
166
90
|
import { serverMonitor } from '@djangocfg/monitor/server'
|
|
167
|
-
|
|
168
|
-
serverMonitor.configure({
|
|
169
|
-
project: process.env.PROJECT_NAME ?? 'my-app',
|
|
170
|
-
environment: process.env.NODE_ENV,
|
|
171
|
-
// baseUrl: 'https://api.myapp.com', // required if backend is on different origin
|
|
172
|
-
})
|
|
173
|
-
|
|
91
|
+
serverMonitor.configure({ project: 'my-app', environment: process.env.NODE_ENV })
|
|
174
92
|
export { serverMonitor }
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Route Handler
|
|
178
93
|
|
|
179
|
-
```typescript
|
|
180
94
|
// app/api/orders/route.ts
|
|
181
95
|
import { serverMonitor } from '@/lib/monitor'
|
|
182
96
|
|
|
183
97
|
export async function POST(req: Request) {
|
|
184
98
|
try {
|
|
185
|
-
// ...
|
|
99
|
+
// ...
|
|
186
100
|
} catch (err) {
|
|
187
101
|
await serverMonitor.captureError(err, { url: req.url })
|
|
188
102
|
return new Response('Internal Server Error', { status: 500 })
|
|
@@ -190,152 +104,30 @@ export async function POST(req: Request) {
|
|
|
190
104
|
}
|
|
191
105
|
```
|
|
192
106
|
|
|
193
|
-
|
|
107
|
+
Or use the `withMonitor` HOC from `@djangocfg/nextjs/monitor`:
|
|
194
108
|
|
|
195
109
|
```typescript
|
|
196
|
-
|
|
197
|
-
if (!response.ok) {
|
|
198
|
-
await serverMonitor.captureNetworkError(
|
|
199
|
-
response.status,
|
|
200
|
-
'GET',
|
|
201
|
-
upstreamUrl,
|
|
202
|
-
{ pageUrl: req.url },
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Arbitrary Event
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
|
-
import { EventType, EventLevel } from '@djangocfg/monitor/server'
|
|
211
|
-
|
|
212
|
-
await serverMonitor.capture({
|
|
213
|
-
event_type: EventType.WARNING,
|
|
214
|
-
level: EventLevel.WARN,
|
|
215
|
-
message: 'Rate limit approaching',
|
|
216
|
-
url: req.url,
|
|
217
|
-
extra: { remaining: 5 },
|
|
218
|
-
})
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## Types
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
import type { MonitorConfig, MonitorEvent } from '@djangocfg/monitor'
|
|
225
|
-
import { EventType, EventLevel } from '@djangocfg/monitor'
|
|
226
|
-
|
|
227
|
-
EventType.JS_ERROR // 'js_error'
|
|
228
|
-
EventType.NETWORK_ERROR // 'network_error'
|
|
229
|
-
EventType.WARNING // 'warning'
|
|
230
|
-
EventType.ERROR // 'error'
|
|
231
|
-
EventType.INFO // 'info'
|
|
232
|
-
|
|
233
|
-
EventLevel.ERROR // 'error'
|
|
234
|
-
EventLevel.WARN // 'warn'
|
|
235
|
-
EventLevel.INFO // 'info'
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### MonitorConfig
|
|
239
|
-
|
|
240
|
-
| Option | Type | Default | Description |
|
|
241
|
-
|--------|------|---------|-------------|
|
|
242
|
-
| `project` | `string` | `''` | Project name sent with every event |
|
|
243
|
-
| `environment` | `string` | `''` | `production` / `staging` / `development` |
|
|
244
|
-
| `baseUrl` | `string` | `''` | Base URL of the django-cfg backend (same origin by default) |
|
|
245
|
-
| `flushInterval` | `number` | `5000` | Buffer flush interval (ms) |
|
|
246
|
-
| `maxBufferSize` | `number` | `20` | Max events before immediate flush |
|
|
247
|
-
| `captureJsErrors` | `boolean` | `true` | Capture `window.onerror` + unhandled rejections |
|
|
248
|
-
| `captureConsole` | `boolean` | `true` | Intercept `console.warn` / `console.error` |
|
|
249
|
-
| `debug` | `boolean` | `false` | Log init info to console |
|
|
110
|
+
import { withMonitor } from '@djangocfg/nextjs/monitor'
|
|
250
111
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
| Option | Type | Default | Description |
|
|
254
|
-
|--------|------|---------|-------------|
|
|
255
|
-
| `project` | `string` | `''` | Project name |
|
|
256
|
-
| `environment` | `string` | `''` | Environment name |
|
|
257
|
-
| `baseUrl` | `string` | `''` | Base URL of the django-cfg backend (absolute required on server) |
|
|
258
|
-
|
|
259
|
-
## Zod Validation Integration
|
|
260
|
-
|
|
261
|
-
`@djangocfg/api` automatically dispatches a `zod-validation-error` CustomEvent whenever a response fails schema validation. `@djangocfg/monitor/client` listens and forwards it as a `WARNING` — zero setup needed.
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
@djangocfg/api fetcher validates response with Zod
|
|
265
|
-
→ dispatches window CustomEvent('zod-validation-error', { detail })
|
|
266
|
-
→ @djangocfg/monitor/client capture/validation.ts catches it
|
|
267
|
-
→ pushed to store → flushed to backend
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## Custom HTTP Adapter
|
|
271
|
-
|
|
272
|
-
The generated `KeepAliveFetchAdapter` is also available if you need it elsewhere:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import { KeepAliveFetchAdapter } from '@djangocfg/monitor/client'
|
|
276
|
-
// or directly from generated:
|
|
277
|
-
import { API, KeepAliveFetchAdapter } from '@djangocfg/monitor/client'
|
|
278
|
-
|
|
279
|
-
const api = new API('https://api.myapp.com', {
|
|
280
|
-
httpClient: new KeepAliveFetchAdapter(),
|
|
112
|
+
export const POST = withMonitor(async (req) => {
|
|
113
|
+
// errors are captured automatically
|
|
281
114
|
})
|
|
282
115
|
```
|
|
283
116
|
|
|
284
117
|
## Django Backend
|
|
285
118
|
|
|
286
|
-
Enable the `cfg_monitor` extension:
|
|
287
|
-
|
|
288
119
|
```python
|
|
289
120
|
# cfg.py
|
|
290
|
-
from django_cfg import DjangoConfig
|
|
291
|
-
|
|
292
121
|
class Config(DjangoConfig):
|
|
293
|
-
installed_apps = [
|
|
294
|
-
...
|
|
295
|
-
'django_cfg.apps.system.monitor',
|
|
296
|
-
]
|
|
122
|
+
installed_apps = [..., 'django_cfg.apps.system.monitor']
|
|
297
123
|
```
|
|
298
124
|
|
|
299
|
-
|
|
300
|
-
|---|---|
|
|
301
|
-
| Endpoint | `POST /cfg/monitor/ingest/` |
|
|
302
|
-
| Auth | not required |
|
|
303
|
-
| Rate limit | 100 req/min per IP |
|
|
304
|
-
| Batch size | up to 50 events |
|
|
125
|
+
Ingest endpoint: `POST /cfg/monitor/ingest/` — no auth required, 100 req/min per IP.
|
|
305
126
|
|
|
306
|
-
##
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
# Regenerate API client from Django OpenAPI schema
|
|
310
|
-
make generate
|
|
127
|
+
## Safety
|
|
311
128
|
|
|
312
|
-
|
|
313
|
-
make build
|
|
314
|
-
|
|
315
|
-
# Watch mode
|
|
316
|
-
pnpm dev
|
|
317
|
-
|
|
318
|
-
# Type check
|
|
319
|
-
pnpm check
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
## Peer Dependencies
|
|
323
|
-
|
|
324
|
-
| Package | Required | Description |
|
|
325
|
-
|---------|----------|-------------|
|
|
326
|
-
| `consola` | optional | Uses consola reporter instead of patching `console.*` |
|
|
327
|
-
| `zustand` | optional | Required for the client entry point event buffer |
|
|
328
|
-
|
|
329
|
-
## Requirements
|
|
330
|
-
|
|
331
|
-
- Next.js >= 15 (or any React >= 19 app)
|
|
332
|
-
- Django with `django_cfg >= 1.7` and `monitor` extension
|
|
129
|
+
Transport errors are **always swallowed** — monitor never crashes your app and never sends its own errors to itself. If the backend is unreachable, events are silently dropped.
|
|
333
130
|
|
|
334
131
|
## License
|
|
335
132
|
|
|
336
133
|
MIT
|
|
337
|
-
|
|
338
|
-
## Links
|
|
339
|
-
|
|
340
|
-
- [DjangoCFG Documentation](https://djangocfg.com)
|
|
341
|
-
- [GitHub](https://github.com/markolofsen/django-cfg)
|
package/dist/client.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var client_exports = {};
|
|
33
33
|
__export(client_exports, {
|
|
34
34
|
FrontendMonitor: () => FrontendMonitor,
|
|
35
|
+
MonitorProvider: () => MonitorProvider,
|
|
35
36
|
getSessionId: () => getSessionId,
|
|
36
37
|
monitoredFetch: () => monitoredFetch
|
|
37
38
|
});
|
|
@@ -1234,6 +1235,17 @@ async function monitoredFetch(input, init) {
|
|
|
1234
1235
|
}
|
|
1235
1236
|
__name(monitoredFetch, "monitoredFetch");
|
|
1236
1237
|
|
|
1238
|
+
// src/client/MonitorProvider.tsx
|
|
1239
|
+
var import_react = require("react");
|
|
1240
|
+
function MonitorProvider({ children, ...config }) {
|
|
1241
|
+
(0, import_react.useEffect)(() => {
|
|
1242
|
+
FrontendMonitor.init(config);
|
|
1243
|
+
return () => FrontendMonitor.destroy();
|
|
1244
|
+
}, []);
|
|
1245
|
+
return children ?? null;
|
|
1246
|
+
}
|
|
1247
|
+
__name(MonitorProvider, "MonitorProvider");
|
|
1248
|
+
|
|
1237
1249
|
// src/client/index.ts
|
|
1238
1250
|
var flushInterval = null;
|
|
1239
1251
|
var cleanupFns = [];
|