@owlmetry/node 0.1.2 → 0.1.4
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/src/index.js +14 -3
- package/dist/src/transport.d.ts +1 -0
- package/dist/src/transport.js +61 -49
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -199,11 +199,18 @@ export const Owl = {
|
|
|
199
199
|
if (!beforeExitRegistered) {
|
|
200
200
|
beforeExitRegistered = true;
|
|
201
201
|
process.on("beforeExit", async () => {
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
try {
|
|
203
|
+
if (transport && transport.bufferSize > 0) {
|
|
204
|
+
await transport.flush();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Best-effort flush on exit — never crash the host process
|
|
204
209
|
}
|
|
205
210
|
});
|
|
206
211
|
}
|
|
212
|
+
// Emit session start event
|
|
213
|
+
log("info", "sdk:session_started");
|
|
207
214
|
},
|
|
208
215
|
info(message, attrs) {
|
|
209
216
|
log("info", message, attrs);
|
|
@@ -234,7 +241,10 @@ export const Owl = {
|
|
|
234
241
|
return experiments[name];
|
|
235
242
|
}
|
|
236
243
|
if (options.length === 0) {
|
|
237
|
-
|
|
244
|
+
if (config?.debug) {
|
|
245
|
+
console.error(`OwlMetry: getVariant("${name}") called with empty options array`);
|
|
246
|
+
}
|
|
247
|
+
return "";
|
|
238
248
|
}
|
|
239
249
|
const variant = options[Math.floor(Math.random() * options.length)];
|
|
240
250
|
experiments[name] = variant;
|
|
@@ -290,6 +300,7 @@ export const Owl = {
|
|
|
290
300
|
},
|
|
291
301
|
async shutdown() {
|
|
292
302
|
if (transport) {
|
|
303
|
+
log("info", "sdk:session_ended");
|
|
293
304
|
await transport.shutdown();
|
|
294
305
|
transport = null;
|
|
295
306
|
}
|
package/dist/src/transport.d.ts
CHANGED
package/dist/src/transport.js
CHANGED
|
@@ -11,7 +11,7 @@ export class Transport {
|
|
|
11
11
|
flushing = false;
|
|
12
12
|
constructor(config) {
|
|
13
13
|
this.config = config;
|
|
14
|
-
this.timer = setInterval(() => this.flush(), config.flushIntervalMs);
|
|
14
|
+
this.timer = setInterval(() => this.flush().catch((err) => this.logError("flush failed", err)), config.flushIntervalMs);
|
|
15
15
|
// Prevent timer from keeping the process alive
|
|
16
16
|
if (this.timer.unref) {
|
|
17
17
|
this.timer.unref();
|
|
@@ -24,7 +24,7 @@ export class Transport {
|
|
|
24
24
|
}
|
|
25
25
|
this.buffer.push(event);
|
|
26
26
|
if (this.buffer.length >= this.config.flushThreshold) {
|
|
27
|
-
this.flush();
|
|
27
|
+
this.flush().catch((err) => this.logError("flush failed", err));
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
async flush() {
|
|
@@ -51,61 +51,73 @@ export class Transport {
|
|
|
51
51
|
get bufferSize() {
|
|
52
52
|
return this.buffer.length;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
let payload;
|
|
58
|
-
let contentEncoding;
|
|
59
|
-
if (json.length > GZIP_THRESHOLD) {
|
|
60
|
-
payload = new Uint8Array(gzipSync(json));
|
|
61
|
-
contentEncoding = "gzip";
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
payload = json;
|
|
54
|
+
logError(message, err) {
|
|
55
|
+
if (this.config.debug) {
|
|
56
|
+
console.error(`OwlMetry: ${message}`, err);
|
|
65
57
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
58
|
+
}
|
|
59
|
+
async sendBatch(events) {
|
|
60
|
+
try {
|
|
61
|
+
const body = { events };
|
|
62
|
+
const json = JSON.stringify(body);
|
|
63
|
+
let payload;
|
|
64
|
+
let contentEncoding;
|
|
65
|
+
if (json.length > GZIP_THRESHOLD) {
|
|
66
|
+
payload = new Uint8Array(gzipSync(json));
|
|
67
|
+
contentEncoding = "gzip";
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
payload = json;
|
|
71
|
+
}
|
|
72
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
73
|
+
try {
|
|
74
|
+
const headers = {
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
77
|
+
};
|
|
78
|
+
if (contentEncoding) {
|
|
79
|
+
headers["Content-Encoding"] = contentEncoding;
|
|
80
|
+
}
|
|
81
|
+
const res = await fetch(`${this.config.endpoint}/v1/ingest`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers,
|
|
84
|
+
body: payload,
|
|
85
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
86
|
+
});
|
|
87
|
+
if (res.ok)
|
|
88
|
+
return;
|
|
89
|
+
// Don't retry client errors (except 429)
|
|
90
|
+
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
|
|
91
|
+
if (this.config.debug) {
|
|
92
|
+
const text = await res.text().catch(() => "");
|
|
93
|
+
console.error(`OwlMetry: ingest failed with ${res.status}: ${text}`);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Server error or 429 — retry with backoff
|
|
98
|
+
if (attempt < MAX_RETRIES) {
|
|
99
|
+
const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
|
|
100
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
101
|
+
}
|
|
74
102
|
}
|
|
75
|
-
|
|
76
|
-
method: "POST",
|
|
77
|
-
headers,
|
|
78
|
-
body: payload,
|
|
79
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
80
|
-
});
|
|
81
|
-
if (res.ok)
|
|
82
|
-
return;
|
|
83
|
-
// Don't retry client errors (except 429)
|
|
84
|
-
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
|
|
103
|
+
catch (err) {
|
|
85
104
|
if (this.config.debug) {
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
console.error("OwlMetry: network error during ingest", err);
|
|
106
|
+
}
|
|
107
|
+
if (attempt < MAX_RETRIES) {
|
|
108
|
+
const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
|
|
109
|
+
await new Promise((r) => setTimeout(r, backoff));
|
|
88
110
|
}
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// Server error or 429 — retry with backoff
|
|
92
|
-
if (attempt < MAX_RETRIES) {
|
|
93
|
-
const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
|
|
94
|
-
await new Promise((r) => setTimeout(r, backoff));
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
console.error("OwlMetry: network error during ingest", err);
|
|
100
|
-
}
|
|
101
|
-
if (attempt < MAX_RETRIES) {
|
|
102
|
-
const backoff = Math.min(Math.pow(2, attempt) * 1000, MAX_BACKOFF_MS);
|
|
103
|
-
await new Promise((r) => setTimeout(r, backoff));
|
|
104
|
-
}
|
|
113
|
+
if (this.config.debug) {
|
|
114
|
+
console.error(`OwlMetry: failed to send batch after ${MAX_RETRIES + 1} attempts, dropping ${events.length} events`);
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
|
-
|
|
108
|
-
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (this.config.debug) {
|
|
119
|
+
console.error("OwlMetry: failed to prepare batch for sending", err);
|
|
120
|
+
}
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
}
|