@doow/track 0.1.0
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/CHANGELOG.md +33 -0
- package/README.md +222 -0
- package/dist/cjs/debug.js +19 -0
- package/dist/cjs/debug.js.map +1 -0
- package/dist/cjs/event-processor.js +147 -0
- package/dist/cjs/event-processor.js.map +1 -0
- package/dist/cjs/exporter.js +477 -0
- package/dist/cjs/exporter.js.map +1 -0
- package/dist/cjs/file-offline-store.js +101 -0
- package/dist/cjs/file-offline-store.js.map +1 -0
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/tracker.js +292 -0
- package/dist/cjs/tracker.js.map +1 -0
- package/dist/cjs/uuid.js +13 -0
- package/dist/cjs/uuid.js.map +1 -0
- package/dist/cli.cjs +1491 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/esm/debug.js +17 -0
- package/dist/esm/debug.js.map +1 -0
- package/dist/esm/event-processor.js +145 -0
- package/dist/esm/event-processor.js.map +1 -0
- package/dist/esm/exporter.js +475 -0
- package/dist/esm/exporter.js.map +1 -0
- package/dist/esm/file-offline-store.js +99 -0
- package/dist/esm/file-offline-store.js.map +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tracker.js +290 -0
- package/dist/esm/tracker.js.map +1 -0
- package/dist/esm/uuid.js +11 -0
- package/dist/esm/uuid.js.map +1 -0
- package/dist/sidecar.cjs +1285 -0
- package/dist/sidecar.cjs.map +1 -0
- package/dist/types/index.d.ts +224 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@doow/track` will be documented here.
|
|
4
|
+
|
|
5
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
+
Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [0.1.0] — 2026-04-20
|
|
11
|
+
|
|
12
|
+
Initial release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `DoowTracker` — core tracking class with full init options surface (18 options)
|
|
17
|
+
- Automatic flush on `flushAt` (event count), `flushInterval` (timer), and `maxPayloadBytes` (size)
|
|
18
|
+
- Ring buffer with configurable `maxQueueSize` (drop oldest when full)
|
|
19
|
+
- Gzip compression with `Content-Encoding: gzip` (disable via `disableCompression`)
|
|
20
|
+
- Exponential backoff with ±20% jitter, configurable `retryCount`
|
|
21
|
+
- 429 rate limiting: respects `Retry-After` header + `X-Doow-Rate-Limits` per-category header
|
|
22
|
+
- 413 adaptive batch halving on payload-too-large
|
|
23
|
+
- 207 partial accept: surfaces rejected event IDs via `onError`
|
|
24
|
+
- 401 auth failure: stops SDK permanently, surfaces `AUTH_FAILURE` error
|
|
25
|
+
- `beforeSend` / `beforeFlush` hooks for per-event and per-batch filtering
|
|
26
|
+
- `FileOfflineStore` — atomic FIFO persistent store for failed batches; drains on reconnect
|
|
27
|
+
- Serverless wrappers: `withLambda`, `withVercel`, `withAzureFunction` — guaranteed flush before return
|
|
28
|
+
- Sidecar process: stdin / file-tail / TCP input modes, HTTP `/healthz` endpoint
|
|
29
|
+
- CLI daemon: `--config`, `--api-key`, `--pidfile`, `--version` flags, `SIGHUP` config reload
|
|
30
|
+
- Environment variable overrides for all major options (`DOOW_TRACK_*`)
|
|
31
|
+
- `__SDK_VERSION__` compile-time constant — wire protocol `sdk_version` and `X-Doow-SDK-Version` header track package.json version
|
|
32
|
+
- Full TypeScript types exported (`DoowTrackerOptions`, `TrackEvent`, `SdkError`, etc.)
|
|
33
|
+
- 137 unit tests across all stories
|
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# @doow/track
|
|
2
|
+
|
|
3
|
+
Customer-facing SDK for emitting usage telemetry to Doow. Tracks metered usage events (API calls, tokens, storage, requests, etc.) from any Node.js application.
|
|
4
|
+
|
|
5
|
+
Source repository: https://github.com/Doow-Dev/doow-track-sdk
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { DoowTracker } from '@doow/track';
|
|
11
|
+
|
|
12
|
+
const meter = new DoowTracker('dk_your_api_key');
|
|
13
|
+
meter.track({ metric: 'api_calls', quantity: 1, license_id: 'lic_...' });
|
|
14
|
+
await meter.shutdown();
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @doow/track
|
|
21
|
+
# or
|
|
22
|
+
yarn add @doow/track
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## All init options
|
|
26
|
+
|
|
27
|
+
| Option | Type | Default | Description |
|
|
28
|
+
|--------|------|---------|-------------|
|
|
29
|
+
| `endpoint` | `string` | `https://api.doow.co` | Telemetry server endpoint |
|
|
30
|
+
| `enabled` | `boolean` | `true` | Enable/disable the SDK. When `false`, all operations are no-ops |
|
|
31
|
+
| `attribution` | `Record<string, string \| number \| boolean>` | `undefined` | SDK-level attribution bag merged into every event |
|
|
32
|
+
| `debug` | `boolean` | `false` | Enable debug logging (only works in non-production builds) |
|
|
33
|
+
| `flushAt` | `number` | `20` | Flush after N events queued |
|
|
34
|
+
| `flushInterval` | `number` | `10000` | Flush every N milliseconds |
|
|
35
|
+
| `maxPayloadBytes` | `number` | `460800` | Max payload bytes (450 KB) before triggering a flush |
|
|
36
|
+
| `maxQueueSize` | `number` | `10000` | Max events in ring buffer before dropping oldest |
|
|
37
|
+
| `timeout` | `number` | `10000` | Per-request timeout in milliseconds |
|
|
38
|
+
| `retryCount` | `number` | `3` | Max retries on transient failures |
|
|
39
|
+
| `disableCompression` | `boolean` | `false` | Disable gzip compression |
|
|
40
|
+
| `onError` | `(error: SdkError) => void` | `console.warn` | Called on errors — SDK never throws |
|
|
41
|
+
| `beforeSend` | `(event: SerializedEvent) => SerializedEvent \| null` | `undefined` | Per-event hook. Return `null` to drop the event |
|
|
42
|
+
| `beforeFlush` | `(batch: SerializedEvent[]) => SerializedEvent[] \| null` | `undefined` | Per-batch hook. Return `null` to drop the entire batch |
|
|
43
|
+
| `transport` | `CustomTransport` | `undefined` | Custom HTTP transport (for testing, mTLS, HTTP/2) |
|
|
44
|
+
| `offlineStore` | `OfflineStore` | `undefined` | Offline persistent store for failed batches |
|
|
45
|
+
| `maxConcurrentFlushes` | `number` | `30` | Max concurrent network promises |
|
|
46
|
+
| `shutdownTimeout` | `number` | `5000` | Shutdown timeout in milliseconds |
|
|
47
|
+
|
|
48
|
+
## Environment variable overrides
|
|
49
|
+
|
|
50
|
+
All options can be overridden via environment variables. Env vars take precedence over code-level options.
|
|
51
|
+
|
|
52
|
+
| Variable | Overrides | Notes |
|
|
53
|
+
|----------|-----------|-------|
|
|
54
|
+
| `DOOW_TRACK_API_KEY` | first positional argument | Preferred over hard-coded key |
|
|
55
|
+
| `DOOW_TRACK_ENDPOINT` | `endpoint` | Override server URL |
|
|
56
|
+
| `DOOW_TRACK_DISABLED=true` | `enabled` | Disable the SDK entirely |
|
|
57
|
+
| `DOOW_TRACK_DEBUG=true` | `debug` | Enable debug output |
|
|
58
|
+
| `DOOW_TRACK_FLUSH_AT` | `flushAt` | Integer, e.g. `5` |
|
|
59
|
+
| `DOOW_TRACK_FLUSH_INTERVAL` | `flushInterval` | Milliseconds, e.g. `5000` |
|
|
60
|
+
| `DOOW_TRACK_ATTRIBUTION` | `attribution` | JSON string, e.g. `{"env":"prod"}` |
|
|
61
|
+
|
|
62
|
+
## Serverless guide
|
|
63
|
+
|
|
64
|
+
Long-lived Node.js processes use the timer-based auto-flush. In serverless environments (Lambda, Vercel, Azure Functions) the process exits after each invocation, so you need guaranteed flush before return.
|
|
65
|
+
|
|
66
|
+
### AWS Lambda
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { DoowTracker } from '@doow/track';
|
|
70
|
+
|
|
71
|
+
const meter = new DoowTracker(process.env.DOOW_API_KEY!);
|
|
72
|
+
|
|
73
|
+
export const handler = meter.withLambda(async (event, context) => {
|
|
74
|
+
meter.track({ metric: 'api_calls', quantity: 1, license_id: 'lic_...' });
|
|
75
|
+
return { statusCode: 200, body: 'ok' };
|
|
76
|
+
// shutdown() is called automatically in a finally block
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Vercel
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { DoowTracker } from '@doow/track';
|
|
84
|
+
import type { VercelRequest, VercelResponse } from '@vercel/node';
|
|
85
|
+
|
|
86
|
+
const meter = new DoowTracker(process.env.DOOW_API_KEY!);
|
|
87
|
+
|
|
88
|
+
export default meter.withVercel(async (req: VercelRequest, res: VercelResponse) => {
|
|
89
|
+
meter.track({ metric: 'requests', quantity: 1, license_id: 'lic_...' });
|
|
90
|
+
res.status(200).json({ ok: true });
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Azure Functions
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { DoowTracker } from '@doow/track';
|
|
98
|
+
import type { Context } from '@azure/functions';
|
|
99
|
+
|
|
100
|
+
const meter = new DoowTracker(process.env.DOOW_API_KEY!);
|
|
101
|
+
|
|
102
|
+
export default meter.withAzureFunction(async (context: Context, req: unknown) => {
|
|
103
|
+
meter.track({ metric: 'executions', quantity: 1, license_id: 'lic_...' });
|
|
104
|
+
return { status: 200, body: 'ok' };
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Sidecar Docker Compose example
|
|
109
|
+
|
|
110
|
+
For use cases where you emit telemetry from non-Node.js services (Python, Go, Rust, etc.), run the sidecar container and pipe JSON events to it over stdin or TCP.
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
# docker-compose.yml
|
|
114
|
+
version: '3.9'
|
|
115
|
+
|
|
116
|
+
services:
|
|
117
|
+
app:
|
|
118
|
+
image: your-app
|
|
119
|
+
depends_on:
|
|
120
|
+
- doow-sidecar
|
|
121
|
+
environment:
|
|
122
|
+
- DOOW_SIDECAR_HOST=doow-sidecar
|
|
123
|
+
- DOOW_SIDECAR_PORT=9091
|
|
124
|
+
|
|
125
|
+
doow-sidecar:
|
|
126
|
+
image: doow/track-sidecar:latest
|
|
127
|
+
environment:
|
|
128
|
+
- DOOW_TRACK_API_KEY=dk_your_api_key
|
|
129
|
+
- DOOW_TRACK_ENDPOINT=https://api.doow.co
|
|
130
|
+
- DOOW_TRACK_INPUT=tcp # stdin | file-tail | tcp
|
|
131
|
+
- DOOW_TRACK_TCP_PORT=9091
|
|
132
|
+
- DOOW_TRACK_HEALTH_PORT=9090
|
|
133
|
+
ports:
|
|
134
|
+
- '9090:9090' # health check
|
|
135
|
+
- '9091:9091' # TCP event ingestion
|
|
136
|
+
healthcheck:
|
|
137
|
+
test: ['CMD', 'wget', '-qO-', 'http://localhost:9090/healthz']
|
|
138
|
+
interval: 10s
|
|
139
|
+
timeout: 5s
|
|
140
|
+
retries: 3
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Send events from your app as newline-delimited JSON:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{"metric":"api_calls","quantity":1,"license_id":"lic_..."}
|
|
147
|
+
{"metric":"tokens","quantity":512,"license_id":"lic_...","unit":"tokens"}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## CLI usage
|
|
151
|
+
|
|
152
|
+
Run the sidecar as a standalone daemon process:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Start as daemon with config file
|
|
156
|
+
npx @doow/track --config ./doow-track.json --pidfile /var/run/doow-track.pid
|
|
157
|
+
|
|
158
|
+
# Pipe mode: pipe newline-delimited JSON from stdin
|
|
159
|
+
echo '{"metric":"api_calls","quantity":1,"license_id":"lic_..."}' | npx @doow/track
|
|
160
|
+
|
|
161
|
+
# Reload config without restart (daemon mode)
|
|
162
|
+
kill -HUP $(cat /var/run/doow-track.pid)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Config file (`doow-track.json`):
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"api_key": "dk_your_api_key",
|
|
170
|
+
"endpoint": "https://api.doow.co",
|
|
171
|
+
"input": { "mode": "stdin" },
|
|
172
|
+
"flush_at": 20,
|
|
173
|
+
"flush_interval": 10000
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### CLI flags
|
|
178
|
+
|
|
179
|
+
| Flag | Description |
|
|
180
|
+
|------|-------------|
|
|
181
|
+
| `--config <path>` | Path to JSON config file |
|
|
182
|
+
| `--api-key <key>` | API key (overrides config and env) |
|
|
183
|
+
| `--pidfile <path>` | Write PID to file (daemon mode) |
|
|
184
|
+
| `--version` | Print SDK version and exit |
|
|
185
|
+
| `--help` | Print usage and exit |
|
|
186
|
+
|
|
187
|
+
## Offline store
|
|
188
|
+
|
|
189
|
+
When network delivery fails, events can be persisted locally and replayed on reconnect:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { DoowTracker, FileOfflineStore } from '@doow/track';
|
|
193
|
+
|
|
194
|
+
const meter = new DoowTracker('dk_your_api_key', {
|
|
195
|
+
offlineStore: new FileOfflineStore('./doow-track-offline'),
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Failed batches are written as atomic JSON files (write-then-rename) and replayed FIFO on the next successful flush.
|
|
200
|
+
|
|
201
|
+
## Error handling
|
|
202
|
+
|
|
203
|
+
The SDK never throws. All errors are surfaced via the `onError` callback:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
const meter = new DoowTracker('dk_your_api_key', {
|
|
207
|
+
onError: (error) => {
|
|
208
|
+
console.error(`[doow/track] ${error.kind}: ${error.message}`);
|
|
209
|
+
// error.kind: 'AUTH_FAILURE' | 'RATE_LIMITED' | 'PARTIAL_ACCEPT' | 'NETWORK_ERROR' | 'TIMEOUT' | 'DROPPED_EVENTS' | 'TRANSPORT_ERROR'
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
After `AUTH_FAILURE`, the SDK stops emitting permanently (check `meter.stopped`).
|
|
215
|
+
|
|
216
|
+
## Further reading
|
|
217
|
+
|
|
218
|
+
- [Serverless guide](docs/serverless.md) — Lambda, Vercel, Azure Functions
|
|
219
|
+
- [Sidecar guide](docs/sidecar.md) — Docker Compose, Kubernetes sidecar pattern
|
|
220
|
+
- [Daemon / CLI guide](docs/daemon.md) — systemd unit file, config file reference
|
|
221
|
+
- [Migration guide](docs/migration.md) — Migrating from manual CSV upload to SDK
|
|
222
|
+
- [OTLP push guide](docs/otlp.md) — OpenTelemetry Collector config, GenAI semconv mapping
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function createDebugLogger(enabled) {
|
|
4
|
+
if (!enabled) {
|
|
5
|
+
return { log: () => undefined, warn: () => undefined };
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
log: (msg, ...args) => {
|
|
9
|
+
// eslint-disable-next-line no-console
|
|
10
|
+
console.warn(`[doow/track] ${msg}`, ...args);
|
|
11
|
+
},
|
|
12
|
+
warn: (msg, ...args) => {
|
|
13
|
+
console.warn(`[doow/track:warn] ${msg}`, ...args);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.createDebugLogger = createDebugLogger;
|
|
19
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.js","sources":["../../../src/debug.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAgBM,SAAU,iBAAiB,CAAC,OAAgB,EAAA;IAMhD,IAAI,CAAC,OAAO,EAAE;AACZ,QAAA,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,EAAE,IAAI,EAAE,MAAM,SAAS,EAAE;IACxD;IAEA,OAAO;AACL,QAAA,GAAG,EAAE,CAAC,GAAW,EAAE,GAAG,IAAe,KAAU;;YAE7C,OAAO,CAAC,IAAI,CAAC,CAAA,aAAA,EAAgB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QAC9C,CAAC;AACD,QAAA,IAAI,EAAE,CAAC,GAAW,EAAE,GAAG,IAAe,KAAU;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAA,kBAAA,EAAqB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QACnD,CAAC;KACF;AACH;;;;"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class EventProcessor {
|
|
4
|
+
constructor(config, exporter) {
|
|
5
|
+
this._queue = [];
|
|
6
|
+
this._timer = null;
|
|
7
|
+
this._hasFlushed = false;
|
|
8
|
+
this._currentBytes = 0;
|
|
9
|
+
/** Track pending async enqueue promises so shutdown can wait for them */
|
|
10
|
+
this._pendingEnqueues = new Set();
|
|
11
|
+
this._config = config;
|
|
12
|
+
this._exporter = exporter;
|
|
13
|
+
}
|
|
14
|
+
/** Enqueue an event. Applies beforeSend hook, ring buffer eviction, flush triggers. */
|
|
15
|
+
enqueue(event) {
|
|
16
|
+
const p = this._enqueueAsync(event);
|
|
17
|
+
this._pendingEnqueues.add(p);
|
|
18
|
+
void p.finally(() => this._pendingEnqueues.delete(p));
|
|
19
|
+
return p;
|
|
20
|
+
}
|
|
21
|
+
async _enqueueAsync(event) {
|
|
22
|
+
// Apply beforeSend hook
|
|
23
|
+
const processed = await this._applyBeforeSend(event);
|
|
24
|
+
if (processed === null) {
|
|
25
|
+
this._config.debug.log(`beforeSend dropped event ${event.event_id}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Ring buffer: evict oldest if at capacity
|
|
29
|
+
if (this._queue.length >= this._config.maxQueueSize) {
|
|
30
|
+
const evicted = this._queue.shift();
|
|
31
|
+
if (evicted) {
|
|
32
|
+
this._currentBytes -= this._eventBytes(evicted);
|
|
33
|
+
this._config.debug.warn(`Ring buffer full — evicted oldest event ${evicted.event_id}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
this._queue.push(processed);
|
|
37
|
+
this._currentBytes += this._eventBytes(processed);
|
|
38
|
+
// First-event fast path
|
|
39
|
+
if (!this._hasFlushed) {
|
|
40
|
+
this._config.debug.log(`First event — immediate flush`);
|
|
41
|
+
await this._triggerFlush();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Count threshold
|
|
45
|
+
if (this._queue.length >= this._config.flushAt) {
|
|
46
|
+
this._config.debug.log(`Count threshold (${this._config.flushAt}) reached — flushing`);
|
|
47
|
+
await this._triggerFlush();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Byte threshold
|
|
51
|
+
if (this._currentBytes >= this._config.maxPayloadBytes) {
|
|
52
|
+
this._config.debug.log(`Byte threshold (${this._config.maxPayloadBytes}) reached — flushing`);
|
|
53
|
+
await this._triggerFlush();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Ensure timer is running
|
|
57
|
+
this._ensureTimer();
|
|
58
|
+
}
|
|
59
|
+
/** Manual flush — waits for pending enqueues, then flushes buffer */
|
|
60
|
+
async flush() {
|
|
61
|
+
if (this._pendingEnqueues.size > 0) {
|
|
62
|
+
await Promise.all([...this._pendingEnqueues]);
|
|
63
|
+
}
|
|
64
|
+
await this._triggerFlush();
|
|
65
|
+
}
|
|
66
|
+
/** Flush and stop timer */
|
|
67
|
+
async shutdown() {
|
|
68
|
+
this._stopTimer();
|
|
69
|
+
if (this._pendingEnqueues.size > 0) {
|
|
70
|
+
await Promise.all([...this._pendingEnqueues]);
|
|
71
|
+
}
|
|
72
|
+
await this._triggerFlush();
|
|
73
|
+
await this._exporter.drain();
|
|
74
|
+
}
|
|
75
|
+
/** Stop the interval timer */
|
|
76
|
+
_stopTimer() {
|
|
77
|
+
if (this._timer !== null) {
|
|
78
|
+
clearTimeout(this._timer);
|
|
79
|
+
this._timer = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
_ensureTimer() {
|
|
83
|
+
if (this._timer !== null)
|
|
84
|
+
return;
|
|
85
|
+
this._timer = setTimeout(() => {
|
|
86
|
+
this._timer = null;
|
|
87
|
+
this._config.debug.log(`Timer flush after ${this._config.flushInterval}ms`);
|
|
88
|
+
void this._triggerFlush();
|
|
89
|
+
}, this._config.flushInterval);
|
|
90
|
+
}
|
|
91
|
+
async _triggerFlush() {
|
|
92
|
+
this._stopTimer();
|
|
93
|
+
if (this._queue.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
// Drain the queue
|
|
96
|
+
const batch = this._queue.splice(0, this._queue.length);
|
|
97
|
+
this._currentBytes = 0;
|
|
98
|
+
this._hasFlushed = true;
|
|
99
|
+
// Apply beforeFlush hook
|
|
100
|
+
const finalBatch = await this._applyBeforeFlush(batch);
|
|
101
|
+
if (finalBatch === null || finalBatch.length === 0) {
|
|
102
|
+
this._config.debug.log(`beforeFlush dropped batch of ${batch.length} events`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
await this._exporter.flush(finalBatch);
|
|
106
|
+
}
|
|
107
|
+
async _applyBeforeSend(event) {
|
|
108
|
+
if (!this._config.beforeSend)
|
|
109
|
+
return event;
|
|
110
|
+
try {
|
|
111
|
+
return await this._config.beforeSend(event);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this._config.debug.warn(`beforeSend threw: ${String(err)}`);
|
|
115
|
+
return event; // fail-open
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async _applyBeforeFlush(batch) {
|
|
119
|
+
if (!this._config.beforeFlush)
|
|
120
|
+
return batch;
|
|
121
|
+
try {
|
|
122
|
+
return await this._config.beforeFlush(batch);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
this._config.debug.warn(`beforeFlush threw: ${String(err)}`);
|
|
126
|
+
return batch; // fail-open
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
_eventBytes(event) {
|
|
130
|
+
return Buffer.byteLength(JSON.stringify(event), 'utf8');
|
|
131
|
+
}
|
|
132
|
+
/** Expose queue length for testing */
|
|
133
|
+
get queueLength() {
|
|
134
|
+
return this._queue.length;
|
|
135
|
+
}
|
|
136
|
+
/** Expose hasFlushed for testing */
|
|
137
|
+
get hasFlushed() {
|
|
138
|
+
return this._hasFlushed;
|
|
139
|
+
}
|
|
140
|
+
/** Override flushAt threshold (used by serverless wrappers to force flushAt=1) */
|
|
141
|
+
setFlushAt(n) {
|
|
142
|
+
this._config.flushAt = n;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
exports.EventProcessor = EventProcessor;
|
|
147
|
+
//# sourceMappingURL=event-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-processor.js","sources":["../../../src/event-processor.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|