@neowhale/telemetry 1.1.0 → 1.1.2
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 +334 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# @neowhale/telemetry
|
|
2
|
+
|
|
3
|
+
Error tracking, analytics, Web Vitals, and AI/LLM call monitoring for WhaleTools stores. Zero dependencies. Works in the browser and on the server.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @neowhale/telemetry
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `react >=18` (optional -- only needed for `@neowhale/telemetry/react`).
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### Browser (React)
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { WhaleTelemetry, WhaleErrorBoundary } from '@neowhale/telemetry/react'
|
|
19
|
+
|
|
20
|
+
export default function App() {
|
|
21
|
+
return (
|
|
22
|
+
<WhaleTelemetry apiKey="wk_live_..." storeId="your-store-uuid">
|
|
23
|
+
<WhaleErrorBoundary fallback={<p>Something went wrong.</p>}>
|
|
24
|
+
<YourApp />
|
|
25
|
+
</WhaleErrorBoundary>
|
|
26
|
+
</WhaleTelemetry>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`WhaleTelemetry` calls `init()` on mount and `destroy()` on unmount. In the browser it automatically captures:
|
|
32
|
+
|
|
33
|
+
- Unhandled errors and promise rejections
|
|
34
|
+
- Page views (initial load and SPA navigation)
|
|
35
|
+
- Web Vitals (LCP, FID, CLS, FCP)
|
|
36
|
+
- Breadcrumbs (clicks, navigation, console warnings/errors, fetch)
|
|
37
|
+
|
|
38
|
+
### Browser (Vanilla)
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { whaletools } from '@neowhale/telemetry'
|
|
42
|
+
|
|
43
|
+
whaletools.init({ apiKey: 'wk_live_...', storeId: 'your-store-uuid' })
|
|
44
|
+
|
|
45
|
+
whaletools.track('add_to_cart', { product_id: 'abc', price: 29.99 })
|
|
46
|
+
whaletools.captureError(new Error('Payment failed'))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Server (Next.js App Router)
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { withTelemetry } from '@neowhale/telemetry/server'
|
|
53
|
+
|
|
54
|
+
const config = { apiKey: 'wk_live_...', storeId: 'your-store-uuid' }
|
|
55
|
+
|
|
56
|
+
export const GET = withTelemetry(async (req) => {
|
|
57
|
+
return Response.json({ ok: true })
|
|
58
|
+
}, config)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Server (Manual Error Reporting)
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { reportServerError } from '@neowhale/telemetry/server'
|
|
65
|
+
|
|
66
|
+
const config = { apiKey: 'wk_live_...', storeId: 'your-store-uuid' }
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await riskyOperation()
|
|
70
|
+
} catch (err) {
|
|
71
|
+
await reportServerError(err as Error, config, { context: 'cron_job' })
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Server (AI/LLM Call Tracking)
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { reportAICall } from '@neowhale/telemetry/server'
|
|
79
|
+
|
|
80
|
+
const config = { apiKey: 'wk_live_...', storeId: 'your-store-uuid' }
|
|
81
|
+
|
|
82
|
+
const start = Date.now()
|
|
83
|
+
const response = await openai.chat.completions.create({ model: 'gpt-4o', messages })
|
|
84
|
+
|
|
85
|
+
await reportAICall({
|
|
86
|
+
model: 'gpt-4o',
|
|
87
|
+
provider: 'openai',
|
|
88
|
+
prompt_tokens: response.usage?.prompt_tokens,
|
|
89
|
+
completion_tokens: response.usage?.completion_tokens,
|
|
90
|
+
duration_ms: Date.now() - start,
|
|
91
|
+
status: 'ok',
|
|
92
|
+
}, config)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
### `WhaleToolsConfig` (browser)
|
|
100
|
+
|
|
101
|
+
| Option | Type | Default | Description |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| `apiKey` | `string` | **required** | API key (`wk_live_...` or `wk_test_...`) |
|
|
104
|
+
| `storeId` | `string` | **required** | Store UUID |
|
|
105
|
+
| `endpoint` | `string` | `"https://whale-gateway.fly.dev"` | Gateway URL |
|
|
106
|
+
| `errors` | `boolean` | `true` | Auto-capture unhandled errors |
|
|
107
|
+
| `analytics` | `boolean` | `true` | Auto-capture page views |
|
|
108
|
+
| `vitals` | `boolean` | `true` | Collect Web Vitals |
|
|
109
|
+
| `breadcrumbs` | `boolean` | `true` | Auto-collect breadcrumbs |
|
|
110
|
+
| `environment` | `string` | `"production"` | Environment tag |
|
|
111
|
+
| `serviceName` | `string` | `"store_client"` | Service name tag |
|
|
112
|
+
| `serviceVersion` | `string` | `""` | Service version tag |
|
|
113
|
+
| `flushInterval` | `number` | `5000` | Flush interval in ms |
|
|
114
|
+
| `flushThreshold` | `number` | `10` | Max queued items before auto-flush |
|
|
115
|
+
| `debug` | `boolean` | `false` | Log telemetry to console |
|
|
116
|
+
| `sampleRate` | `number` | `1` | Analytics sample rate (0--1) |
|
|
117
|
+
| `beforeSend` | `(error: ErrorPayload) => ErrorPayload \| false` | -- | Mutate or drop errors before send |
|
|
118
|
+
|
|
119
|
+
### `ServerConfig` (server)
|
|
120
|
+
|
|
121
|
+
| Option | Type | Default | Description |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| `apiKey` | `string` | **required** | API key |
|
|
124
|
+
| `storeId` | `string` | **required** | Store UUID |
|
|
125
|
+
| `endpoint` | `string` | `"https://whale-gateway.fly.dev"` | Gateway URL |
|
|
126
|
+
| `serviceName` | `string` | `"store_server"` | Service name tag |
|
|
127
|
+
| `environment` | `string` | `"production"` | Environment tag |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## API Reference
|
|
132
|
+
|
|
133
|
+
### Core -- `@neowhale/telemetry`
|
|
134
|
+
|
|
135
|
+
The default export provides a pre-initialized `whaletools` singleton of type `WhaleToolsClient`.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { whaletools } from '@neowhale/telemetry'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `whaletools.init(config: WhaleToolsConfig): void`
|
|
142
|
+
|
|
143
|
+
Initialize the client. Must be called once before other methods. In the browser, automatically installs error handlers, page view tracking, Web Vitals collection, and breadcrumb capture based on config flags.
|
|
144
|
+
|
|
145
|
+
#### `whaletools.captureError(error: Error | string, extra?: Record<string, unknown>): void`
|
|
146
|
+
|
|
147
|
+
Manually capture an error. Generates a SHA-256 fingerprint, attaches breadcrumbs, and queues for delivery.
|
|
148
|
+
|
|
149
|
+
#### `whaletools.captureMessage(message: string, severity?: Severity, extra?: Record<string, unknown>): void`
|
|
150
|
+
|
|
151
|
+
Capture a message as an error event. Severity is one of `"debug" | "info" | "warning" | "error" | "fatal"` (default `"info"`).
|
|
152
|
+
|
|
153
|
+
#### `whaletools.track(eventName: string, properties?: Record<string, unknown>): void`
|
|
154
|
+
|
|
155
|
+
Track a custom analytics event. Automatically enriched with `page_url` and `page_title`. Subject to `sampleRate`.
|
|
156
|
+
|
|
157
|
+
#### `whaletools.page(properties?: Record<string, unknown>): void`
|
|
158
|
+
|
|
159
|
+
Track a page view. Enriched with `page_url`, `page_path`, and `page_title`. Called automatically on init and SPA navigation when `analytics` is enabled.
|
|
160
|
+
|
|
161
|
+
#### `whaletools.identify(userId: string, traits?: Record<string, unknown>): void`
|
|
162
|
+
|
|
163
|
+
Set user context. Traits such as `email` and `name` are attached to subsequent telemetry payloads.
|
|
164
|
+
|
|
165
|
+
#### `whaletools.trackAICall(call: AICallPayload): void`
|
|
166
|
+
|
|
167
|
+
Track an AI/LLM API call. Automatically calculates `total_tokens` from `prompt_tokens + completion_tokens` if omitted.
|
|
168
|
+
|
|
169
|
+
#### `whaletools.addBreadcrumb(category: string, message: string, data?: Record<string, unknown>): void`
|
|
170
|
+
|
|
171
|
+
Add a manual breadcrumb to the ring buffer (max 25).
|
|
172
|
+
|
|
173
|
+
#### `whaletools.flush(): void`
|
|
174
|
+
|
|
175
|
+
Force-flush all queued errors, events, vitals, and AI calls to the gateway.
|
|
176
|
+
|
|
177
|
+
#### `whaletools.destroy(): void`
|
|
178
|
+
|
|
179
|
+
Stop the flush timer, perform a final flush, and reset the initialized state.
|
|
180
|
+
|
|
181
|
+
#### `addBreadcrumb(category, message, level?, data?)` (standalone)
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { addBreadcrumb } from '@neowhale/telemetry'
|
|
185
|
+
|
|
186
|
+
addBreadcrumb('checkout', 'User entered payment info', 'info', { step: 3 })
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Add a breadcrumb directly to the global ring buffer without going through the client instance.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### React -- `@neowhale/telemetry/react`
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { WhaleTelemetry, WhaleErrorBoundary } from '@neowhale/telemetry/react'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### `<WhaleTelemetry {...config} />`
|
|
200
|
+
|
|
201
|
+
Provider component. Accepts all `WhaleToolsConfig` props plus `children`. Calls `whaletools.init()` on mount and `whaletools.destroy()` on unmount.
|
|
202
|
+
|
|
203
|
+
#### `<WhaleErrorBoundary />`
|
|
204
|
+
|
|
205
|
+
React error boundary that reports caught render errors to WhaleTools.
|
|
206
|
+
|
|
207
|
+
| Prop | Type | Description |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `children` | `ReactNode` | Child components to wrap |
|
|
210
|
+
| `fallback` | `ReactNode \| (error: Error) => ReactNode` | Fallback UI on error |
|
|
211
|
+
| `onError` | `(error: Error, errorInfo: { componentStack: string }) => void` | Callback on error |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
### Server -- `@neowhale/telemetry/server`
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { withTelemetry, reportServerError, reportAICall, reportAICallBatch } from '@neowhale/telemetry/server'
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### `withTelemetry(handler, config): handler`
|
|
222
|
+
|
|
223
|
+
Wraps a route handler (Next.js App Router, etc.) with automatic error capture. Catches unhandled errors, reports them to the gateway, and re-throws so the framework can handle the response.
|
|
224
|
+
|
|
225
|
+
#### `reportServerError(error: Error, config: ServerConfig, extra?: Record<string, unknown>): Promise<void>`
|
|
226
|
+
|
|
227
|
+
Manually report a server-side error. Use in `catch` blocks where `withTelemetry` is not applicable.
|
|
228
|
+
|
|
229
|
+
#### `reportAICall(call: AICallReport, config: ServerConfig): Promise<void>`
|
|
230
|
+
|
|
231
|
+
Report a single AI/LLM API call. `total_tokens` is auto-calculated from `prompt_tokens + completion_tokens` if omitted.
|
|
232
|
+
|
|
233
|
+
#### `reportAICallBatch(calls: AICallReport[], config: ServerConfig): Promise<void>`
|
|
234
|
+
|
|
235
|
+
Report multiple AI/LLM calls in a single request.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Types
|
|
240
|
+
|
|
241
|
+
### `ErrorPayload`
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
interface ErrorPayload {
|
|
245
|
+
error_type: string
|
|
246
|
+
error_message: string
|
|
247
|
+
stack_trace: string
|
|
248
|
+
fingerprint: string
|
|
249
|
+
severity: 'debug' | 'info' | 'warning' | 'error' | 'fatal'
|
|
250
|
+
source_file: string
|
|
251
|
+
source_line: number
|
|
252
|
+
source_function: string
|
|
253
|
+
tags: Record<string, string>
|
|
254
|
+
extra: Record<string, unknown>
|
|
255
|
+
breadcrumbs: Breadcrumb[]
|
|
256
|
+
occurred_at: string
|
|
257
|
+
platform: string
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `Breadcrumb`
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
interface Breadcrumb {
|
|
265
|
+
timestamp: string
|
|
266
|
+
category: string
|
|
267
|
+
message: string
|
|
268
|
+
level: 'debug' | 'info' | 'warning' | 'error'
|
|
269
|
+
data?: Record<string, unknown>
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### `AnalyticsEvent`
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
interface AnalyticsEvent {
|
|
277
|
+
event_name: string
|
|
278
|
+
properties: Record<string, unknown>
|
|
279
|
+
timestamp: string
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### `WebVital`
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
interface WebVital {
|
|
287
|
+
name: 'CLS' | 'FID' | 'LCP' | 'INP' | 'TTFB' | 'FCP'
|
|
288
|
+
value: number
|
|
289
|
+
rating: 'good' | 'needs-improvement' | 'poor'
|
|
290
|
+
timestamp: string
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### `AICallPayload`
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
interface AICallPayload {
|
|
298
|
+
model: string // required
|
|
299
|
+
provider?: string
|
|
300
|
+
operation?: string
|
|
301
|
+
prompt_tokens?: number
|
|
302
|
+
completion_tokens?: number
|
|
303
|
+
total_tokens?: number
|
|
304
|
+
cost?: number
|
|
305
|
+
duration_ms?: number
|
|
306
|
+
status?: 'ok' | 'error'
|
|
307
|
+
error_message?: string
|
|
308
|
+
http_status?: number
|
|
309
|
+
agent_id?: string
|
|
310
|
+
conversation_id?: string
|
|
311
|
+
tool_name?: string
|
|
312
|
+
parent_span_id?: string
|
|
313
|
+
attributes?: Record<string, unknown>
|
|
314
|
+
timestamp?: string
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Internals
|
|
321
|
+
|
|
322
|
+
**Transport** -- Telemetry is batched and sent via HTTP POST to `/v1/stores/{storeId}/telemetry/ingest`. On page unload, the client falls back to `navigator.sendBeacon` to avoid data loss.
|
|
323
|
+
|
|
324
|
+
**Sessions** -- `visitor_id` is persisted in `localStorage` (`wt_vid`). `session_id` (`wt_sid`) resets after 30 minutes of inactivity.
|
|
325
|
+
|
|
326
|
+
**Fingerprinting** -- Errors are deduplicated using a SHA-256 hash of `type|message|source`. Falls back to djb2 when SubtleCrypto is unavailable.
|
|
327
|
+
|
|
328
|
+
**Breadcrumbs** -- Automatically collected categories: `ui.click`, `navigation`, `console.warn`, `console.error`, `fetch`. Stored in a ring buffer (max 25 entries) and attached to error payloads.
|
|
329
|
+
|
|
330
|
+
**Web Vitals** -- LCP, FID, CLS, and FCP are measured via `PerformanceObserver`. Ratings follow web.dev thresholds.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT
|