@logtide/sdk-node 0.6.1 → 0.7.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 -21
- package/README.md +493 -493
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.cjs +2 -2
- package/dist/middleware/index.cjs.map +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/index.js.map +1 -1
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -1,493 +1,493 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/logtide-dev/logtide/main/docs/images/logo.png" alt="LogTide Logo" width="400">
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
<h1 align="center">LogTide Node.js SDK</h1>
|
|
6
|
-
|
|
7
|
-
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@logtide/sdk-node"><img src="https://img.shields.io/npm/v/@logtide/sdk-node?color=blue" alt="npm"></a>
|
|
9
|
-
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
|
|
10
|
-
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-20+-green.svg" alt="Node.js"></a>
|
|
11
|
-
<a href="https://github.com/logtide-dev/logtide-javascript/releases"><img src="https://img.shields.io/github/v/release/logtide-dev/logtide-javascript" alt="Release"></a>
|
|
12
|
-
</p>
|
|
13
|
-
|
|
14
|
-
<p align="center">
|
|
15
|
-
Official Node.js SDK for <a href="https://logtide.dev">LogTide</a> with automatic batching, retry logic, circuit breaker, query API, live streaming, and middleware support.
|
|
16
|
-
</p>
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Features
|
|
21
|
-
|
|
22
|
-
- **Automatic batching** with configurable size and interval
|
|
23
|
-
- **Retry logic** with exponential backoff
|
|
24
|
-
- **Circuit breaker** pattern for fault tolerance
|
|
25
|
-
- **Max buffer size** with drop policy to prevent memory leaks
|
|
26
|
-
- **Query API** for searching and filtering logs
|
|
27
|
-
- **Live tail** with Server-Sent Events (SSE)
|
|
28
|
-
- **Trace ID context** for distributed tracing
|
|
29
|
-
- **Global metadata** added to all logs
|
|
30
|
-
- **Structured error serialization**
|
|
31
|
-
- **Internal metrics** (logs sent, errors, latency, etc.)
|
|
32
|
-
- **Express & Fastify middleware** for auto-logging HTTP requests
|
|
33
|
-
- **Full TypeScript support** with strict types
|
|
34
|
-
|
|
35
|
-
## Installation
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npm install @logtide/sdk-node
|
|
39
|
-
# or
|
|
40
|
-
pnpm add @logtide/sdk-node
|
|
41
|
-
# or
|
|
42
|
-
yarn add @logtide/sdk-node
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Quick Start
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
import { LogTideClient } from '@logtide/sdk-node';
|
|
49
|
-
|
|
50
|
-
const client = new LogTideClient({
|
|
51
|
-
apiUrl: 'http://localhost:8080',
|
|
52
|
-
apiKey: 'lp_your_api_key_here',
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Send logs
|
|
56
|
-
client.info('api-gateway', 'Server started', { port: 3000 });
|
|
57
|
-
client.error('database', 'Connection failed', new Error('Timeout'));
|
|
58
|
-
|
|
59
|
-
// Graceful shutdown
|
|
60
|
-
process.on('SIGINT', async () => {
|
|
61
|
-
await client.close();
|
|
62
|
-
process.exit(0);
|
|
63
|
-
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Configuration Options
|
|
69
|
-
|
|
70
|
-
### Basic Options
|
|
71
|
-
|
|
72
|
-
| Option | Type | Default | Description |
|
|
73
|
-
|--------|------|---------|-------------|
|
|
74
|
-
| `apiUrl` | `string` | **required** | Base URL of your LogTide instance |
|
|
75
|
-
| `apiKey` | `string` | **required** | Project API key (starts with `lp_`) |
|
|
76
|
-
| `batchSize` | `number` | `100` | Number of logs to batch before sending |
|
|
77
|
-
| `flushInterval` | `number` | `5000` | Interval in ms to auto-flush logs |
|
|
78
|
-
|
|
79
|
-
### Advanced Options
|
|
80
|
-
|
|
81
|
-
| Option | Type | Default | Description |
|
|
82
|
-
|--------|------|---------|-------------|
|
|
83
|
-
| `maxBufferSize` | `number` | `10000` | Max logs in buffer (prevents memory leak) |
|
|
84
|
-
| `maxRetries` | `number` | `3` | Max retry attempts on failure |
|
|
85
|
-
| `retryDelayMs` | `number` | `1000` | Initial retry delay (exponential backoff) |
|
|
86
|
-
| `circuitBreakerThreshold` | `number` | `5` | Failures before opening circuit |
|
|
87
|
-
| `circuitBreakerResetMs` | `number` | `30000` | Time before retrying after circuit opens |
|
|
88
|
-
| `enableMetrics` | `boolean` | `true` | Track internal metrics |
|
|
89
|
-
| `debug` | `boolean` | `false` | Enable debug logging to console |
|
|
90
|
-
| `globalMetadata` | `object` | `{}` | Metadata added to all logs |
|
|
91
|
-
| `autoTraceId` | `boolean` | `false` | Auto-generate trace IDs for logs |
|
|
92
|
-
|
|
93
|
-
### Example: Full Configuration
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const client = new LogTideClient({
|
|
97
|
-
apiUrl: 'http://localhost:8080',
|
|
98
|
-
apiKey: 'lp_your_api_key_here',
|
|
99
|
-
|
|
100
|
-
// Batching
|
|
101
|
-
batchSize: 100,
|
|
102
|
-
flushInterval: 5000,
|
|
103
|
-
|
|
104
|
-
// Buffer management
|
|
105
|
-
maxBufferSize: 10000,
|
|
106
|
-
|
|
107
|
-
// Retry with exponential backoff (1s -> 2s -> 4s)
|
|
108
|
-
maxRetries: 3,
|
|
109
|
-
retryDelayMs: 1000,
|
|
110
|
-
|
|
111
|
-
// Circuit breaker
|
|
112
|
-
circuitBreakerThreshold: 5,
|
|
113
|
-
circuitBreakerResetMs: 30000,
|
|
114
|
-
|
|
115
|
-
// Metrics & debugging
|
|
116
|
-
enableMetrics: true,
|
|
117
|
-
debug: true,
|
|
118
|
-
|
|
119
|
-
// Global context
|
|
120
|
-
globalMetadata: {
|
|
121
|
-
env: process.env.NODE_ENV,
|
|
122
|
-
version: '1.0.0',
|
|
123
|
-
hostname: process.env.HOSTNAME,
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
// Auto trace IDs
|
|
127
|
-
autoTraceId: false,
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## Logging Methods
|
|
134
|
-
|
|
135
|
-
### Basic Logging
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
client.debug('service-name', 'Debug message');
|
|
139
|
-
client.info('service-name', 'Info message', { userId: 123 });
|
|
140
|
-
client.warn('service-name', 'Warning message');
|
|
141
|
-
client.error('service-name', 'Error message', { custom: 'data' });
|
|
142
|
-
client.critical('service-name', 'Critical message');
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Error Logging with Auto-Serialization
|
|
146
|
-
|
|
147
|
-
The SDK automatically serializes `Error` objects:
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
try {
|
|
151
|
-
throw new Error('Database timeout');
|
|
152
|
-
} catch (error) {
|
|
153
|
-
// Automatically serializes error with stack trace
|
|
154
|
-
client.error('database', 'Query failed', error);
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Generated log metadata:
|
|
159
|
-
```json
|
|
160
|
-
{
|
|
161
|
-
"error": {
|
|
162
|
-
"name": "Error",
|
|
163
|
-
"message": "Database timeout",
|
|
164
|
-
"stack": "Error: Database timeout\n at ..."
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Custom Log Entry
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
client.log({
|
|
173
|
-
service: 'custom-service',
|
|
174
|
-
level: 'info',
|
|
175
|
-
message: 'Custom log',
|
|
176
|
-
time: new Date().toISOString(),
|
|
177
|
-
metadata: { key: 'value' },
|
|
178
|
-
trace_id: 'custom-trace-id',
|
|
179
|
-
});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## Trace ID Context
|
|
185
|
-
|
|
186
|
-
Track requests across services with trace IDs.
|
|
187
|
-
|
|
188
|
-
### Manual Trace ID
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
client.setTraceId('request-123');
|
|
192
|
-
|
|
193
|
-
client.info('api', 'Request received');
|
|
194
|
-
client.info('database', 'Querying users');
|
|
195
|
-
client.info('api', 'Response sent');
|
|
196
|
-
|
|
197
|
-
client.setTraceId(null); // Clear context
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Scoped Trace ID
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
client.withTraceId('request-456', () => {
|
|
204
|
-
client.info('api', 'Processing in context');
|
|
205
|
-
client.warn('cache', 'Cache miss');
|
|
206
|
-
});
|
|
207
|
-
// Trace ID automatically restored after block
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Auto-Generated Trace ID
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
client.withNewTraceId(() => {
|
|
214
|
-
client.info('worker', 'Background job started');
|
|
215
|
-
client.info('worker', 'Job completed');
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## Query API
|
|
222
|
-
|
|
223
|
-
Search and retrieve logs programmatically.
|
|
224
|
-
|
|
225
|
-
### Basic Query
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
const result = await client.query({
|
|
229
|
-
service: 'api-gateway',
|
|
230
|
-
level: 'error',
|
|
231
|
-
from: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24h
|
|
232
|
-
to: new Date(),
|
|
233
|
-
limit: 100,
|
|
234
|
-
offset: 0,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
console.log(`Found ${result.total} logs`);
|
|
238
|
-
console.log(result.logs);
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Full-Text Search
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
const result = await client.query({
|
|
245
|
-
q: 'timeout',
|
|
246
|
-
limit: 50,
|
|
247
|
-
});
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Get Logs by Trace ID
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
const logs = await client.getByTraceId('trace-123');
|
|
254
|
-
console.log(`Trace has ${logs.length} logs`);
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Aggregated Statistics
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
const stats = await client.getAggregatedStats({
|
|
261
|
-
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
|
|
262
|
-
to: new Date(),
|
|
263
|
-
interval: '1h', // '1m' | '5m' | '1h' | '1d'
|
|
264
|
-
service: 'api-gateway', // Optional
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
console.log(stats.timeseries); // Time-bucketed counts
|
|
268
|
-
console.log(stats.top_services); // Top services by log count
|
|
269
|
-
console.log(stats.top_errors); // Most common errors
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
---
|
|
273
|
-
|
|
274
|
-
## Live Tail (Streaming)
|
|
275
|
-
|
|
276
|
-
Stream logs in real-time using Server-Sent Events.
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
const cleanup = client.stream({
|
|
280
|
-
service: 'api-gateway', // Optional filter
|
|
281
|
-
level: 'error', // Optional filter
|
|
282
|
-
|
|
283
|
-
onLog: (log) => {
|
|
284
|
-
console.log(`[${log.time}] ${log.level}: ${log.message}`);
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
onError: (error) => {
|
|
288
|
-
console.error('Stream error:', error);
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Stop streaming when done
|
|
293
|
-
setTimeout(() => {
|
|
294
|
-
cleanup();
|
|
295
|
-
}, 60000);
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
## Metrics
|
|
301
|
-
|
|
302
|
-
Track SDK performance and health.
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
const metrics = client.getMetrics();
|
|
306
|
-
|
|
307
|
-
console.log(metrics.logsSent); // Total logs sent
|
|
308
|
-
console.log(metrics.logsDropped); // Logs dropped (buffer full)
|
|
309
|
-
console.log(metrics.errors); // Send errors
|
|
310
|
-
console.log(metrics.retries); // Retry attempts
|
|
311
|
-
console.log(metrics.avgLatencyMs); // Average send latency
|
|
312
|
-
console.log(metrics.circuitBreakerTrips); // Circuit breaker openings
|
|
313
|
-
|
|
314
|
-
// Get circuit breaker state
|
|
315
|
-
console.log(client.getCircuitBreakerState()); // 'CLOSED' | 'OPEN' | 'HALF_OPEN'
|
|
316
|
-
|
|
317
|
-
// Reset metrics
|
|
318
|
-
client.resetMetrics();
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
## Middleware Integration
|
|
324
|
-
|
|
325
|
-
LogTide provides ready-to-use middleware for popular frameworks.
|
|
326
|
-
|
|
327
|
-
### Express Middleware
|
|
328
|
-
|
|
329
|
-
Auto-log all HTTP requests and responses.
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
import express from 'express';
|
|
333
|
-
import { LogTideClient, logTideMiddleware } from '@logtide/sdk-node';
|
|
334
|
-
|
|
335
|
-
const app = express();
|
|
336
|
-
const logger = new LogTideClient({
|
|
337
|
-
apiUrl: 'http://localhost:8080',
|
|
338
|
-
apiKey: 'lp_your_api_key_here',
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
app.use(
|
|
342
|
-
logTideMiddleware({
|
|
343
|
-
client: logger,
|
|
344
|
-
serviceName: 'express-api',
|
|
345
|
-
logRequests: true,
|
|
346
|
-
logResponses: true,
|
|
347
|
-
logErrors: true,
|
|
348
|
-
includeHeaders: false,
|
|
349
|
-
includeBody: false,
|
|
350
|
-
skipHealthCheck: true,
|
|
351
|
-
skipPaths: ['/metrics'],
|
|
352
|
-
}),
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
app.get('/', (req, res) => {
|
|
356
|
-
res.json({ message: 'Hello' });
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
app.listen(3000);
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
**Logged automatically:**
|
|
363
|
-
- Request: `GET /users/123`
|
|
364
|
-
- Response: `GET /users/123 200 (45ms)`
|
|
365
|
-
- Errors: `Request error: Internal Server Error`
|
|
366
|
-
|
|
367
|
-
### Fastify Plugin
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
import Fastify from 'fastify';
|
|
371
|
-
import { LogTideClient, logTideFastifyPlugin } from '@logtide/sdk-node';
|
|
372
|
-
|
|
373
|
-
const fastify = Fastify();
|
|
374
|
-
const logger = new LogTideClient({
|
|
375
|
-
apiUrl: 'http://localhost:8080',
|
|
376
|
-
apiKey: 'lp_your_api_key_here',
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
await fastify.register(logTideFastifyPlugin, {
|
|
380
|
-
client: logger,
|
|
381
|
-
serviceName: 'fastify-api',
|
|
382
|
-
logRequests: true,
|
|
383
|
-
logResponses: true,
|
|
384
|
-
logErrors: true,
|
|
385
|
-
skipHealthCheck: true,
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
fastify.get('/', async () => ({ message: 'Hello' }));
|
|
389
|
-
|
|
390
|
-
await fastify.listen({ port: 3000 });
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
---
|
|
394
|
-
|
|
395
|
-
## Examples
|
|
396
|
-
|
|
397
|
-
See the [examples/](./examples) directory for complete working examples:
|
|
398
|
-
|
|
399
|
-
- **[basic.ts](./examples/basic.ts)** - Simple usage
|
|
400
|
-
- **[advanced.ts](./examples/advanced.ts)** - All advanced features
|
|
401
|
-
- **[express-middleware.ts](./examples/express-middleware.ts)** - Express integration
|
|
402
|
-
- **[fastify-plugin.ts](./examples/fastify-plugin.ts)** - Fastify integration
|
|
403
|
-
|
|
404
|
-
---
|
|
405
|
-
|
|
406
|
-
## Best Practices
|
|
407
|
-
|
|
408
|
-
### 1. Always Close on Shutdown
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
process.on('SIGINT', async () => {
|
|
412
|
-
await client.close(); // Flushes buffered logs
|
|
413
|
-
process.exit(0);
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
process.on('SIGTERM', async () => {
|
|
417
|
-
await client.close();
|
|
418
|
-
process.exit(0);
|
|
419
|
-
});
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### 2. Use Global Metadata
|
|
423
|
-
|
|
424
|
-
```typescript
|
|
425
|
-
const client = new LogTideClient({
|
|
426
|
-
apiUrl: 'http://localhost:8080',
|
|
427
|
-
apiKey: 'lp_your_api_key_here',
|
|
428
|
-
globalMetadata: {
|
|
429
|
-
env: process.env.NODE_ENV,
|
|
430
|
-
version: require('./package.json').version,
|
|
431
|
-
region: process.env.AWS_REGION,
|
|
432
|
-
pod: process.env.HOSTNAME,
|
|
433
|
-
},
|
|
434
|
-
});
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### 3. Enable Debug Mode in Development
|
|
438
|
-
|
|
439
|
-
```typescript
|
|
440
|
-
const client = new LogTideClient({
|
|
441
|
-
apiUrl: 'http://localhost:8080',
|
|
442
|
-
apiKey: 'lp_your_api_key_here',
|
|
443
|
-
debug: process.env.NODE_ENV === 'development',
|
|
444
|
-
});
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### 4. Monitor Metrics in Production
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
setInterval(() => {
|
|
451
|
-
const metrics = client.getMetrics();
|
|
452
|
-
|
|
453
|
-
if (metrics.logsDropped > 0) {
|
|
454
|
-
console.warn(`Logs dropped: ${metrics.logsDropped}`);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (metrics.circuitBreakerTrips > 0) {
|
|
458
|
-
console.error('Circuit breaker is OPEN!');
|
|
459
|
-
}
|
|
460
|
-
}, 60000);
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
## TypeScript Support
|
|
466
|
-
|
|
467
|
-
Fully typed with strict TypeScript support:
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
import type {
|
|
471
|
-
LogTideClient,
|
|
472
|
-
LogEntry,
|
|
473
|
-
QueryOptions,
|
|
474
|
-
LogsResponse,
|
|
475
|
-
ClientMetrics,
|
|
476
|
-
} from '@logtide/sdk-node';
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
---
|
|
480
|
-
|
|
481
|
-
## Contributing
|
|
482
|
-
|
|
483
|
-
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
484
|
-
|
|
485
|
-
## License
|
|
486
|
-
|
|
487
|
-
MIT License - see [LICENSE](LICENSE) for details.
|
|
488
|
-
|
|
489
|
-
## Links
|
|
490
|
-
|
|
491
|
-
- [LogTide Website](https://logtide.dev)
|
|
492
|
-
- [Documentation](https://logtide.dev/docs/sdks/node/)
|
|
493
|
-
- [GitHub Issues](https://github.com/logtide-dev/logtide-javascript/issues)
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/logtide-dev/logtide/main/docs/images/logo.png" alt="LogTide Logo" width="400">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">LogTide Node.js SDK</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@logtide/sdk-node"><img src="https://img.shields.io/npm/v/@logtide/sdk-node?color=blue" alt="npm"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
|
|
10
|
+
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-20+-green.svg" alt="Node.js"></a>
|
|
11
|
+
<a href="https://github.com/logtide-dev/logtide-javascript/releases"><img src="https://img.shields.io/github/v/release/logtide-dev/logtide-javascript" alt="Release"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
Official Node.js SDK for <a href="https://logtide.dev">LogTide</a> with automatic batching, retry logic, circuit breaker, query API, live streaming, and middleware support.
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Automatic batching** with configurable size and interval
|
|
23
|
+
- **Retry logic** with exponential backoff
|
|
24
|
+
- **Circuit breaker** pattern for fault tolerance
|
|
25
|
+
- **Max buffer size** with drop policy to prevent memory leaks
|
|
26
|
+
- **Query API** for searching and filtering logs
|
|
27
|
+
- **Live tail** with Server-Sent Events (SSE)
|
|
28
|
+
- **Trace ID context** for distributed tracing
|
|
29
|
+
- **Global metadata** added to all logs
|
|
30
|
+
- **Structured error serialization**
|
|
31
|
+
- **Internal metrics** (logs sent, errors, latency, etc.)
|
|
32
|
+
- **Express & Fastify middleware** for auto-logging HTTP requests
|
|
33
|
+
- **Full TypeScript support** with strict types
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @logtide/sdk-node
|
|
39
|
+
# or
|
|
40
|
+
pnpm add @logtide/sdk-node
|
|
41
|
+
# or
|
|
42
|
+
yarn add @logtide/sdk-node
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { LogTideClient } from '@logtide/sdk-node';
|
|
49
|
+
|
|
50
|
+
const client = new LogTideClient({
|
|
51
|
+
apiUrl: 'http://localhost:8080',
|
|
52
|
+
apiKey: 'lp_your_api_key_here',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Send logs
|
|
56
|
+
client.info('api-gateway', 'Server started', { port: 3000 });
|
|
57
|
+
client.error('database', 'Connection failed', new Error('Timeout'));
|
|
58
|
+
|
|
59
|
+
// Graceful shutdown
|
|
60
|
+
process.on('SIGINT', async () => {
|
|
61
|
+
await client.close();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Configuration Options
|
|
69
|
+
|
|
70
|
+
### Basic Options
|
|
71
|
+
|
|
72
|
+
| Option | Type | Default | Description |
|
|
73
|
+
|--------|------|---------|-------------|
|
|
74
|
+
| `apiUrl` | `string` | **required** | Base URL of your LogTide instance |
|
|
75
|
+
| `apiKey` | `string` | **required** | Project API key (starts with `lp_`) |
|
|
76
|
+
| `batchSize` | `number` | `100` | Number of logs to batch before sending |
|
|
77
|
+
| `flushInterval` | `number` | `5000` | Interval in ms to auto-flush logs |
|
|
78
|
+
|
|
79
|
+
### Advanced Options
|
|
80
|
+
|
|
81
|
+
| Option | Type | Default | Description |
|
|
82
|
+
|--------|------|---------|-------------|
|
|
83
|
+
| `maxBufferSize` | `number` | `10000` | Max logs in buffer (prevents memory leak) |
|
|
84
|
+
| `maxRetries` | `number` | `3` | Max retry attempts on failure |
|
|
85
|
+
| `retryDelayMs` | `number` | `1000` | Initial retry delay (exponential backoff) |
|
|
86
|
+
| `circuitBreakerThreshold` | `number` | `5` | Failures before opening circuit |
|
|
87
|
+
| `circuitBreakerResetMs` | `number` | `30000` | Time before retrying after circuit opens |
|
|
88
|
+
| `enableMetrics` | `boolean` | `true` | Track internal metrics |
|
|
89
|
+
| `debug` | `boolean` | `false` | Enable debug logging to console |
|
|
90
|
+
| `globalMetadata` | `object` | `{}` | Metadata added to all logs |
|
|
91
|
+
| `autoTraceId` | `boolean` | `false` | Auto-generate trace IDs for logs |
|
|
92
|
+
|
|
93
|
+
### Example: Full Configuration
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const client = new LogTideClient({
|
|
97
|
+
apiUrl: 'http://localhost:8080',
|
|
98
|
+
apiKey: 'lp_your_api_key_here',
|
|
99
|
+
|
|
100
|
+
// Batching
|
|
101
|
+
batchSize: 100,
|
|
102
|
+
flushInterval: 5000,
|
|
103
|
+
|
|
104
|
+
// Buffer management
|
|
105
|
+
maxBufferSize: 10000,
|
|
106
|
+
|
|
107
|
+
// Retry with exponential backoff (1s -> 2s -> 4s)
|
|
108
|
+
maxRetries: 3,
|
|
109
|
+
retryDelayMs: 1000,
|
|
110
|
+
|
|
111
|
+
// Circuit breaker
|
|
112
|
+
circuitBreakerThreshold: 5,
|
|
113
|
+
circuitBreakerResetMs: 30000,
|
|
114
|
+
|
|
115
|
+
// Metrics & debugging
|
|
116
|
+
enableMetrics: true,
|
|
117
|
+
debug: true,
|
|
118
|
+
|
|
119
|
+
// Global context
|
|
120
|
+
globalMetadata: {
|
|
121
|
+
env: process.env.NODE_ENV,
|
|
122
|
+
version: '1.0.0',
|
|
123
|
+
hostname: process.env.HOSTNAME,
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// Auto trace IDs
|
|
127
|
+
autoTraceId: false,
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Logging Methods
|
|
134
|
+
|
|
135
|
+
### Basic Logging
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
client.debug('service-name', 'Debug message');
|
|
139
|
+
client.info('service-name', 'Info message', { userId: 123 });
|
|
140
|
+
client.warn('service-name', 'Warning message');
|
|
141
|
+
client.error('service-name', 'Error message', { custom: 'data' });
|
|
142
|
+
client.critical('service-name', 'Critical message');
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Error Logging with Auto-Serialization
|
|
146
|
+
|
|
147
|
+
The SDK automatically serializes `Error` objects:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
try {
|
|
151
|
+
throw new Error('Database timeout');
|
|
152
|
+
} catch (error) {
|
|
153
|
+
// Automatically serializes error with stack trace
|
|
154
|
+
client.error('database', 'Query failed', error);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Generated log metadata:
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"error": {
|
|
162
|
+
"name": "Error",
|
|
163
|
+
"message": "Database timeout",
|
|
164
|
+
"stack": "Error: Database timeout\n at ..."
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Custom Log Entry
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
client.log({
|
|
173
|
+
service: 'custom-service',
|
|
174
|
+
level: 'info',
|
|
175
|
+
message: 'Custom log',
|
|
176
|
+
time: new Date().toISOString(),
|
|
177
|
+
metadata: { key: 'value' },
|
|
178
|
+
trace_id: 'custom-trace-id',
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Trace ID Context
|
|
185
|
+
|
|
186
|
+
Track requests across services with trace IDs.
|
|
187
|
+
|
|
188
|
+
### Manual Trace ID
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
client.setTraceId('request-123');
|
|
192
|
+
|
|
193
|
+
client.info('api', 'Request received');
|
|
194
|
+
client.info('database', 'Querying users');
|
|
195
|
+
client.info('api', 'Response sent');
|
|
196
|
+
|
|
197
|
+
client.setTraceId(null); // Clear context
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Scoped Trace ID
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
client.withTraceId('request-456', () => {
|
|
204
|
+
client.info('api', 'Processing in context');
|
|
205
|
+
client.warn('cache', 'Cache miss');
|
|
206
|
+
});
|
|
207
|
+
// Trace ID automatically restored after block
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Auto-Generated Trace ID
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
client.withNewTraceId(() => {
|
|
214
|
+
client.info('worker', 'Background job started');
|
|
215
|
+
client.info('worker', 'Job completed');
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Query API
|
|
222
|
+
|
|
223
|
+
Search and retrieve logs programmatically.
|
|
224
|
+
|
|
225
|
+
### Basic Query
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const result = await client.query({
|
|
229
|
+
service: 'api-gateway',
|
|
230
|
+
level: 'error',
|
|
231
|
+
from: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24h
|
|
232
|
+
to: new Date(),
|
|
233
|
+
limit: 100,
|
|
234
|
+
offset: 0,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
console.log(`Found ${result.total} logs`);
|
|
238
|
+
console.log(result.logs);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Full-Text Search
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const result = await client.query({
|
|
245
|
+
q: 'timeout',
|
|
246
|
+
limit: 50,
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Get Logs by Trace ID
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const logs = await client.getByTraceId('trace-123');
|
|
254
|
+
console.log(`Trace has ${logs.length} logs`);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Aggregated Statistics
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const stats = await client.getAggregatedStats({
|
|
261
|
+
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
|
|
262
|
+
to: new Date(),
|
|
263
|
+
interval: '1h', // '1m' | '5m' | '1h' | '1d'
|
|
264
|
+
service: 'api-gateway', // Optional
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
console.log(stats.timeseries); // Time-bucketed counts
|
|
268
|
+
console.log(stats.top_services); // Top services by log count
|
|
269
|
+
console.log(stats.top_errors); // Most common errors
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Live Tail (Streaming)
|
|
275
|
+
|
|
276
|
+
Stream logs in real-time using Server-Sent Events.
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const cleanup = client.stream({
|
|
280
|
+
service: 'api-gateway', // Optional filter
|
|
281
|
+
level: 'error', // Optional filter
|
|
282
|
+
|
|
283
|
+
onLog: (log) => {
|
|
284
|
+
console.log(`[${log.time}] ${log.level}: ${log.message}`);
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
onError: (error) => {
|
|
288
|
+
console.error('Stream error:', error);
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Stop streaming when done
|
|
293
|
+
setTimeout(() => {
|
|
294
|
+
cleanup();
|
|
295
|
+
}, 60000);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Metrics
|
|
301
|
+
|
|
302
|
+
Track SDK performance and health.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const metrics = client.getMetrics();
|
|
306
|
+
|
|
307
|
+
console.log(metrics.logsSent); // Total logs sent
|
|
308
|
+
console.log(metrics.logsDropped); // Logs dropped (buffer full)
|
|
309
|
+
console.log(metrics.errors); // Send errors
|
|
310
|
+
console.log(metrics.retries); // Retry attempts
|
|
311
|
+
console.log(metrics.avgLatencyMs); // Average send latency
|
|
312
|
+
console.log(metrics.circuitBreakerTrips); // Circuit breaker openings
|
|
313
|
+
|
|
314
|
+
// Get circuit breaker state
|
|
315
|
+
console.log(client.getCircuitBreakerState()); // 'CLOSED' | 'OPEN' | 'HALF_OPEN'
|
|
316
|
+
|
|
317
|
+
// Reset metrics
|
|
318
|
+
client.resetMetrics();
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Middleware Integration
|
|
324
|
+
|
|
325
|
+
LogTide provides ready-to-use middleware for popular frameworks.
|
|
326
|
+
|
|
327
|
+
### Express Middleware
|
|
328
|
+
|
|
329
|
+
Auto-log all HTTP requests and responses.
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import express from 'express';
|
|
333
|
+
import { LogTideClient, logTideMiddleware } from '@logtide/sdk-node';
|
|
334
|
+
|
|
335
|
+
const app = express();
|
|
336
|
+
const logger = new LogTideClient({
|
|
337
|
+
apiUrl: 'http://localhost:8080',
|
|
338
|
+
apiKey: 'lp_your_api_key_here',
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
app.use(
|
|
342
|
+
logTideMiddleware({
|
|
343
|
+
client: logger,
|
|
344
|
+
serviceName: 'express-api',
|
|
345
|
+
logRequests: true,
|
|
346
|
+
logResponses: true,
|
|
347
|
+
logErrors: true,
|
|
348
|
+
includeHeaders: false,
|
|
349
|
+
includeBody: false,
|
|
350
|
+
skipHealthCheck: true,
|
|
351
|
+
skipPaths: ['/metrics'],
|
|
352
|
+
}),
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
app.get('/', (req, res) => {
|
|
356
|
+
res.json({ message: 'Hello' });
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
app.listen(3000);
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**Logged automatically:**
|
|
363
|
+
- Request: `GET /users/123`
|
|
364
|
+
- Response: `GET /users/123 200 (45ms)`
|
|
365
|
+
- Errors: `Request error: Internal Server Error`
|
|
366
|
+
|
|
367
|
+
### Fastify Plugin
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
import Fastify from 'fastify';
|
|
371
|
+
import { LogTideClient, logTideFastifyPlugin } from '@logtide/sdk-node';
|
|
372
|
+
|
|
373
|
+
const fastify = Fastify();
|
|
374
|
+
const logger = new LogTideClient({
|
|
375
|
+
apiUrl: 'http://localhost:8080',
|
|
376
|
+
apiKey: 'lp_your_api_key_here',
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
await fastify.register(logTideFastifyPlugin, {
|
|
380
|
+
client: logger,
|
|
381
|
+
serviceName: 'fastify-api',
|
|
382
|
+
logRequests: true,
|
|
383
|
+
logResponses: true,
|
|
384
|
+
logErrors: true,
|
|
385
|
+
skipHealthCheck: true,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
fastify.get('/', async () => ({ message: 'Hello' }));
|
|
389
|
+
|
|
390
|
+
await fastify.listen({ port: 3000 });
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Examples
|
|
396
|
+
|
|
397
|
+
See the [examples/](./examples) directory for complete working examples:
|
|
398
|
+
|
|
399
|
+
- **[basic.ts](./examples/basic.ts)** - Simple usage
|
|
400
|
+
- **[advanced.ts](./examples/advanced.ts)** - All advanced features
|
|
401
|
+
- **[express-middleware.ts](./examples/express-middleware.ts)** - Express integration
|
|
402
|
+
- **[fastify-plugin.ts](./examples/fastify-plugin.ts)** - Fastify integration
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Best Practices
|
|
407
|
+
|
|
408
|
+
### 1. Always Close on Shutdown
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
process.on('SIGINT', async () => {
|
|
412
|
+
await client.close(); // Flushes buffered logs
|
|
413
|
+
process.exit(0);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
process.on('SIGTERM', async () => {
|
|
417
|
+
await client.close();
|
|
418
|
+
process.exit(0);
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### 2. Use Global Metadata
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
const client = new LogTideClient({
|
|
426
|
+
apiUrl: 'http://localhost:8080',
|
|
427
|
+
apiKey: 'lp_your_api_key_here',
|
|
428
|
+
globalMetadata: {
|
|
429
|
+
env: process.env.NODE_ENV,
|
|
430
|
+
version: require('./package.json').version,
|
|
431
|
+
region: process.env.AWS_REGION,
|
|
432
|
+
pod: process.env.HOSTNAME,
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 3. Enable Debug Mode in Development
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
const client = new LogTideClient({
|
|
441
|
+
apiUrl: 'http://localhost:8080',
|
|
442
|
+
apiKey: 'lp_your_api_key_here',
|
|
443
|
+
debug: process.env.NODE_ENV === 'development',
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### 4. Monitor Metrics in Production
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
setInterval(() => {
|
|
451
|
+
const metrics = client.getMetrics();
|
|
452
|
+
|
|
453
|
+
if (metrics.logsDropped > 0) {
|
|
454
|
+
console.warn(`Logs dropped: ${metrics.logsDropped}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (metrics.circuitBreakerTrips > 0) {
|
|
458
|
+
console.error('Circuit breaker is OPEN!');
|
|
459
|
+
}
|
|
460
|
+
}, 60000);
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## TypeScript Support
|
|
466
|
+
|
|
467
|
+
Fully typed with strict TypeScript support:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import type {
|
|
471
|
+
LogTideClient,
|
|
472
|
+
LogEntry,
|
|
473
|
+
QueryOptions,
|
|
474
|
+
LogsResponse,
|
|
475
|
+
ClientMetrics,
|
|
476
|
+
} from '@logtide/sdk-node';
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Contributing
|
|
482
|
+
|
|
483
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
484
|
+
|
|
485
|
+
## License
|
|
486
|
+
|
|
487
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
488
|
+
|
|
489
|
+
## Links
|
|
490
|
+
|
|
491
|
+
- [LogTide Website](https://logtide.dev)
|
|
492
|
+
- [Documentation](https://logtide.dev/docs/sdks/node/)
|
|
493
|
+
- [GitHub Issues](https://github.com/logtide-dev/logtide-javascript/issues)
|