@justanalyticsapp/node 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/dist/client.d.ts +286 -0
- package/dist/client.js +681 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +126 -0
- package/dist/context.js +170 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +135 -0
- package/dist/errors.js +180 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +301 -0
- package/dist/index.js +314 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/express.d.ts +77 -0
- package/dist/integrations/express.js +87 -0
- package/dist/integrations/express.js.map +1 -0
- package/dist/integrations/http.d.ts +129 -0
- package/dist/integrations/http.js +465 -0
- package/dist/integrations/http.js.map +1 -0
- package/dist/integrations/metrics.d.ts +110 -0
- package/dist/integrations/metrics.js +313 -0
- package/dist/integrations/metrics.js.map +1 -0
- package/dist/integrations/next.d.ts +252 -0
- package/dist/integrations/next.js +480 -0
- package/dist/integrations/next.js.map +1 -0
- package/dist/integrations/pg.d.ts +169 -0
- package/dist/integrations/pg.js +616 -0
- package/dist/integrations/pg.js.map +1 -0
- package/dist/integrations/pino.d.ts +52 -0
- package/dist/integrations/pino.js +153 -0
- package/dist/integrations/pino.js.map +1 -0
- package/dist/integrations/redis.d.ts +190 -0
- package/dist/integrations/redis.js +597 -0
- package/dist/integrations/redis.js.map +1 -0
- package/dist/integrations/winston.d.ts +48 -0
- package/dist/integrations/winston.js +99 -0
- package/dist/integrations/winston.js.map +1 -0
- package/dist/logger.d.ts +148 -0
- package/dist/logger.js +162 -0
- package/dist/logger.js.map +1 -0
- package/dist/span.d.ts +192 -0
- package/dist/span.js +197 -0
- package/dist/span.js.map +1 -0
- package/dist/transport.d.ts +246 -0
- package/dist/transport.js +654 -0
- package/dist/transport.js.map +1 -0
- package/dist/utils/headers.d.ts +60 -0
- package/dist/utils/headers.js +93 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/id.d.ts +23 -0
- package/dist/utils/id.js +36 -0
- package/dist/utils/id.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file packages/node-sdk/src/transport.ts
|
|
4
|
+
* @description Batched HTTP transport for sending span and error data to the JustAnalytics server.
|
|
5
|
+
*
|
|
6
|
+
* Implements Story 035 - Node.js SDK Core
|
|
7
|
+
* Updated for Story 041 - Server-Side Error Tracking via SDK
|
|
8
|
+
* Updated for Story 046 - SDK Log Integration
|
|
9
|
+
* Updated for Story 048 - Infrastructure Metrics Collection
|
|
10
|
+
*
|
|
11
|
+
* The BatchTransport collects ended spans, error events, log entries, and
|
|
12
|
+
* infrastructure metrics in separate in-memory buffers and periodically
|
|
13
|
+
* flushes them to their respective endpoints:
|
|
14
|
+
* - Spans: `POST /api/ingest/spans` (batched, 1-100 per request)
|
|
15
|
+
* - Errors: `POST /api/ingest/errors` (individual, one per request)
|
|
16
|
+
* - Logs: `POST /api/ingest/logs` (batched, 1-100 per request)
|
|
17
|
+
* - Metrics: `POST /api/ingest/metrics` (batched, 1-500 per request)
|
|
18
|
+
*
|
|
19
|
+
* It uses Node.js built-in `http`/`https` modules (not fetch, not axios)
|
|
20
|
+
* for maximum compatibility with Node.js 18+.
|
|
21
|
+
*
|
|
22
|
+
* Flush behavior:
|
|
23
|
+
* - Timer-based: flushes every `flushIntervalMs` milliseconds (default: 2000)
|
|
24
|
+
* - Size-based: flushes immediately when buffer reaches `maxBatchSize` (default: 100)
|
|
25
|
+
* - Manual: `flush()` can be called explicitly
|
|
26
|
+
*
|
|
27
|
+
* Error handling:
|
|
28
|
+
* - HTTP 429 (rate limited): doubles flush interval for 60 seconds, then resets
|
|
29
|
+
* - Network errors / non-2xx: logs warning (in debug mode), drops the batch
|
|
30
|
+
* - Never throws unhandled exceptions that could crash the host process
|
|
31
|
+
*
|
|
32
|
+
* References:
|
|
33
|
+
* - src/app/api/ingest/spans/route.ts (target endpoint and expected payload format)
|
|
34
|
+
* - src/app/api/ingest/errors/route.ts (error ingestion endpoint)
|
|
35
|
+
*/
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.BatchTransport = void 0;
|
|
41
|
+
const http_1 = __importDefault(require("http"));
|
|
42
|
+
const https_1 = __importDefault(require("https"));
|
|
43
|
+
const url_1 = require("url");
|
|
44
|
+
/**
|
|
45
|
+
* BatchTransport collects spans and errors in memory and sends them
|
|
46
|
+
* in batches to the JustAnalytics ingestion endpoints.
|
|
47
|
+
*
|
|
48
|
+
* Uses Node.js built-in `http`/`https` modules for HTTP communication.
|
|
49
|
+
* Implements timer-based and size-based flushing with 429 backoff.
|
|
50
|
+
*/
|
|
51
|
+
class BatchTransport {
|
|
52
|
+
constructor(options) {
|
|
53
|
+
this.buffer = [];
|
|
54
|
+
this.errorBuffer = [];
|
|
55
|
+
this.logBuffer = [];
|
|
56
|
+
this.metricBuffer = [];
|
|
57
|
+
this.timer = null;
|
|
58
|
+
/** Independent flushing guards for spans, errors, logs, and metrics */
|
|
59
|
+
this.isFlushingSpans = false;
|
|
60
|
+
this.isFlushingErrors = false;
|
|
61
|
+
this.isFlushingLogs = false;
|
|
62
|
+
this.isFlushingMetrics = false;
|
|
63
|
+
/** Timer for resetting flush interval after 429 backoff */
|
|
64
|
+
this.backoffResetTimer = null;
|
|
65
|
+
this.serverUrl = options.serverUrl;
|
|
66
|
+
this.apiKey = options.apiKey;
|
|
67
|
+
this.flushIntervalMs = options.flushIntervalMs;
|
|
68
|
+
this.maxBatchSize = options.maxBatchSize;
|
|
69
|
+
this.debug = options.debug;
|
|
70
|
+
this.originalFlushIntervalMs = options.flushIntervalMs;
|
|
71
|
+
this.currentFlushIntervalMs = options.flushIntervalMs;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Enqueue a span payload for batched sending.
|
|
75
|
+
*
|
|
76
|
+
* If the buffer reaches `maxBatchSize`, triggers an immediate flush.
|
|
77
|
+
*
|
|
78
|
+
* @param span - Serialized span payload to enqueue
|
|
79
|
+
*/
|
|
80
|
+
enqueue(span) {
|
|
81
|
+
this.buffer.push(span);
|
|
82
|
+
if (this.buffer.length >= this.maxBatchSize) {
|
|
83
|
+
// Trigger immediate flush (fire-and-forget)
|
|
84
|
+
this.flushSpans().catch(() => {
|
|
85
|
+
// Errors handled inside flushSpans()
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Enqueue an error event payload for batched sending.
|
|
91
|
+
* Errors are flushed alongside spans on the periodic timer.
|
|
92
|
+
*
|
|
93
|
+
* @param payload - Error event payload to enqueue
|
|
94
|
+
*/
|
|
95
|
+
enqueueError(payload) {
|
|
96
|
+
this.errorBuffer.push(payload);
|
|
97
|
+
if (this.errorBuffer.length >= this.maxBatchSize) {
|
|
98
|
+
this.flushErrors().catch(() => {
|
|
99
|
+
// Errors handled inside flushErrors()
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Enqueue a log entry payload for batched sending.
|
|
105
|
+
* Logs are flushed alongside spans and errors on the periodic timer.
|
|
106
|
+
*
|
|
107
|
+
* If the buffer reaches `maxBatchSize`, triggers an immediate flush.
|
|
108
|
+
*
|
|
109
|
+
* @param entry - Log entry payload to enqueue
|
|
110
|
+
*/
|
|
111
|
+
enqueueLog(entry) {
|
|
112
|
+
this.logBuffer.push(entry);
|
|
113
|
+
if (this.logBuffer.length >= this.maxBatchSize) {
|
|
114
|
+
this.flushLogs().catch(() => {
|
|
115
|
+
// Errors handled inside flushLogs()
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Enqueue an infrastructure metric payload for batched sending.
|
|
121
|
+
* Metrics are flushed alongside spans, errors, and logs on the periodic timer.
|
|
122
|
+
*
|
|
123
|
+
* If the buffer reaches `maxBatchSize`, triggers an immediate flush.
|
|
124
|
+
*
|
|
125
|
+
* @param metric - Metric data point payload to enqueue
|
|
126
|
+
*/
|
|
127
|
+
enqueueMetric(metric) {
|
|
128
|
+
this.metricBuffer.push(metric);
|
|
129
|
+
if (this.metricBuffer.length >= this.maxBatchSize) {
|
|
130
|
+
this.flushMetrics().catch(() => {
|
|
131
|
+
// Errors handled inside flushMetrics()
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Send a single error immediately (non-batched).
|
|
137
|
+
* Used for uncaughtException where the process is about to exit.
|
|
138
|
+
*
|
|
139
|
+
* Fire-and-forget: the promise result is intentionally not awaited
|
|
140
|
+
* by the caller because the process is exiting.
|
|
141
|
+
*
|
|
142
|
+
* @param payload - Error event payload to send immediately
|
|
143
|
+
*/
|
|
144
|
+
sendErrorImmediate(payload) {
|
|
145
|
+
try {
|
|
146
|
+
this.sendSingleError(payload).catch(() => {
|
|
147
|
+
// Never throw from immediate send
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Never throw
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Flush all pending spans and errors to the server.
|
|
156
|
+
*
|
|
157
|
+
* Delegates to `flushSpans()` and `flushErrors()` in parallel.
|
|
158
|
+
* Each sub-method has its own isFlushing guard.
|
|
159
|
+
*
|
|
160
|
+
* @returns Promise that resolves when both flushes complete
|
|
161
|
+
*/
|
|
162
|
+
async flush() {
|
|
163
|
+
await Promise.all([
|
|
164
|
+
this.flushSpans(),
|
|
165
|
+
this.flushErrors(),
|
|
166
|
+
this.flushLogs(),
|
|
167
|
+
this.flushMetrics(),
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Flush all pending span payloads to `POST /api/ingest/spans`.
|
|
172
|
+
*
|
|
173
|
+
* Sends all buffered spans in a single HTTP request.
|
|
174
|
+
* The buffer is cleared before sending, so new spans that arrive
|
|
175
|
+
* during the flush are queued for the next flush.
|
|
176
|
+
*
|
|
177
|
+
* @returns Promise that resolves when the flush completes (or fails)
|
|
178
|
+
*/
|
|
179
|
+
async flushSpans() {
|
|
180
|
+
if (this.buffer.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
if (this.isFlushingSpans)
|
|
183
|
+
return;
|
|
184
|
+
this.isFlushingSpans = true;
|
|
185
|
+
// Take the current buffer and clear it
|
|
186
|
+
const batch = this.buffer.splice(0, this.buffer.length);
|
|
187
|
+
try {
|
|
188
|
+
await this.sendBatch(batch);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (this.debug) {
|
|
192
|
+
console.debug(`[JustAnalytics] Span flush failed, dropped ${batch.length} span(s):`, error instanceof Error ? error.message : String(error));
|
|
193
|
+
}
|
|
194
|
+
// Drop the batch -- do not re-enqueue to prevent memory leaks
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
this.isFlushingSpans = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Flush all pending error event payloads to `POST /api/ingest/errors`.
|
|
202
|
+
*
|
|
203
|
+
* Each error is sent as an individual POST request because the server-side
|
|
204
|
+
* fingerprinting and upsert logic operates on individual events.
|
|
205
|
+
*
|
|
206
|
+
* @returns Promise that resolves when all errors are sent (or dropped)
|
|
207
|
+
*/
|
|
208
|
+
async flushErrors() {
|
|
209
|
+
if (this.errorBuffer.length === 0)
|
|
210
|
+
return;
|
|
211
|
+
if (this.isFlushingErrors)
|
|
212
|
+
return;
|
|
213
|
+
this.isFlushingErrors = true;
|
|
214
|
+
const batch = this.errorBuffer.splice(0, this.errorBuffer.length);
|
|
215
|
+
try {
|
|
216
|
+
await this.sendErrorBatch(batch);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
if (this.debug) {
|
|
220
|
+
console.debug(`[JustAnalytics] Error flush failed, dropped ${batch.length} error(s):`, error instanceof Error ? error.message : String(error));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
this.isFlushingErrors = false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Start the periodic flush timer.
|
|
229
|
+
*
|
|
230
|
+
* Uses `setInterval` with the current effective flush interval.
|
|
231
|
+
* The timer is `unref()`'d so it does not keep the process alive.
|
|
232
|
+
*/
|
|
233
|
+
start() {
|
|
234
|
+
this.stop(); // Clear any existing timer
|
|
235
|
+
this.timer = setInterval(() => {
|
|
236
|
+
this.flush().catch(() => {
|
|
237
|
+
// Errors handled inside flush()
|
|
238
|
+
});
|
|
239
|
+
}, this.currentFlushIntervalMs);
|
|
240
|
+
// unref() so the timer doesn't keep the Node.js process alive
|
|
241
|
+
if (this.timer && typeof this.timer.unref === 'function') {
|
|
242
|
+
this.timer.unref();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Stop the periodic flush timer.
|
|
247
|
+
*/
|
|
248
|
+
stop() {
|
|
249
|
+
if (this.timer !== null) {
|
|
250
|
+
clearInterval(this.timer);
|
|
251
|
+
this.timer = null;
|
|
252
|
+
}
|
|
253
|
+
if (this.backoffResetTimer !== null) {
|
|
254
|
+
clearTimeout(this.backoffResetTimer);
|
|
255
|
+
this.backoffResetTimer = null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Number of spans currently in the buffer awaiting flush.
|
|
260
|
+
*/
|
|
261
|
+
get pendingCount() {
|
|
262
|
+
return this.buffer.length;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Number of errors currently in the error buffer awaiting flush.
|
|
266
|
+
*/
|
|
267
|
+
get pendingErrorCount() {
|
|
268
|
+
return this.errorBuffer.length;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Number of log entries currently in the buffer awaiting flush.
|
|
272
|
+
*/
|
|
273
|
+
get pendingLogCount() {
|
|
274
|
+
return this.logBuffer.length;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Number of metrics currently in the buffer awaiting flush.
|
|
278
|
+
*/
|
|
279
|
+
get pendingMetricCount() {
|
|
280
|
+
return this.metricBuffer.length;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Send a batch of spans via HTTP POST to /api/ingest/spans.
|
|
284
|
+
*
|
|
285
|
+
* Uses Node.js built-in `http` or `https` module based on the server URL
|
|
286
|
+
* protocol. Handles HTTP 429 with backoff.
|
|
287
|
+
*
|
|
288
|
+
* @param batch - Array of span payloads to send
|
|
289
|
+
*/
|
|
290
|
+
sendBatch(batch) {
|
|
291
|
+
return new Promise((resolve) => {
|
|
292
|
+
const body = JSON.stringify({ spans: batch });
|
|
293
|
+
const url = new url_1.URL('/api/ingest/spans', this.serverUrl);
|
|
294
|
+
const isHttps = url.protocol === 'https:';
|
|
295
|
+
const transport = isHttps ? https_1.default : http_1.default;
|
|
296
|
+
const requestOptions = {
|
|
297
|
+
hostname: url.hostname,
|
|
298
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
299
|
+
path: url.pathname,
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: {
|
|
302
|
+
'Content-Type': 'application/json',
|
|
303
|
+
'Content-Length': Buffer.byteLength(body),
|
|
304
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
305
|
+
},
|
|
306
|
+
timeout: 10000, // 10 second timeout
|
|
307
|
+
};
|
|
308
|
+
const req = transport.request(requestOptions, (res) => {
|
|
309
|
+
// Consume response body
|
|
310
|
+
const chunks = [];
|
|
311
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
312
|
+
res.on('end', () => {
|
|
313
|
+
const statusCode = res.statusCode || 0;
|
|
314
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
315
|
+
if (this.debug) {
|
|
316
|
+
console.debug(`[JustAnalytics] Flushed ${batch.length} span(s) successfully`);
|
|
317
|
+
}
|
|
318
|
+
resolve();
|
|
319
|
+
}
|
|
320
|
+
else if (statusCode === 429) {
|
|
321
|
+
this.handleBackoff();
|
|
322
|
+
if (this.debug) {
|
|
323
|
+
console.debug(`[JustAnalytics] Rate limited (429). Backing off flush interval. Dropped ${batch.length} span(s).`);
|
|
324
|
+
}
|
|
325
|
+
resolve(); // Don't reject -- we handled it
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
if (this.debug) {
|
|
329
|
+
const responseBody = Buffer.concat(chunks).toString('utf-8');
|
|
330
|
+
console.debug(`[JustAnalytics] Flush failed with HTTP ${statusCode}. Dropped ${batch.length} span(s). Response: ${responseBody}`);
|
|
331
|
+
}
|
|
332
|
+
resolve(); // Don't reject for HTTP errors -- just drop the batch
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
req.on('error', (error) => {
|
|
337
|
+
if (this.debug) {
|
|
338
|
+
console.debug(`[JustAnalytics] Network error during flush: ${error.message}. Dropped ${batch.length} span(s).`);
|
|
339
|
+
}
|
|
340
|
+
// Resolve instead of reject so the caller doesn't need to handle it
|
|
341
|
+
resolve();
|
|
342
|
+
});
|
|
343
|
+
req.on('timeout', () => {
|
|
344
|
+
req.destroy();
|
|
345
|
+
if (this.debug) {
|
|
346
|
+
console.debug(`[JustAnalytics] Flush request timed out. Dropped ${batch.length} span(s).`);
|
|
347
|
+
}
|
|
348
|
+
resolve();
|
|
349
|
+
});
|
|
350
|
+
req.write(body);
|
|
351
|
+
req.end();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Send a batch of error events via HTTP POST.
|
|
356
|
+
* Each error is sent as an individual POST to /api/ingest/errors
|
|
357
|
+
* because the server-side fingerprinting and upsert logic operates
|
|
358
|
+
* on individual events.
|
|
359
|
+
*
|
|
360
|
+
* @param batch - Array of error payloads to send
|
|
361
|
+
*/
|
|
362
|
+
async sendErrorBatch(batch) {
|
|
363
|
+
for (const payload of batch) {
|
|
364
|
+
await this.sendSingleError(payload);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Send a single error event via HTTP POST to /api/ingest/errors.
|
|
369
|
+
*
|
|
370
|
+
* @param payload - Error event payload to send
|
|
371
|
+
*/
|
|
372
|
+
sendSingleError(payload) {
|
|
373
|
+
return new Promise((resolve) => {
|
|
374
|
+
try {
|
|
375
|
+
const body = JSON.stringify(payload);
|
|
376
|
+
const url = new url_1.URL('/api/ingest/errors', this.serverUrl);
|
|
377
|
+
const isHttps = url.protocol === 'https:';
|
|
378
|
+
const transport = isHttps ? https_1.default : http_1.default;
|
|
379
|
+
const requestOptions = {
|
|
380
|
+
hostname: url.hostname,
|
|
381
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
382
|
+
path: url.pathname,
|
|
383
|
+
method: 'POST',
|
|
384
|
+
headers: {
|
|
385
|
+
'Content-Type': 'application/json',
|
|
386
|
+
'Content-Length': Buffer.byteLength(body),
|
|
387
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
388
|
+
},
|
|
389
|
+
timeout: 10000,
|
|
390
|
+
};
|
|
391
|
+
const req = transport.request(requestOptions, (res) => {
|
|
392
|
+
// Consume response body
|
|
393
|
+
const chunks = [];
|
|
394
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
395
|
+
res.on('end', () => {
|
|
396
|
+
const statusCode = res.statusCode || 0;
|
|
397
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
398
|
+
if (this.debug) {
|
|
399
|
+
console.debug(`[JustAnalytics] Error event sent successfully (eventId: ${payload.eventId})`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
if (this.debug) {
|
|
404
|
+
const responseBody = Buffer.concat(chunks).toString('utf-8');
|
|
405
|
+
console.debug(`[JustAnalytics] Error send failed with HTTP ${statusCode}. Response: ${responseBody}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
resolve();
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
req.on('error', (error) => {
|
|
412
|
+
if (this.debug) {
|
|
413
|
+
console.debug(`[JustAnalytics] Network error sending error event: ${error.message}`);
|
|
414
|
+
}
|
|
415
|
+
resolve();
|
|
416
|
+
});
|
|
417
|
+
req.on('timeout', () => {
|
|
418
|
+
req.destroy();
|
|
419
|
+
if (this.debug) {
|
|
420
|
+
console.debug(`[JustAnalytics] Error send request timed out.`);
|
|
421
|
+
}
|
|
422
|
+
resolve();
|
|
423
|
+
});
|
|
424
|
+
req.write(body);
|
|
425
|
+
req.end();
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
// Never throw from error send
|
|
429
|
+
resolve();
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Flush all pending log entry payloads to POST /api/ingest/logs.
|
|
435
|
+
*
|
|
436
|
+
* Sends all buffered logs in a single HTTP POST request.
|
|
437
|
+
* The buffer is cleared before sending, so new logs that arrive
|
|
438
|
+
* during the flush are queued for the next flush.
|
|
439
|
+
*
|
|
440
|
+
* @returns Promise that resolves when the flush completes (or fails)
|
|
441
|
+
*/
|
|
442
|
+
async flushLogs() {
|
|
443
|
+
if (this.logBuffer.length === 0)
|
|
444
|
+
return;
|
|
445
|
+
if (this.isFlushingLogs)
|
|
446
|
+
return;
|
|
447
|
+
this.isFlushingLogs = true;
|
|
448
|
+
const batch = this.logBuffer.splice(0, this.logBuffer.length);
|
|
449
|
+
try {
|
|
450
|
+
await this.sendLogBatch(batch);
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
if (this.debug) {
|
|
454
|
+
console.debug(`[JustAnalytics] Log flush failed, dropped ${batch.length} log(s):`, error instanceof Error ? error.message : String(error));
|
|
455
|
+
}
|
|
456
|
+
// Drop the batch -- do not re-enqueue to prevent memory leaks
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
this.isFlushingLogs = false;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Send a batch of log entries via HTTP POST to /api/ingest/logs.
|
|
464
|
+
*
|
|
465
|
+
* Uses Node.js built-in `http`/`https` module based on the server URL
|
|
466
|
+
* protocol. Handles HTTP 429 with backoff.
|
|
467
|
+
*
|
|
468
|
+
* @param batch - Array of log entry payloads to send
|
|
469
|
+
*/
|
|
470
|
+
sendLogBatch(batch) {
|
|
471
|
+
return new Promise((resolve) => {
|
|
472
|
+
const body = JSON.stringify({ logs: batch });
|
|
473
|
+
const url = new url_1.URL('/api/ingest/logs', this.serverUrl);
|
|
474
|
+
const isHttps = url.protocol === 'https:';
|
|
475
|
+
const transport = isHttps ? https_1.default : http_1.default;
|
|
476
|
+
const requestOptions = {
|
|
477
|
+
hostname: url.hostname,
|
|
478
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
479
|
+
path: url.pathname,
|
|
480
|
+
method: 'POST',
|
|
481
|
+
headers: {
|
|
482
|
+
'Content-Type': 'application/json',
|
|
483
|
+
'Content-Length': Buffer.byteLength(body),
|
|
484
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
485
|
+
},
|
|
486
|
+
timeout: 10000,
|
|
487
|
+
};
|
|
488
|
+
const req = transport.request(requestOptions, (res) => {
|
|
489
|
+
const chunks = [];
|
|
490
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
491
|
+
res.on('end', () => {
|
|
492
|
+
const statusCode = res.statusCode || 0;
|
|
493
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
494
|
+
if (this.debug) {
|
|
495
|
+
console.debug(`[JustAnalytics] Flushed ${batch.length} log(s) successfully`);
|
|
496
|
+
}
|
|
497
|
+
resolve();
|
|
498
|
+
}
|
|
499
|
+
else if (statusCode === 429) {
|
|
500
|
+
this.handleBackoff();
|
|
501
|
+
if (this.debug) {
|
|
502
|
+
console.debug(`[JustAnalytics] Rate limited (429). Backing off. Dropped ${batch.length} log(s).`);
|
|
503
|
+
}
|
|
504
|
+
resolve();
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
if (this.debug) {
|
|
508
|
+
const responseBody = Buffer.concat(chunks).toString('utf-8');
|
|
509
|
+
console.debug(`[JustAnalytics] Log flush failed with HTTP ${statusCode}. Dropped ${batch.length} log(s). Response: ${responseBody}`);
|
|
510
|
+
}
|
|
511
|
+
resolve();
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
req.on('error', (error) => {
|
|
516
|
+
if (this.debug) {
|
|
517
|
+
console.debug(`[JustAnalytics] Network error during log flush: ${error.message}. Dropped ${batch.length} log(s).`);
|
|
518
|
+
}
|
|
519
|
+
resolve();
|
|
520
|
+
});
|
|
521
|
+
req.on('timeout', () => {
|
|
522
|
+
req.destroy();
|
|
523
|
+
if (this.debug) {
|
|
524
|
+
console.debug(`[JustAnalytics] Log flush request timed out. Dropped ${batch.length} log(s).`);
|
|
525
|
+
}
|
|
526
|
+
resolve();
|
|
527
|
+
});
|
|
528
|
+
req.write(body);
|
|
529
|
+
req.end();
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Flush all pending metric payloads to POST /api/ingest/metrics.
|
|
534
|
+
*
|
|
535
|
+
* Sends all buffered metrics in a single HTTP POST request.
|
|
536
|
+
* The buffer is cleared before sending, so new metrics that arrive
|
|
537
|
+
* during the flush are queued for the next flush.
|
|
538
|
+
*
|
|
539
|
+
* @returns Promise that resolves when the flush completes (or fails)
|
|
540
|
+
*/
|
|
541
|
+
async flushMetrics() {
|
|
542
|
+
if (this.metricBuffer.length === 0)
|
|
543
|
+
return;
|
|
544
|
+
if (this.isFlushingMetrics)
|
|
545
|
+
return;
|
|
546
|
+
this.isFlushingMetrics = true;
|
|
547
|
+
const batch = this.metricBuffer.splice(0, this.metricBuffer.length);
|
|
548
|
+
try {
|
|
549
|
+
await this.sendMetricBatch(batch);
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
if (this.debug) {
|
|
553
|
+
console.debug(`[JustAnalytics] Metric flush failed, dropped ${batch.length} metric(s):`, error instanceof Error ? error.message : String(error));
|
|
554
|
+
}
|
|
555
|
+
// Drop the batch -- do not re-enqueue to prevent memory leaks
|
|
556
|
+
}
|
|
557
|
+
finally {
|
|
558
|
+
this.isFlushingMetrics = false;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Send a batch of metrics via HTTP POST to /api/ingest/metrics.
|
|
563
|
+
*
|
|
564
|
+
* Uses Node.js built-in `http`/`https` module based on the server URL
|
|
565
|
+
* protocol. Handles HTTP 429 with backoff.
|
|
566
|
+
*
|
|
567
|
+
* @param batch - Array of metric payloads to send
|
|
568
|
+
*/
|
|
569
|
+
sendMetricBatch(batch) {
|
|
570
|
+
return new Promise((resolve) => {
|
|
571
|
+
const body = JSON.stringify({ metrics: batch });
|
|
572
|
+
const url = new url_1.URL('/api/ingest/metrics', this.serverUrl);
|
|
573
|
+
const isHttps = url.protocol === 'https:';
|
|
574
|
+
const transport = isHttps ? https_1.default : http_1.default;
|
|
575
|
+
const requestOptions = {
|
|
576
|
+
hostname: url.hostname,
|
|
577
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
578
|
+
path: url.pathname,
|
|
579
|
+
method: 'POST',
|
|
580
|
+
headers: {
|
|
581
|
+
'Content-Type': 'application/json',
|
|
582
|
+
'Content-Length': Buffer.byteLength(body),
|
|
583
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
584
|
+
},
|
|
585
|
+
timeout: 10000,
|
|
586
|
+
};
|
|
587
|
+
const req = transport.request(requestOptions, (res) => {
|
|
588
|
+
const chunks = [];
|
|
589
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
590
|
+
res.on('end', () => {
|
|
591
|
+
const statusCode = res.statusCode || 0;
|
|
592
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
593
|
+
if (this.debug) {
|
|
594
|
+
console.debug(`[JustAnalytics] Flushed ${batch.length} metric(s) successfully`);
|
|
595
|
+
}
|
|
596
|
+
resolve();
|
|
597
|
+
}
|
|
598
|
+
else if (statusCode === 429) {
|
|
599
|
+
this.handleBackoff();
|
|
600
|
+
if (this.debug) {
|
|
601
|
+
console.debug(`[JustAnalytics] Rate limited (429). Backing off. Dropped ${batch.length} metric(s).`);
|
|
602
|
+
}
|
|
603
|
+
resolve();
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
if (this.debug) {
|
|
607
|
+
const responseBody = Buffer.concat(chunks).toString('utf-8');
|
|
608
|
+
console.debug(`[JustAnalytics] Metric flush failed with HTTP ${statusCode}. Dropped ${batch.length} metric(s). Response: ${responseBody}`);
|
|
609
|
+
}
|
|
610
|
+
resolve();
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
req.on('error', (error) => {
|
|
615
|
+
if (this.debug) {
|
|
616
|
+
console.debug(`[JustAnalytics] Network error during metric flush: ${error.message}. Dropped ${batch.length} metric(s).`);
|
|
617
|
+
}
|
|
618
|
+
resolve();
|
|
619
|
+
});
|
|
620
|
+
req.on('timeout', () => {
|
|
621
|
+
req.destroy();
|
|
622
|
+
if (this.debug) {
|
|
623
|
+
console.debug(`[JustAnalytics] Metric flush request timed out. Dropped ${batch.length} metric(s).`);
|
|
624
|
+
}
|
|
625
|
+
resolve();
|
|
626
|
+
});
|
|
627
|
+
req.write(body);
|
|
628
|
+
req.end();
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Handle HTTP 429 backoff: double the flush interval for 60 seconds, then reset.
|
|
633
|
+
*/
|
|
634
|
+
handleBackoff() {
|
|
635
|
+
// Double the effective flush interval
|
|
636
|
+
this.currentFlushIntervalMs = this.originalFlushIntervalMs * 2;
|
|
637
|
+
// Restart the timer with the new interval
|
|
638
|
+
this.start();
|
|
639
|
+
// Reset after 60 seconds
|
|
640
|
+
if (this.backoffResetTimer !== null) {
|
|
641
|
+
clearTimeout(this.backoffResetTimer);
|
|
642
|
+
}
|
|
643
|
+
this.backoffResetTimer = setTimeout(() => {
|
|
644
|
+
this.currentFlushIntervalMs = this.originalFlushIntervalMs;
|
|
645
|
+
this.start(); // Restart with original interval
|
|
646
|
+
this.backoffResetTimer = null;
|
|
647
|
+
}, 60000);
|
|
648
|
+
if (this.backoffResetTimer && typeof this.backoffResetTimer.unref === 'function') {
|
|
649
|
+
this.backoffResetTimer.unref();
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
exports.BatchTransport = BatchTransport;
|
|
654
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|