@logtide/sdk-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/LICENSE +21 -0
- package/README.md +561 -0
- package/dist/index.cjs +408 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +134 -0
- package/dist/index.d.ts +134 -0
- package/dist/index.js +402 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.cjs +297 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +31 -0
- package/dist/middleware/index.d.ts +31 -0
- package/dist/middleware/index.js +295 -0
- package/dist/middleware/index.js.map +1 -0
- package/package.json +97 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
var CircuitBreaker = class {
|
|
9
|
+
constructor(threshold, resetMs) {
|
|
10
|
+
this.threshold = threshold;
|
|
11
|
+
this.resetMs = resetMs;
|
|
12
|
+
}
|
|
13
|
+
state = "CLOSED" /* CLOSED */;
|
|
14
|
+
failureCount = 0;
|
|
15
|
+
lastFailureTime = null;
|
|
16
|
+
recordSuccess() {
|
|
17
|
+
this.failureCount = 0;
|
|
18
|
+
this.state = "CLOSED" /* CLOSED */;
|
|
19
|
+
}
|
|
20
|
+
recordFailure() {
|
|
21
|
+
this.failureCount++;
|
|
22
|
+
this.lastFailureTime = Date.now();
|
|
23
|
+
if (this.failureCount >= this.threshold) {
|
|
24
|
+
this.state = "OPEN" /* OPEN */;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
canAttempt() {
|
|
28
|
+
if (this.state === "CLOSED" /* CLOSED */) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (this.state === "OPEN" /* OPEN */) {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
if (this.lastFailureTime && now - this.lastFailureTime >= this.resetMs) {
|
|
34
|
+
this.state = "HALF_OPEN" /* HALF_OPEN */;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
getState() {
|
|
42
|
+
return this.state;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function isValidUUID(str) {
|
|
46
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
47
|
+
return uuidRegex.test(str);
|
|
48
|
+
}
|
|
49
|
+
function normalizeTraceId(traceId, debug) {
|
|
50
|
+
if (!traceId) {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
if (isValidUUID(traceId)) {
|
|
54
|
+
return traceId;
|
|
55
|
+
}
|
|
56
|
+
const newTraceId = crypto.randomUUID();
|
|
57
|
+
if (debug) {
|
|
58
|
+
console.warn(
|
|
59
|
+
`[LogTide] Invalid trace_id "${traceId}" (must be UUID v4). Generated new UUID: ${newTraceId}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return newTraceId;
|
|
63
|
+
}
|
|
64
|
+
function serializeError(error) {
|
|
65
|
+
if (error instanceof Error) {
|
|
66
|
+
const result = {
|
|
67
|
+
name: error.name,
|
|
68
|
+
message: error.message,
|
|
69
|
+
stack: error.stack
|
|
70
|
+
};
|
|
71
|
+
if (error.cause) {
|
|
72
|
+
result.cause = serializeError(error.cause);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
if (typeof error === "string") {
|
|
77
|
+
return { message: error };
|
|
78
|
+
}
|
|
79
|
+
if (typeof error === "object" && error !== null) {
|
|
80
|
+
return error;
|
|
81
|
+
}
|
|
82
|
+
return { message: String(error) };
|
|
83
|
+
}
|
|
84
|
+
var LogTideClient = class {
|
|
85
|
+
apiUrl;
|
|
86
|
+
apiKey;
|
|
87
|
+
batchSize;
|
|
88
|
+
flushInterval;
|
|
89
|
+
maxBufferSize;
|
|
90
|
+
maxRetries;
|
|
91
|
+
retryDelayMs;
|
|
92
|
+
enableMetrics;
|
|
93
|
+
debugMode;
|
|
94
|
+
globalMetadata;
|
|
95
|
+
autoTraceId;
|
|
96
|
+
buffer = [];
|
|
97
|
+
timer = null;
|
|
98
|
+
circuitBreaker;
|
|
99
|
+
// Metrics
|
|
100
|
+
metrics = {
|
|
101
|
+
logsSent: 0,
|
|
102
|
+
logsDropped: 0,
|
|
103
|
+
errors: 0,
|
|
104
|
+
retries: 0,
|
|
105
|
+
avgLatencyMs: 0,
|
|
106
|
+
circuitBreakerTrips: 0
|
|
107
|
+
};
|
|
108
|
+
latencies = [];
|
|
109
|
+
// Context tracking
|
|
110
|
+
currentTraceId = null;
|
|
111
|
+
constructor(options) {
|
|
112
|
+
this.apiUrl = options.apiUrl.replace(/\/$/, "");
|
|
113
|
+
this.apiKey = options.apiKey;
|
|
114
|
+
this.batchSize = options.batchSize || 100;
|
|
115
|
+
this.flushInterval = options.flushInterval || 5e3;
|
|
116
|
+
this.maxBufferSize = options.maxBufferSize || 1e4;
|
|
117
|
+
this.maxRetries = options.maxRetries || 3;
|
|
118
|
+
this.retryDelayMs = options.retryDelayMs || 1e3;
|
|
119
|
+
this.enableMetrics = options.enableMetrics ?? true;
|
|
120
|
+
this.debugMode = options.debug ?? false;
|
|
121
|
+
this.globalMetadata = options.globalMetadata || {};
|
|
122
|
+
this.autoTraceId = options.autoTraceId ?? false;
|
|
123
|
+
this.circuitBreaker = new CircuitBreaker(
|
|
124
|
+
options.circuitBreakerThreshold || 5,
|
|
125
|
+
options.circuitBreakerResetMs || 3e4
|
|
126
|
+
);
|
|
127
|
+
this.startFlushTimer();
|
|
128
|
+
}
|
|
129
|
+
// ==================== Context Helpers ====================
|
|
130
|
+
/**
|
|
131
|
+
* Set trace ID for subsequent logs
|
|
132
|
+
* Automatically validates and normalizes to UUID v4
|
|
133
|
+
*/
|
|
134
|
+
setTraceId(traceId) {
|
|
135
|
+
this.currentTraceId = normalizeTraceId(traceId, this.debugMode) || null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get current trace ID
|
|
139
|
+
*/
|
|
140
|
+
getTraceId() {
|
|
141
|
+
return this.currentTraceId;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Execute function with a specific trace ID context
|
|
145
|
+
*/
|
|
146
|
+
withTraceId(traceId, fn) {
|
|
147
|
+
const previousTraceId = this.currentTraceId;
|
|
148
|
+
this.currentTraceId = traceId;
|
|
149
|
+
try {
|
|
150
|
+
return fn();
|
|
151
|
+
} finally {
|
|
152
|
+
this.currentTraceId = previousTraceId;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Execute function with a new auto-generated trace ID
|
|
157
|
+
*/
|
|
158
|
+
withNewTraceId(fn) {
|
|
159
|
+
return this.withTraceId(crypto.randomUUID(), fn);
|
|
160
|
+
}
|
|
161
|
+
// ==================== Logging Methods ====================
|
|
162
|
+
startFlushTimer() {
|
|
163
|
+
this.timer = setInterval(() => {
|
|
164
|
+
this.flush();
|
|
165
|
+
}, this.flushInterval);
|
|
166
|
+
}
|
|
167
|
+
log(entry) {
|
|
168
|
+
if (this.buffer.length >= this.maxBufferSize) {
|
|
169
|
+
this.metrics.logsDropped++;
|
|
170
|
+
if (this.debugMode) {
|
|
171
|
+
console.warn(`[LogTide] Buffer full, dropping log: ${entry.message}`);
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const normalizedTraceId = normalizeTraceId(entry.trace_id, this.debugMode) || normalizeTraceId(this.currentTraceId, this.debugMode) || (this.autoTraceId ? crypto.randomUUID() : void 0);
|
|
176
|
+
const internalEntry = {
|
|
177
|
+
...entry,
|
|
178
|
+
time: entry.time || (/* @__PURE__ */ new Date()).toISOString(),
|
|
179
|
+
metadata: {
|
|
180
|
+
...this.globalMetadata,
|
|
181
|
+
...entry.metadata
|
|
182
|
+
},
|
|
183
|
+
trace_id: normalizedTraceId
|
|
184
|
+
};
|
|
185
|
+
this.buffer.push(internalEntry);
|
|
186
|
+
if (this.buffer.length >= this.batchSize) {
|
|
187
|
+
this.flush();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
debug(service, message, metadata) {
|
|
191
|
+
this.log({ service, level: "debug", message, metadata });
|
|
192
|
+
}
|
|
193
|
+
info(service, message, metadata) {
|
|
194
|
+
this.log({ service, level: "info", message, metadata });
|
|
195
|
+
}
|
|
196
|
+
warn(service, message, metadata) {
|
|
197
|
+
this.log({ service, level: "warn", message, metadata });
|
|
198
|
+
}
|
|
199
|
+
error(service, message, metadataOrError) {
|
|
200
|
+
let metadata = {};
|
|
201
|
+
if (metadataOrError instanceof Error) {
|
|
202
|
+
metadata = { error: serializeError(metadataOrError) };
|
|
203
|
+
} else if (metadataOrError) {
|
|
204
|
+
metadata = metadataOrError;
|
|
205
|
+
}
|
|
206
|
+
this.log({ service, level: "error", message, metadata });
|
|
207
|
+
}
|
|
208
|
+
critical(service, message, metadataOrError) {
|
|
209
|
+
let metadata = {};
|
|
210
|
+
if (metadataOrError instanceof Error) {
|
|
211
|
+
metadata = { error: serializeError(metadataOrError) };
|
|
212
|
+
} else if (metadataOrError) {
|
|
213
|
+
metadata = metadataOrError;
|
|
214
|
+
}
|
|
215
|
+
this.log({ service, level: "critical", message, metadata });
|
|
216
|
+
}
|
|
217
|
+
// ==================== Flush with Retry & Circuit Breaker ====================
|
|
218
|
+
async flush() {
|
|
219
|
+
if (this.buffer.length === 0) return;
|
|
220
|
+
if (!this.circuitBreaker.canAttempt()) {
|
|
221
|
+
this.metrics.circuitBreakerTrips++;
|
|
222
|
+
if (this.debugMode) {
|
|
223
|
+
console.warn("[LogTide] Circuit breaker OPEN, skipping flush");
|
|
224
|
+
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const logs = [...this.buffer];
|
|
228
|
+
this.buffer = [];
|
|
229
|
+
const startTime = Date.now();
|
|
230
|
+
let lastError = null;
|
|
231
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
232
|
+
try {
|
|
233
|
+
const response = await fetch(`${this.apiUrl}/api/v1/ingest`, {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: {
|
|
236
|
+
"Content-Type": "application/json",
|
|
237
|
+
"X-API-Key": this.apiKey
|
|
238
|
+
},
|
|
239
|
+
body: JSON.stringify({ logs })
|
|
240
|
+
});
|
|
241
|
+
if (!response.ok) {
|
|
242
|
+
const errorText = await response.text();
|
|
243
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
244
|
+
}
|
|
245
|
+
this.circuitBreaker.recordSuccess();
|
|
246
|
+
this.metrics.logsSent += logs.length;
|
|
247
|
+
if (this.enableMetrics) {
|
|
248
|
+
const latency = Date.now() - startTime;
|
|
249
|
+
this.latencies.push(latency);
|
|
250
|
+
if (this.latencies.length > 100) {
|
|
251
|
+
this.latencies.shift();
|
|
252
|
+
}
|
|
253
|
+
this.metrics.avgLatencyMs = this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length;
|
|
254
|
+
}
|
|
255
|
+
if (this.debugMode) {
|
|
256
|
+
console.log(`[LogTide] Sent ${logs.length} logs successfully`);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
261
|
+
this.metrics.errors++;
|
|
262
|
+
if (attempt < this.maxRetries) {
|
|
263
|
+
this.metrics.retries++;
|
|
264
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
265
|
+
if (this.debugMode) {
|
|
266
|
+
console.warn(
|
|
267
|
+
`[LogTide] Retry ${attempt + 1}/${this.maxRetries} after ${delay}ms: ${lastError.message}`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
await this.sleep(delay);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
this.circuitBreaker.recordFailure();
|
|
275
|
+
if (this.debugMode) {
|
|
276
|
+
console.error(`[LogTide] Failed to send logs after ${this.maxRetries} retries:`, lastError);
|
|
277
|
+
}
|
|
278
|
+
if (this.buffer.length + logs.length <= this.maxBufferSize) {
|
|
279
|
+
this.buffer.unshift(...logs);
|
|
280
|
+
} else {
|
|
281
|
+
this.metrics.logsDropped += logs.length;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
sleep(ms) {
|
|
285
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
286
|
+
}
|
|
287
|
+
// ==================== Query Methods ====================
|
|
288
|
+
async query(options = {}) {
|
|
289
|
+
const params = new URLSearchParams();
|
|
290
|
+
if (options.service) params.append("service", options.service);
|
|
291
|
+
if (options.level) params.append("level", options.level);
|
|
292
|
+
if (options.from) {
|
|
293
|
+
const from = options.from instanceof Date ? options.from.toISOString() : options.from;
|
|
294
|
+
params.append("from", from);
|
|
295
|
+
}
|
|
296
|
+
if (options.to) {
|
|
297
|
+
const to = options.to instanceof Date ? options.to.toISOString() : options.to;
|
|
298
|
+
params.append("to", to);
|
|
299
|
+
}
|
|
300
|
+
if (options.q) params.append("q", options.q);
|
|
301
|
+
if (options.limit) params.append("limit", String(options.limit));
|
|
302
|
+
if (options.offset) params.append("offset", String(options.offset));
|
|
303
|
+
const url = `${this.apiUrl}/api/v1/logs?${params.toString()}`;
|
|
304
|
+
const response = await fetch(url, {
|
|
305
|
+
headers: {
|
|
306
|
+
"X-API-Key": this.apiKey
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
if (!response.ok) {
|
|
310
|
+
const errorText = await response.text();
|
|
311
|
+
throw new Error(`Query failed: HTTP ${response.status}: ${errorText}`);
|
|
312
|
+
}
|
|
313
|
+
return await response.json();
|
|
314
|
+
}
|
|
315
|
+
async getByTraceId(traceId) {
|
|
316
|
+
const url = `${this.apiUrl}/api/v1/logs/trace/${traceId}`;
|
|
317
|
+
const response = await fetch(url, {
|
|
318
|
+
headers: {
|
|
319
|
+
"X-API-Key": this.apiKey
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
const errorText = await response.text();
|
|
324
|
+
throw new Error(`Get by trace ID failed: HTTP ${response.status}: ${errorText}`);
|
|
325
|
+
}
|
|
326
|
+
const data = await response.json();
|
|
327
|
+
return data.logs || [];
|
|
328
|
+
}
|
|
329
|
+
async getAggregatedStats(options) {
|
|
330
|
+
const params = new URLSearchParams();
|
|
331
|
+
const from = options.from instanceof Date ? options.from.toISOString() : options.from;
|
|
332
|
+
const to = options.to instanceof Date ? options.to.toISOString() : options.to;
|
|
333
|
+
params.append("from", from);
|
|
334
|
+
params.append("to", to);
|
|
335
|
+
if (options.interval) params.append("interval", options.interval);
|
|
336
|
+
if (options.service) params.append("service", options.service);
|
|
337
|
+
const url = `${this.apiUrl}/api/v1/logs/aggregated?${params.toString()}`;
|
|
338
|
+
const response = await fetch(url, {
|
|
339
|
+
headers: {
|
|
340
|
+
"X-API-Key": this.apiKey
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
if (!response.ok) {
|
|
344
|
+
const errorText = await response.text();
|
|
345
|
+
throw new Error(`Get aggregated stats failed: HTTP ${response.status}: ${errorText}`);
|
|
346
|
+
}
|
|
347
|
+
return await response.json();
|
|
348
|
+
}
|
|
349
|
+
// ==================== Live Tail (SSE) ====================
|
|
350
|
+
stream(options) {
|
|
351
|
+
const params = new URLSearchParams();
|
|
352
|
+
params.append("token", this.apiKey);
|
|
353
|
+
if (options.service) params.append("service", options.service);
|
|
354
|
+
if (options.level) params.append("level", options.level);
|
|
355
|
+
const url = `${this.apiUrl}/api/v1/logs/stream?${params.toString()}`;
|
|
356
|
+
const eventSource = new EventSource(url);
|
|
357
|
+
eventSource.addEventListener("log", (event) => {
|
|
358
|
+
try {
|
|
359
|
+
const messageEvent = event;
|
|
360
|
+
const log = JSON.parse(messageEvent.data);
|
|
361
|
+
options.onLog(log);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
364
|
+
options.onError?.(err);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
eventSource.addEventListener("error", () => {
|
|
368
|
+
const error = new Error("SSE connection error");
|
|
369
|
+
options.onError?.(error);
|
|
370
|
+
});
|
|
371
|
+
return () => {
|
|
372
|
+
eventSource.close();
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
// ==================== Metrics ====================
|
|
376
|
+
getMetrics() {
|
|
377
|
+
return { ...this.metrics };
|
|
378
|
+
}
|
|
379
|
+
resetMetrics() {
|
|
380
|
+
this.metrics = {
|
|
381
|
+
logsSent: 0,
|
|
382
|
+
logsDropped: 0,
|
|
383
|
+
errors: 0,
|
|
384
|
+
retries: 0,
|
|
385
|
+
avgLatencyMs: 0,
|
|
386
|
+
circuitBreakerTrips: 0
|
|
387
|
+
};
|
|
388
|
+
this.latencies = [];
|
|
389
|
+
}
|
|
390
|
+
getCircuitBreakerState() {
|
|
391
|
+
return this.circuitBreaker.getState();
|
|
392
|
+
}
|
|
393
|
+
// ==================== Cleanup ====================
|
|
394
|
+
async close() {
|
|
395
|
+
if (this.timer) {
|
|
396
|
+
clearInterval(this.timer);
|
|
397
|
+
this.timer = null;
|
|
398
|
+
}
|
|
399
|
+
await this.flush();
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
var index_default = LogTideClient;
|
|
403
|
+
|
|
404
|
+
exports.LogTideClient = LogTideClient;
|
|
405
|
+
exports.default = index_default;
|
|
406
|
+
exports.serializeError = serializeError;
|
|
407
|
+
//# sourceMappingURL=index.cjs.map
|
|
408
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["randomUUID"],"mappings":"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","file":"index.cjs","sourcesContent":["import { randomUUID } from 'crypto';\n\n// ==================== Types ====================\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'critical';\n\nexport interface LogTideClientOptions {\n apiUrl: string;\n apiKey: string;\n batchSize?: number;\n flushInterval?: number;\n maxBufferSize?: number;\n maxRetries?: number;\n retryDelayMs?: number;\n circuitBreakerThreshold?: number;\n circuitBreakerResetMs?: number;\n enableMetrics?: boolean;\n debug?: boolean;\n globalMetadata?: Record<string, unknown>;\n autoTraceId?: boolean;\n}\n\nexport interface LogEntry {\n service: string;\n level: LogLevel;\n message: string;\n time?: string;\n metadata?: Record<string, unknown>;\n trace_id?: string;\n}\n\nexport interface InternalLogEntry extends LogEntry {\n time: string;\n}\n\nexport interface QueryOptions {\n service?: string;\n level?: LogLevel;\n from?: Date | string;\n to?: Date | string;\n q?: string;\n limit?: number;\n offset?: number;\n}\n\nexport interface LogsResponse {\n logs: InternalLogEntry[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport interface AggregatedStatsOptions {\n from: Date | string;\n to: Date | string;\n interval?: '1m' | '5m' | '1h' | '1d';\n service?: string;\n}\n\nexport interface AggregatedStatsResponse {\n timeseries: Array<{\n bucket: string;\n total: number;\n by_level: Record<string, number>;\n }>;\n top_services: Array<{ service: string; count: number }>;\n top_errors: Array<{ message: string; count: number }>;\n}\n\nexport interface ClientMetrics {\n logsSent: number;\n logsDropped: number;\n errors: number;\n retries: number;\n avgLatencyMs: number;\n circuitBreakerTrips: number;\n}\n\nexport interface StreamOptions {\n service?: string;\n level?: LogLevel;\n onLog: (log: InternalLogEntry) => void;\n onError?: (error: Error) => void;\n}\n\n// ==================== Circuit Breaker ====================\n\nenum CircuitState {\n CLOSED = 'CLOSED',\n OPEN = 'OPEN',\n HALF_OPEN = 'HALF_OPEN',\n}\n\nclass CircuitBreaker {\n private state: CircuitState = CircuitState.CLOSED;\n private failureCount = 0;\n private lastFailureTime: number | null = null;\n\n constructor(\n private threshold: number,\n private resetMs: number,\n ) {}\n\n recordSuccess() {\n this.failureCount = 0;\n this.state = CircuitState.CLOSED;\n }\n\n recordFailure() {\n this.failureCount++;\n this.lastFailureTime = Date.now();\n\n if (this.failureCount >= this.threshold) {\n this.state = CircuitState.OPEN;\n }\n }\n\n canAttempt(): boolean {\n if (this.state === CircuitState.CLOSED) {\n return true;\n }\n\n if (this.state === CircuitState.OPEN) {\n const now = Date.now();\n if (this.lastFailureTime && now - this.lastFailureTime >= this.resetMs) {\n this.state = CircuitState.HALF_OPEN;\n return true;\n }\n return false;\n }\n\n // HALF_OPEN state - allow one attempt\n return true;\n }\n\n getState(): CircuitState {\n return this.state;\n }\n}\n\n// ==================== UUID Validation ====================\n\n/**\n * Validates if a string is a valid UUID (v4)\n */\nfunction isValidUUID(str: string): boolean {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return uuidRegex.test(str);\n}\n\n/**\n * Normalizes a trace_id to ensure it's a valid UUID.\n * If invalid, generates a new UUID and warns in debug mode.\n */\nfunction normalizeTraceId(traceId: string | null | undefined, debug: boolean): string | undefined {\n if (!traceId) {\n return undefined;\n }\n\n if (isValidUUID(traceId)) {\n return traceId;\n }\n\n // Invalid UUID - generate a new one\n const newTraceId = randomUUID();\n if (debug) {\n console.warn(\n `[LogTide] Invalid trace_id \"${traceId}\" (must be UUID v4). Generated new UUID: ${newTraceId}`,\n );\n }\n return newTraceId;\n}\n\n// ==================== Error Serialization ====================\n\nexport function serializeError(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n const result: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n stack: error.stack,\n };\n\n if (error.cause) {\n result.cause = serializeError(error.cause);\n }\n\n return result;\n }\n\n if (typeof error === 'string') {\n return { message: error };\n }\n\n if (typeof error === 'object' && error !== null) {\n return error as Record<string, unknown>;\n }\n\n return { message: String(error) };\n}\n\n// ==================== Main Client ====================\n\nexport class LogTideClient {\n private apiUrl: string;\n private apiKey: string;\n private batchSize: number;\n private flushInterval: number;\n private maxBufferSize: number;\n private maxRetries: number;\n private retryDelayMs: number;\n private enableMetrics: boolean;\n private debugMode: boolean;\n private globalMetadata: Record<string, unknown>;\n private autoTraceId: boolean;\n\n private buffer: InternalLogEntry[] = [];\n private timer: NodeJS.Timeout | null = null;\n private circuitBreaker: CircuitBreaker;\n\n // Metrics\n private metrics: ClientMetrics = {\n logsSent: 0,\n logsDropped: 0,\n errors: 0,\n retries: 0,\n avgLatencyMs: 0,\n circuitBreakerTrips: 0,\n };\n private latencies: number[] = [];\n\n // Context tracking\n private currentTraceId: string | null = null;\n\n constructor(options: LogTideClientOptions) {\n this.apiUrl = options.apiUrl.replace(/\\/$/, '');\n this.apiKey = options.apiKey;\n this.batchSize = options.batchSize || 100;\n this.flushInterval = options.flushInterval || 5000;\n this.maxBufferSize = options.maxBufferSize || 10000;\n this.maxRetries = options.maxRetries || 3;\n this.retryDelayMs = options.retryDelayMs || 1000;\n this.enableMetrics = options.enableMetrics ?? true;\n this.debugMode = options.debug ?? false;\n this.globalMetadata = options.globalMetadata || {};\n this.autoTraceId = options.autoTraceId ?? false;\n\n this.circuitBreaker = new CircuitBreaker(\n options.circuitBreakerThreshold || 5,\n options.circuitBreakerResetMs || 30000,\n );\n\n this.startFlushTimer();\n }\n\n // ==================== Context Helpers ====================\n\n /**\n * Set trace ID for subsequent logs\n * Automatically validates and normalizes to UUID v4\n */\n setTraceId(traceId: string | null) {\n this.currentTraceId = normalizeTraceId(traceId, this.debugMode) || null;\n }\n\n /**\n * Get current trace ID\n */\n getTraceId(): string | null {\n return this.currentTraceId;\n }\n\n /**\n * Execute function with a specific trace ID context\n */\n withTraceId<T>(traceId: string, fn: () => T): T {\n const previousTraceId = this.currentTraceId;\n this.currentTraceId = traceId;\n try {\n return fn();\n } finally {\n this.currentTraceId = previousTraceId;\n }\n }\n\n /**\n * Execute function with a new auto-generated trace ID\n */\n withNewTraceId<T>(fn: () => T): T {\n return this.withTraceId(randomUUID(), fn);\n }\n\n // ==================== Logging Methods ====================\n\n private startFlushTimer() {\n this.timer = setInterval(() => {\n this.flush();\n }, this.flushInterval);\n }\n\n log(entry: LogEntry) {\n // Check buffer size limit\n if (this.buffer.length >= this.maxBufferSize) {\n this.metrics.logsDropped++;\n if (this.debugMode) {\n console.warn(`[LogTide] Buffer full, dropping log: ${entry.message}`);\n }\n return;\n }\n\n // Normalize trace_id to ensure it's a valid UUID\n const normalizedTraceId =\n normalizeTraceId(entry.trace_id, this.debugMode) ||\n normalizeTraceId(this.currentTraceId, this.debugMode) ||\n (this.autoTraceId ? randomUUID() : undefined);\n\n const internalEntry: InternalLogEntry = {\n ...entry,\n time: entry.time || new Date().toISOString(),\n metadata: {\n ...this.globalMetadata,\n ...entry.metadata,\n },\n trace_id: normalizedTraceId,\n };\n\n this.buffer.push(internalEntry);\n\n if (this.buffer.length >= this.batchSize) {\n this.flush();\n }\n }\n\n debug(service: string, message: string, metadata?: Record<string, unknown>) {\n this.log({ service, level: 'debug', message, metadata });\n }\n\n info(service: string, message: string, metadata?: Record<string, unknown>) {\n this.log({ service, level: 'info', message, metadata });\n }\n\n warn(service: string, message: string, metadata?: Record<string, unknown>) {\n this.log({ service, level: 'warn', message, metadata });\n }\n\n error(service: string, message: string, metadataOrError?: Record<string, unknown> | Error) {\n let metadata: Record<string, unknown> = {};\n\n if (metadataOrError instanceof Error) {\n metadata = { error: serializeError(metadataOrError) };\n } else if (metadataOrError) {\n metadata = metadataOrError;\n }\n\n this.log({ service, level: 'error', message, metadata });\n }\n\n critical(service: string, message: string, metadataOrError?: Record<string, unknown> | Error) {\n let metadata: Record<string, unknown> = {};\n\n if (metadataOrError instanceof Error) {\n metadata = { error: serializeError(metadataOrError) };\n } else if (metadataOrError) {\n metadata = metadataOrError;\n }\n\n this.log({ service, level: 'critical', message, metadata });\n }\n\n // ==================== Flush with Retry & Circuit Breaker ====================\n\n async flush() {\n if (this.buffer.length === 0) return;\n\n // Check circuit breaker\n if (!this.circuitBreaker.canAttempt()) {\n this.metrics.circuitBreakerTrips++;\n if (this.debugMode) {\n console.warn('[LogTide] Circuit breaker OPEN, skipping flush');\n }\n return;\n }\n\n const logs = [...this.buffer];\n this.buffer = [];\n\n const startTime = Date.now();\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const response = await fetch(`${this.apiUrl}/api/v1/ingest`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ logs }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`HTTP ${response.status}: ${errorText}`);\n }\n\n // Success\n this.circuitBreaker.recordSuccess();\n this.metrics.logsSent += logs.length;\n\n if (this.enableMetrics) {\n const latency = Date.now() - startTime;\n this.latencies.push(latency);\n if (this.latencies.length > 100) {\n this.latencies.shift();\n }\n this.metrics.avgLatencyMs =\n this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length;\n }\n\n if (this.debugMode) {\n console.log(`[LogTide] Sent ${logs.length} logs successfully`);\n }\n\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.metrics.errors++;\n\n if (attempt < this.maxRetries) {\n this.metrics.retries++;\n const delay = this.retryDelayMs * Math.pow(2, attempt);\n if (this.debugMode) {\n console.warn(\n `[LogTide] Retry ${attempt + 1}/${this.maxRetries} after ${delay}ms: ${lastError.message}`,\n );\n }\n await this.sleep(delay);\n }\n }\n }\n\n // All retries failed\n this.circuitBreaker.recordFailure();\n\n if (this.debugMode) {\n console.error(`[LogTide] Failed to send logs after ${this.maxRetries} retries:`, lastError);\n }\n\n // Re-add logs to buffer if not full\n if (this.buffer.length + logs.length <= this.maxBufferSize) {\n this.buffer.unshift(...logs);\n } else {\n this.metrics.logsDropped += logs.length;\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ==================== Query Methods ====================\n\n async query(options: QueryOptions = {}): Promise<LogsResponse> {\n const params = new URLSearchParams();\n\n if (options.service) params.append('service', options.service);\n if (options.level) params.append('level', options.level);\n if (options.from) {\n const from = options.from instanceof Date ? options.from.toISOString() : options.from;\n params.append('from', from);\n }\n if (options.to) {\n const to = options.to instanceof Date ? options.to.toISOString() : options.to;\n params.append('to', to);\n }\n if (options.q) params.append('q', options.q);\n if (options.limit) params.append('limit', String(options.limit));\n if (options.offset) params.append('offset', String(options.offset));\n\n const url = `${this.apiUrl}/api/v1/logs?${params.toString()}`;\n\n const response = await fetch(url, {\n headers: {\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Query failed: HTTP ${response.status}: ${errorText}`);\n }\n\n return (await response.json()) as LogsResponse;\n }\n\n async getByTraceId(traceId: string): Promise<InternalLogEntry[]> {\n const url = `${this.apiUrl}/api/v1/logs/trace/${traceId}`;\n\n const response = await fetch(url, {\n headers: {\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Get by trace ID failed: HTTP ${response.status}: ${errorText}`);\n }\n\n const data = (await response.json()) as { logs?: InternalLogEntry[] };\n return data.logs || [];\n }\n\n async getAggregatedStats(options: AggregatedStatsOptions): Promise<AggregatedStatsResponse> {\n const params = new URLSearchParams();\n\n const from = options.from instanceof Date ? options.from.toISOString() : options.from;\n const to = options.to instanceof Date ? options.to.toISOString() : options.to;\n\n params.append('from', from);\n params.append('to', to);\n if (options.interval) params.append('interval', options.interval);\n if (options.service) params.append('service', options.service);\n\n const url = `${this.apiUrl}/api/v1/logs/aggregated?${params.toString()}`;\n\n const response = await fetch(url, {\n headers: {\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Get aggregated stats failed: HTTP ${response.status}: ${errorText}`);\n }\n\n return (await response.json()) as AggregatedStatsResponse;\n }\n\n // ==================== Live Tail (SSE) ====================\n\n stream(options: StreamOptions): () => void {\n const params = new URLSearchParams();\n params.append('token', this.apiKey);\n if (options.service) params.append('service', options.service);\n if (options.level) params.append('level', options.level);\n\n const url = `${this.apiUrl}/api/v1/logs/stream?${params.toString()}`;\n\n const eventSource = new EventSource(url);\n\n eventSource.addEventListener('log', (event: Event) => {\n try {\n const messageEvent = event as MessageEvent;\n const log = JSON.parse(messageEvent.data) as InternalLogEntry;\n options.onLog(log);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n options.onError?.(err);\n }\n });\n\n eventSource.addEventListener('error', () => {\n const error = new Error('SSE connection error');\n options.onError?.(error);\n });\n\n // Return cleanup function\n return () => {\n eventSource.close();\n };\n }\n\n // ==================== Metrics ====================\n\n getMetrics(): ClientMetrics {\n return { ...this.metrics };\n }\n\n resetMetrics() {\n this.metrics = {\n logsSent: 0,\n logsDropped: 0,\n errors: 0,\n retries: 0,\n avgLatencyMs: 0,\n circuitBreakerTrips: 0,\n };\n this.latencies = [];\n }\n\n getCircuitBreakerState(): string {\n return this.circuitBreaker.getState();\n }\n\n // ==================== Cleanup ====================\n\n async close() {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n}\n\nexport default LogTideClient;\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'critical';
|
|
2
|
+
interface LogTideClientOptions {
|
|
3
|
+
apiUrl: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
batchSize?: number;
|
|
6
|
+
flushInterval?: number;
|
|
7
|
+
maxBufferSize?: number;
|
|
8
|
+
maxRetries?: number;
|
|
9
|
+
retryDelayMs?: number;
|
|
10
|
+
circuitBreakerThreshold?: number;
|
|
11
|
+
circuitBreakerResetMs?: number;
|
|
12
|
+
enableMetrics?: boolean;
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
globalMetadata?: Record<string, unknown>;
|
|
15
|
+
autoTraceId?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface LogEntry {
|
|
18
|
+
service: string;
|
|
19
|
+
level: LogLevel;
|
|
20
|
+
message: string;
|
|
21
|
+
time?: string;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
trace_id?: string;
|
|
24
|
+
}
|
|
25
|
+
interface InternalLogEntry extends LogEntry {
|
|
26
|
+
time: string;
|
|
27
|
+
}
|
|
28
|
+
interface QueryOptions {
|
|
29
|
+
service?: string;
|
|
30
|
+
level?: LogLevel;
|
|
31
|
+
from?: Date | string;
|
|
32
|
+
to?: Date | string;
|
|
33
|
+
q?: string;
|
|
34
|
+
limit?: number;
|
|
35
|
+
offset?: number;
|
|
36
|
+
}
|
|
37
|
+
interface LogsResponse {
|
|
38
|
+
logs: InternalLogEntry[];
|
|
39
|
+
total: number;
|
|
40
|
+
limit: number;
|
|
41
|
+
offset: number;
|
|
42
|
+
}
|
|
43
|
+
interface AggregatedStatsOptions {
|
|
44
|
+
from: Date | string;
|
|
45
|
+
to: Date | string;
|
|
46
|
+
interval?: '1m' | '5m' | '1h' | '1d';
|
|
47
|
+
service?: string;
|
|
48
|
+
}
|
|
49
|
+
interface AggregatedStatsResponse {
|
|
50
|
+
timeseries: Array<{
|
|
51
|
+
bucket: string;
|
|
52
|
+
total: number;
|
|
53
|
+
by_level: Record<string, number>;
|
|
54
|
+
}>;
|
|
55
|
+
top_services: Array<{
|
|
56
|
+
service: string;
|
|
57
|
+
count: number;
|
|
58
|
+
}>;
|
|
59
|
+
top_errors: Array<{
|
|
60
|
+
message: string;
|
|
61
|
+
count: number;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
interface ClientMetrics {
|
|
65
|
+
logsSent: number;
|
|
66
|
+
logsDropped: number;
|
|
67
|
+
errors: number;
|
|
68
|
+
retries: number;
|
|
69
|
+
avgLatencyMs: number;
|
|
70
|
+
circuitBreakerTrips: number;
|
|
71
|
+
}
|
|
72
|
+
interface StreamOptions {
|
|
73
|
+
service?: string;
|
|
74
|
+
level?: LogLevel;
|
|
75
|
+
onLog: (log: InternalLogEntry) => void;
|
|
76
|
+
onError?: (error: Error) => void;
|
|
77
|
+
}
|
|
78
|
+
declare function serializeError(error: unknown): Record<string, unknown>;
|
|
79
|
+
declare class LogTideClient {
|
|
80
|
+
private apiUrl;
|
|
81
|
+
private apiKey;
|
|
82
|
+
private batchSize;
|
|
83
|
+
private flushInterval;
|
|
84
|
+
private maxBufferSize;
|
|
85
|
+
private maxRetries;
|
|
86
|
+
private retryDelayMs;
|
|
87
|
+
private enableMetrics;
|
|
88
|
+
private debugMode;
|
|
89
|
+
private globalMetadata;
|
|
90
|
+
private autoTraceId;
|
|
91
|
+
private buffer;
|
|
92
|
+
private timer;
|
|
93
|
+
private circuitBreaker;
|
|
94
|
+
private metrics;
|
|
95
|
+
private latencies;
|
|
96
|
+
private currentTraceId;
|
|
97
|
+
constructor(options: LogTideClientOptions);
|
|
98
|
+
/**
|
|
99
|
+
* Set trace ID for subsequent logs
|
|
100
|
+
* Automatically validates and normalizes to UUID v4
|
|
101
|
+
*/
|
|
102
|
+
setTraceId(traceId: string | null): void;
|
|
103
|
+
/**
|
|
104
|
+
* Get current trace ID
|
|
105
|
+
*/
|
|
106
|
+
getTraceId(): string | null;
|
|
107
|
+
/**
|
|
108
|
+
* Execute function with a specific trace ID context
|
|
109
|
+
*/
|
|
110
|
+
withTraceId<T>(traceId: string, fn: () => T): T;
|
|
111
|
+
/**
|
|
112
|
+
* Execute function with a new auto-generated trace ID
|
|
113
|
+
*/
|
|
114
|
+
withNewTraceId<T>(fn: () => T): T;
|
|
115
|
+
private startFlushTimer;
|
|
116
|
+
log(entry: LogEntry): void;
|
|
117
|
+
debug(service: string, message: string, metadata?: Record<string, unknown>): void;
|
|
118
|
+
info(service: string, message: string, metadata?: Record<string, unknown>): void;
|
|
119
|
+
warn(service: string, message: string, metadata?: Record<string, unknown>): void;
|
|
120
|
+
error(service: string, message: string, metadataOrError?: Record<string, unknown> | Error): void;
|
|
121
|
+
critical(service: string, message: string, metadataOrError?: Record<string, unknown> | Error): void;
|
|
122
|
+
flush(): Promise<void>;
|
|
123
|
+
private sleep;
|
|
124
|
+
query(options?: QueryOptions): Promise<LogsResponse>;
|
|
125
|
+
getByTraceId(traceId: string): Promise<InternalLogEntry[]>;
|
|
126
|
+
getAggregatedStats(options: AggregatedStatsOptions): Promise<AggregatedStatsResponse>;
|
|
127
|
+
stream(options: StreamOptions): () => void;
|
|
128
|
+
getMetrics(): ClientMetrics;
|
|
129
|
+
resetMetrics(): void;
|
|
130
|
+
getCircuitBreakerState(): string;
|
|
131
|
+
close(): Promise<void>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { type AggregatedStatsOptions, type AggregatedStatsResponse, type ClientMetrics, type InternalLogEntry, type LogEntry, type LogLevel, LogTideClient, type LogTideClientOptions, type LogsResponse, type QueryOptions, type StreamOptions, LogTideClient as default, serializeError };
|