@rabbit-company/logger 5.1.0 → 5.2.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/README.md +54 -133
- package/module/logger.d.ts +227 -52
- package/module/logger.js +62 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,20 +4,17 @@
|
|
|
4
4
|
[](https://jsr.io/@rabbit-company/logger)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
A
|
|
8
|
-
|
|
9
|
-
- Console output (with colors and custom formatting)
|
|
10
|
-
- NDJSON (Newline Delimited JSON)
|
|
11
|
-
- Grafana Loki (with batching and label management)
|
|
7
|
+
A high-performance, multi-transport logging library for Node.js and browser environments with first-class TypeScript support.
|
|
12
8
|
|
|
13
9
|
## Features ✨
|
|
14
10
|
|
|
15
|
-
- **
|
|
16
|
-
- **Structured logging**: Attach metadata
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **Cross-platform**: Works in Node.js, Deno, Bun and browsers
|
|
11
|
+
- **Multi-level logging**: 8 severity levels from ERROR to SILLY
|
|
12
|
+
- **Structured logging**: Attach rich metadata to log entries
|
|
13
|
+
- **Multiple transports**: Console, NDJSON, and Grafana Loki
|
|
14
|
+
- **Advanced formatting**: Customizable console output with extensive datetime options
|
|
15
|
+
- **Production-ready**: Batching, retries, and queue management for Loki
|
|
16
|
+
- **Cross-platform**: Works in Node.js, Deno, Bun and modern browsers
|
|
17
|
+
- **Type-safe**: Full TypeScript definitions included
|
|
21
18
|
|
|
22
19
|
## Installation 📦
|
|
23
20
|
|
|
@@ -38,16 +35,40 @@ pnpm add @rabbit-company/logger
|
|
|
38
35
|
import { Logger, Levels } from "@rabbit-company/logger";
|
|
39
36
|
|
|
40
37
|
// Create logger with default console transport
|
|
41
|
-
const logger = new Logger({
|
|
38
|
+
const logger = new Logger({
|
|
39
|
+
level: Levels.DEBUG, // Show DEBUG and above
|
|
40
|
+
});
|
|
42
41
|
|
|
43
|
-
//
|
|
44
|
-
logger.info("Application
|
|
42
|
+
// Simple log
|
|
43
|
+
logger.info("Application starting...");
|
|
44
|
+
|
|
45
|
+
// Structured logging
|
|
45
46
|
logger.error("Database connection failed", {
|
|
46
47
|
error: "Connection timeout",
|
|
47
48
|
attempt: 3,
|
|
49
|
+
db: "primary",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Audit logging
|
|
53
|
+
logger.audit("User login", {
|
|
54
|
+
userId: "usr_123",
|
|
55
|
+
ip: "192.168.1.100",
|
|
48
56
|
});
|
|
49
57
|
```
|
|
50
58
|
|
|
59
|
+
## Log Levels 📊
|
|
60
|
+
|
|
61
|
+
| Level | Description | Typical Use Case |
|
|
62
|
+
| ------- | ------------------------------- | ------------------------------------- |
|
|
63
|
+
| ERROR | Critical errors | System failures, unhandled exceptions |
|
|
64
|
+
| WARN | Potential issues | Deprecations, rate limiting |
|
|
65
|
+
| AUDIT | Security events | Logins, permission changes |
|
|
66
|
+
| INFO | Important runtime information | Service starts, config changes |
|
|
67
|
+
| HTTP | HTTP traffic | Request/response logging |
|
|
68
|
+
| DEBUG | Debug information | Flow tracing, variable dumps |
|
|
69
|
+
| VERBOSE | Very detailed information | Data transformations |
|
|
70
|
+
| SILLY | Extremely low-level information | Inner loop logging |
|
|
71
|
+
|
|
51
72
|
## Console Formatting 🖥️
|
|
52
73
|
|
|
53
74
|
The console transport supports extensive datetime formatting:
|
|
@@ -115,135 +136,35 @@ const logger = new Logger({
|
|
|
115
136
|
console.log(ndjsonTransport.getData());
|
|
116
137
|
```
|
|
117
138
|
|
|
118
|
-
### Loki Transport
|
|
139
|
+
### Loki Transport (Grafana)
|
|
119
140
|
|
|
120
141
|
```js
|
|
121
142
|
import { LokiTransport } from "@rabbit-company/logger";
|
|
122
143
|
|
|
144
|
+
const lokiTransport = new LokiTransport({
|
|
145
|
+
url: "http://localhost:3100",
|
|
146
|
+
labels: {
|
|
147
|
+
app: "test",
|
|
148
|
+
env: process.env.NODE_ENV,
|
|
149
|
+
},
|
|
150
|
+
basicAuth: {
|
|
151
|
+
username: process.env.LOKI_USER,
|
|
152
|
+
password: process.env.LOKI_PASS,
|
|
153
|
+
},
|
|
154
|
+
batchSize: 50, // Send batches of 50 logs
|
|
155
|
+
batchTimeout: 5000, // Max 5s wait per batch
|
|
156
|
+
maxQueueSize: 10000, // Keep max 10,000 logs in memory
|
|
157
|
+
debug: true, // Log transport errors
|
|
158
|
+
});
|
|
159
|
+
|
|
123
160
|
const logger = new Logger({
|
|
124
|
-
transports: [
|
|
125
|
-
new LokiTransport({
|
|
126
|
-
url: "http://localhost:3100",
|
|
127
|
-
labels: { app: "my-app", env: "production" },
|
|
128
|
-
basicAuth: { username: "user", password: "pass" },
|
|
129
|
-
maxLabelCount: 30,
|
|
130
|
-
}),
|
|
131
|
-
],
|
|
161
|
+
transports: [lokiTransport],
|
|
132
162
|
});
|
|
133
163
|
```
|
|
134
164
|
|
|
135
165
|
## API Reference 📚
|
|
136
166
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```js
|
|
140
|
-
enum Levels {
|
|
141
|
-
ERROR, // Critical errors
|
|
142
|
-
WARN, // Warnings
|
|
143
|
-
AUDIT, // Security audits
|
|
144
|
-
INFO, // Informational
|
|
145
|
-
HTTP, // HTTP traffic
|
|
146
|
-
DEBUG, // Debugging
|
|
147
|
-
VERBOSE, // Detailed tracing
|
|
148
|
-
SILLY // Very low-level
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Logging Methods
|
|
153
|
-
|
|
154
|
-
```js
|
|
155
|
-
logger.error(message: string, metadata?: object): void
|
|
156
|
-
logger.warn(message: string, metadata?: object): void
|
|
157
|
-
logger.audit(message: string, metadata?: object): void
|
|
158
|
-
logger.info(message: string, metadata?: object): void
|
|
159
|
-
logger.http(message: string, metadata?: object): void
|
|
160
|
-
logger.verbose(message: string, metadata?: object): void
|
|
161
|
-
logger.debug(message: string, metadata?: object): void
|
|
162
|
-
logger.silly(message: string, metadata?: object): void
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Types
|
|
166
|
-
|
|
167
|
-
```ts
|
|
168
|
-
/**
|
|
169
|
-
* Represents a single log entry with message, severity level, timestamp, and optional metadata
|
|
170
|
-
*/
|
|
171
|
-
export interface LogEntry {
|
|
172
|
-
/** The log message content */
|
|
173
|
-
message: string;
|
|
174
|
-
/** Severity level of the log entry */
|
|
175
|
-
level: Levels;
|
|
176
|
-
/** Timestamp in milliseconds since epoch */
|
|
177
|
-
timestamp: number;
|
|
178
|
-
/** Optional structured metadata associated with the log */
|
|
179
|
-
metadata?: Record<string, any>;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Interface for log transport implementations
|
|
184
|
-
*/
|
|
185
|
-
export interface Transport {
|
|
186
|
-
/**
|
|
187
|
-
* Processes and outputs a log entry
|
|
188
|
-
* @param entry The log entry to process
|
|
189
|
-
*/
|
|
190
|
-
log: (entry: LogEntry) => void;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Configuration options for the Logger instance
|
|
195
|
-
*/
|
|
196
|
-
export interface LoggerConfig {
|
|
197
|
-
/** Minimum log level to output (default: INFO) */
|
|
198
|
-
level?: Levels;
|
|
199
|
-
/** Enable colored output (default: true) */
|
|
200
|
-
colors?: boolean;
|
|
201
|
-
/** Format string using {date}, {type}, {message} placeholders (default: "[{date}] {type} {message}") */
|
|
202
|
-
format?: string;
|
|
203
|
-
/** Array of transports to use (default: [ConsoleTransport]) */
|
|
204
|
-
transports?: Transport[];
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Configuration for Loki transport
|
|
209
|
-
*/
|
|
210
|
-
export interface LokiConfig {
|
|
211
|
-
/** Loki server URL (e.g., "http://localhost:3100") */
|
|
212
|
-
url: string;
|
|
213
|
-
/** Base labels to attach to all logs */
|
|
214
|
-
labels?: Record<string, string>;
|
|
215
|
-
/** Basic authentication credentials */
|
|
216
|
-
basicAuth?: {
|
|
217
|
-
username: string;
|
|
218
|
-
password: string;
|
|
219
|
-
};
|
|
220
|
-
/** Number of logs to batch before sending (default: 10) */
|
|
221
|
-
batchSize?: number;
|
|
222
|
-
/** Maximum time in ms to wait before sending a batch (default: 5000) */
|
|
223
|
-
batchTimeout?: number;
|
|
224
|
-
/** Tenant ID for multi-tenant Loki setups */
|
|
225
|
-
tenantID?: string;
|
|
226
|
-
/** Maximum number of labels allowed (default: 50) */
|
|
227
|
-
maxLabelCount?: number;
|
|
228
|
-
/** Enable debug logging for transport errors (default: false) */
|
|
229
|
-
debug?: boolean;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Represents a Loki log stream with labels and log values
|
|
234
|
-
*/
|
|
235
|
-
export interface LokiStream {
|
|
236
|
-
/** Key-value pairs of log labels */
|
|
237
|
-
stream: {
|
|
238
|
-
/** Log level label (required) */
|
|
239
|
-
level: string;
|
|
240
|
-
/** Additional custom labels */
|
|
241
|
-
[key: string]: string;
|
|
242
|
-
};
|
|
243
|
-
/** Array of log entries with [timestamp, message] pairs */
|
|
244
|
-
values: [[string, string]];
|
|
245
|
-
}
|
|
246
|
-
```
|
|
167
|
+
Full API documentation is available in the [TypeScript definitions](https://github.com/Rabbit-Company/Logger-JS/blob/main/src/types.ts).
|
|
247
168
|
|
|
248
169
|
## Advanced Usage 🛠️
|
|
249
170
|
|
package/module/logger.d.ts
CHANGED
|
@@ -110,6 +110,23 @@ export declare enum Levels {
|
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* Represents a single log entry with message, severity level, timestamp, and optional metadata
|
|
113
|
+
*
|
|
114
|
+
* @interface LogEntry
|
|
115
|
+
* @property {string} message - The primary log message content
|
|
116
|
+
* @property {Levels} level - Severity level of the log entry
|
|
117
|
+
* @property {number} timestamp - Unix timestamp in milliseconds since epoch
|
|
118
|
+
* @property {Object.<string, any>} [metadata] - Optional structured metadata associated with the log
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* {
|
|
122
|
+
* message: "User login successful",
|
|
123
|
+
* level: Levels.INFO,
|
|
124
|
+
* timestamp: Date.now(),
|
|
125
|
+
* metadata: {
|
|
126
|
+
* userId: "12345",
|
|
127
|
+
* ipAddress: "192.168.1.100"
|
|
128
|
+
* }
|
|
129
|
+
* }
|
|
113
130
|
*/
|
|
114
131
|
export interface LogEntry {
|
|
115
132
|
/** The log message content */
|
|
@@ -122,50 +139,168 @@ export interface LogEntry {
|
|
|
122
139
|
metadata?: Record<string, any>;
|
|
123
140
|
}
|
|
124
141
|
/**
|
|
125
|
-
* Interface
|
|
142
|
+
* Interface that all log transport implementations must adhere to
|
|
143
|
+
*
|
|
144
|
+
* @interface Transport
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* class CustomTransport implements Transport {
|
|
148
|
+
* log(entry: LogEntry) {
|
|
149
|
+
* // Custom log handling implementation
|
|
150
|
+
* }
|
|
151
|
+
* }
|
|
126
152
|
*/
|
|
127
153
|
export interface Transport {
|
|
128
154
|
/**
|
|
129
155
|
* Processes and outputs a log entry
|
|
130
|
-
* @param entry The log entry to process
|
|
156
|
+
* @param {LogEntry} entry - The log entry to process
|
|
157
|
+
* @returns {void}
|
|
131
158
|
*/
|
|
132
159
|
log: (entry: LogEntry) => void;
|
|
133
160
|
}
|
|
134
161
|
/**
|
|
135
162
|
* Configuration options for the Logger instance
|
|
163
|
+
*
|
|
164
|
+
* @interface LoggerConfig
|
|
165
|
+
* @property {Levels} [level=Levels.INFO] - Minimum log level to output
|
|
166
|
+
* @property {boolean} [colors=true] - Enable colored output in console
|
|
167
|
+
* @property {string} [format="[{datetime-local}] {type} {message}"] - Format string supporting these placeholders:
|
|
168
|
+
*
|
|
169
|
+
* ## Time/Date Placeholders
|
|
170
|
+
*
|
|
171
|
+
* ### UTC Formats
|
|
172
|
+
* - `{iso}`: Full ISO-8601 format with milliseconds (YYYY-MM-DDTHH:MM:SS.mmmZ)
|
|
173
|
+
* - `{datetime}`: Simplified ISO without milliseconds (YYYY-MM-DD HH:MM:SS)
|
|
174
|
+
* - `{date}`: Date component only (YYYY-MM-DD)
|
|
175
|
+
* - `{time}`: Time component only (HH:MM:SS)
|
|
176
|
+
* - `{utc}`: Complete UTC string (e.g., "Wed, 15 Nov 2023 14:30:45 GMT")
|
|
177
|
+
* - `{ms}`: Milliseconds since Unix epoch
|
|
178
|
+
*
|
|
179
|
+
* ### Local Time Formats
|
|
180
|
+
* - `{datetime-local}`: Local date and time (YYYY-MM-DD HH:MM:SS)
|
|
181
|
+
* - `{date-local}`: Local date only (YYYY-MM-DD)
|
|
182
|
+
* - `{time-local}`: Local time only (HH:MM:SS)
|
|
183
|
+
* - `{full-local}`: Complete local string with timezone
|
|
184
|
+
*
|
|
185
|
+
* ## Log Content Placeholders
|
|
186
|
+
* - `{type}`: Log level name (e.g., "INFO", "ERROR")
|
|
187
|
+
* - `{message}`: The actual log message content
|
|
188
|
+
*
|
|
189
|
+
* @property {Transport[]} [transports=[ConsoleTransport]] - Array of transports to use
|
|
190
|
+
*
|
|
191
|
+
* @example <caption>Default Format</caption>
|
|
192
|
+
* {
|
|
193
|
+
* format: "[{datetime-local}] {type} {message}"
|
|
194
|
+
* }
|
|
195
|
+
*
|
|
196
|
+
* @example <caption>UTC Time Format</caption>
|
|
197
|
+
* {
|
|
198
|
+
* format: "[{datetime} UTC] {type}: {message}"
|
|
199
|
+
* }
|
|
200
|
+
*
|
|
201
|
+
* @example <caption>Detailed Local Format</caption>
|
|
202
|
+
* {
|
|
203
|
+
* format: "{date-local} {time-local} [{type}] {message}"
|
|
204
|
+
* }
|
|
205
|
+
*
|
|
206
|
+
* @example <caption>Epoch Timestamp</caption>
|
|
207
|
+
* {
|
|
208
|
+
* format: "{ms} - {type} - {message}"
|
|
209
|
+
* }
|
|
136
210
|
*/
|
|
137
211
|
export interface LoggerConfig {
|
|
138
212
|
/** Minimum log level to output (default: INFO) */
|
|
139
213
|
level?: Levels;
|
|
140
214
|
/** Enable colored output (default: true) */
|
|
141
215
|
colors?: boolean;
|
|
142
|
-
/** Format string using
|
|
216
|
+
/** Format string using placeholders (default: "[{datetime-local}] {type} {message}") */
|
|
143
217
|
format?: string;
|
|
144
218
|
/** Array of transports to use (default: [ConsoleTransport]) */
|
|
145
219
|
transports?: Transport[];
|
|
146
220
|
}
|
|
147
221
|
/**
|
|
148
|
-
* Configuration for Loki transport
|
|
222
|
+
* Configuration options for the Loki transport
|
|
223
|
+
*
|
|
224
|
+
* @interface LokiConfig
|
|
225
|
+
*
|
|
226
|
+
* @example <caption>Basic Configuration</caption>
|
|
227
|
+
* {
|
|
228
|
+
* url: "http://localhost:3100/loki/api/v1/push",
|
|
229
|
+
* labels: { app: "frontend", env: "production" }
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* @example <caption>Advanced Configuration</caption>
|
|
233
|
+
* {
|
|
234
|
+
* url: "http://loki.example.com",
|
|
235
|
+
* basicAuth: { username: "user", password: "pass" },
|
|
236
|
+
* tenantID: "team-a",
|
|
237
|
+
* batchSize: 50,
|
|
238
|
+
* batchTimeout: 2000,
|
|
239
|
+
* maxQueueSize: 50000,
|
|
240
|
+
* maxRetries: 10,
|
|
241
|
+
* retryBaseDelay: 2000,
|
|
242
|
+
* debug: true
|
|
243
|
+
* }
|
|
149
244
|
*/
|
|
150
245
|
export interface LokiConfig {
|
|
151
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Required Loki server endpoint URL
|
|
248
|
+
* @example "http://loki.example.com"
|
|
249
|
+
*/
|
|
152
250
|
url: string;
|
|
153
|
-
/**
|
|
251
|
+
/**
|
|
252
|
+
* Base labels attached to all log entries
|
|
253
|
+
* @example { app: "frontend", env: "production" }
|
|
254
|
+
*/
|
|
154
255
|
labels?: Record<string, string>;
|
|
155
256
|
/** Basic authentication credentials */
|
|
156
257
|
basicAuth?: {
|
|
258
|
+
/** Basic auth username */
|
|
157
259
|
username: string;
|
|
260
|
+
/** Basic auth password */
|
|
158
261
|
password: string;
|
|
159
262
|
};
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/** Tenant ID for multi-tenant Loki setups */
|
|
263
|
+
/**
|
|
264
|
+
* Tenant ID for multi-tenant Loki installations
|
|
265
|
+
* @description Sets the X-Scope-OrgID header
|
|
266
|
+
*/
|
|
165
267
|
tenantID?: string;
|
|
166
|
-
/**
|
|
268
|
+
/**
|
|
269
|
+
* Maximum number of labels allowed per log entry
|
|
270
|
+
* @default 50
|
|
271
|
+
*/
|
|
167
272
|
maxLabelCount?: number;
|
|
168
|
-
/**
|
|
273
|
+
/**
|
|
274
|
+
* Number of logs to accumulate before automatic sending
|
|
275
|
+
* @default 10
|
|
276
|
+
*/
|
|
277
|
+
batchSize?: number;
|
|
278
|
+
/**
|
|
279
|
+
* Maximum time in milliseconds to wait before sending an incomplete batch
|
|
280
|
+
* @default 5000
|
|
281
|
+
*/
|
|
282
|
+
batchTimeout?: number;
|
|
283
|
+
/**
|
|
284
|
+
* Maximum number of logs to buffer in memory during outages
|
|
285
|
+
* @description When reached, oldest logs are dropped
|
|
286
|
+
* @default 10000
|
|
287
|
+
*/
|
|
288
|
+
maxQueueSize?: number;
|
|
289
|
+
/**
|
|
290
|
+
* Maximum number of attempts to send a failed batch
|
|
291
|
+
* @default 5
|
|
292
|
+
*/
|
|
293
|
+
maxRetries?: number;
|
|
294
|
+
/**
|
|
295
|
+
* Initial retry delay in milliseconds (exponential backoff base)
|
|
296
|
+
* @description Delay doubles with each retry up to maximum 30s
|
|
297
|
+
* @default 1000
|
|
298
|
+
*/
|
|
299
|
+
retryBaseDelay?: number;
|
|
300
|
+
/**
|
|
301
|
+
* Enable debug logging for transport internals
|
|
302
|
+
* @default false
|
|
303
|
+
*/
|
|
169
304
|
debug?: boolean;
|
|
170
305
|
}
|
|
171
306
|
/**
|
|
@@ -501,82 +636,122 @@ export declare class NDJsonTransport implements Transport {
|
|
|
501
636
|
reset(): void;
|
|
502
637
|
}
|
|
503
638
|
/**
|
|
504
|
-
*
|
|
639
|
+
* High-reliability transport for sending logs to Grafana Loki with persistent queuing.
|
|
505
640
|
*
|
|
506
641
|
* Features:
|
|
507
|
-
* -
|
|
508
|
-
* -
|
|
642
|
+
* - Persistent in-memory queue with configurable size limits
|
|
643
|
+
* - Exponential backoff retry mechanism with configurable limits
|
|
644
|
+
* - Automatic batching for efficient network utilization
|
|
509
645
|
* - Label management with cardinality control
|
|
510
646
|
* - Multi-tenancy support via X-Scope-OrgID
|
|
511
|
-
* -
|
|
647
|
+
* - Comprehensive error handling and recovery
|
|
512
648
|
*
|
|
513
|
-
* @
|
|
514
|
-
*
|
|
649
|
+
* @implements {Transport}
|
|
650
|
+
*
|
|
651
|
+
* @example <caption>Basic Configuration</caption>
|
|
515
652
|
* const lokiTransport = new LokiTransport({
|
|
516
653
|
* url: "http://localhost:3100",
|
|
517
654
|
* labels: { app: "my-app", env: "production" }
|
|
518
655
|
* });
|
|
519
656
|
*
|
|
520
|
-
* @example
|
|
521
|
-
*
|
|
522
|
-
* const securedTransport = new LokiTransport({
|
|
657
|
+
* @example <caption>Advanced Configuration</caption>
|
|
658
|
+
* const transport = new LokiTransport({
|
|
523
659
|
* url: "http://loki.example.com",
|
|
660
|
+
* batchSize: 50,
|
|
661
|
+
* batchTimeout: 2000,
|
|
662
|
+
* maxQueueSize: 50000,
|
|
663
|
+
* maxRetries: 10,
|
|
664
|
+
* retryBaseDelay: 2000,
|
|
665
|
+
* tenantID: "team-a",
|
|
524
666
|
* basicAuth: { username: "user", password: "pass" },
|
|
525
|
-
*
|
|
526
|
-
* batchTimeout: 10000 // 10 seconds
|
|
667
|
+
* debug: true
|
|
527
668
|
* });
|
|
528
669
|
*/
|
|
529
670
|
export declare class LokiTransport implements Transport {
|
|
530
671
|
private config;
|
|
531
|
-
private
|
|
672
|
+
/** @private Internal log queue */
|
|
673
|
+
private queue;
|
|
674
|
+
/** @private Current batch size setting */
|
|
532
675
|
private batchSize;
|
|
676
|
+
/** @private Current batch timeout setting (ms) */
|
|
533
677
|
private batchTimeout;
|
|
678
|
+
/** @private Handle for batch timeout */
|
|
534
679
|
private timeoutHandle?;
|
|
680
|
+
/** @private Maximum allowed labels per entry */
|
|
535
681
|
private maxLabelCount;
|
|
682
|
+
/** @private Debug mode flag */
|
|
536
683
|
private debug;
|
|
684
|
+
/** @private Maximum queue size before dropping logs */
|
|
685
|
+
private maxQueueSize;
|
|
686
|
+
/** @private Current retry attempt count */
|
|
687
|
+
private retryCount;
|
|
688
|
+
/** @private Maximum allowed retry attempts */
|
|
689
|
+
private maxRetries;
|
|
690
|
+
/** @private Base delay for exponential backoff (ms) */
|
|
691
|
+
private retryBaseDelay;
|
|
692
|
+
/** @private Handle for retry timeout */
|
|
693
|
+
private retryTimer?;
|
|
694
|
+
/** @private Flag indicating active send operation */
|
|
695
|
+
private isSending;
|
|
537
696
|
/**
|
|
538
697
|
* Creates a new LokiTransport instance
|
|
539
|
-
* @param config Configuration options
|
|
540
|
-
* @param config.url Required Loki server
|
|
541
|
-
* @param config.labels Base labels
|
|
542
|
-
* @param config.basicAuth Basic authentication credentials
|
|
543
|
-
* @param config.
|
|
544
|
-
* @param config.
|
|
545
|
-
* @param config.tenantID Tenant ID for multi-tenant Loki
|
|
546
|
-
* @param config.
|
|
547
|
-
* @param config.
|
|
548
|
-
* @
|
|
698
|
+
* @param {LokiConfig} config - Configuration options
|
|
699
|
+
* @param {string} config.url - Required Loki server endpoint (e.g., "http://localhost:3100")
|
|
700
|
+
* @param {Object.<string, string>} [config.labels] - Base labels attached to all log entries
|
|
701
|
+
* @param {Object} [config.basicAuth] - Basic authentication credentials
|
|
702
|
+
* @param {string} config.basicAuth.username - Username for basic auth
|
|
703
|
+
* @param {string} config.basicAuth.password - Password for basic auth
|
|
704
|
+
* @param {string} [config.tenantID] - Tenant ID for multi-tenant Loki installations
|
|
705
|
+
* @param {number} [config.batchSize=10] - Number of logs to accumulate before automatic sending
|
|
706
|
+
* @param {number} [config.batchTimeout=5000] - Maximum time (ms) to wait before sending incomplete batch
|
|
707
|
+
* @param {number} [config.maxLabelCount=50] - Maximum number of labels allowed per log entry
|
|
708
|
+
* @param {number} [config.maxQueueSize=10000] - Maximum number of logs to buffer in memory during outages
|
|
709
|
+
* @param {number} [config.maxRetries=5] - Maximum number of attempts to send a failed batch
|
|
710
|
+
* @param {number} [config.retryBaseDelay=1000] - Initial retry delay in ms (exponential backoff base)
|
|
711
|
+
* @param {boolean} [config.debug=false] - Enable debug logging for transport internals
|
|
549
712
|
*/
|
|
550
713
|
constructor(config: LokiConfig);
|
|
551
714
|
/**
|
|
552
|
-
*
|
|
553
|
-
* - The batch reaches the configured size, OR
|
|
554
|
-
* - The batch timeout is reached
|
|
715
|
+
* Queues a log entry for delivery to Loki
|
|
555
716
|
*
|
|
556
|
-
* @param entry The log entry to
|
|
557
|
-
*
|
|
717
|
+
* @param {LogEntry} entry - The log entry to process
|
|
718
|
+
* @param {string} entry.message - Primary log message content
|
|
719
|
+
* @param {string} entry.level - Log severity level (e.g., "INFO", "ERROR")
|
|
720
|
+
* @param {number} entry.timestamp - Unix timestamp in milliseconds
|
|
721
|
+
* @param {Object} [entry.metadata] - Additional log metadata (will be converted to Loki labels)
|
|
558
722
|
*
|
|
559
723
|
* @example
|
|
560
724
|
* transport.log({
|
|
561
|
-
* message: "User
|
|
562
|
-
* level:
|
|
725
|
+
* message: "User login successful",
|
|
726
|
+
* level: "INFO",
|
|
563
727
|
* timestamp: Date.now(),
|
|
564
|
-
* metadata: {
|
|
728
|
+
* metadata: {
|
|
729
|
+
* userId: "12345",
|
|
730
|
+
* sourceIP: "192.168.1.100",
|
|
731
|
+
* device: "mobile"
|
|
732
|
+
* }
|
|
565
733
|
* });
|
|
566
734
|
*/
|
|
567
735
|
log(entry: LogEntry): void;
|
|
568
736
|
/**
|
|
569
|
-
*
|
|
737
|
+
* Schedules the next batch send operation
|
|
570
738
|
* @private
|
|
739
|
+
* @param {boolean} [immediate=false] - Whether to send immediately without waiting for timeout
|
|
740
|
+
*/
|
|
741
|
+
private scheduleSend;
|
|
742
|
+
/**
|
|
743
|
+
* Sends the current batch to Loki with retry logic
|
|
744
|
+
* @private
|
|
745
|
+
* @async
|
|
746
|
+
* @returns {Promise<void>}
|
|
571
747
|
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
* -
|
|
575
|
-
* -
|
|
576
|
-
* -
|
|
577
|
-
*
|
|
578
|
-
*
|
|
579
|
-
* and typically doesn't need to be called directly.
|
|
748
|
+
* @description
|
|
749
|
+
* Handles the complete send operation including:
|
|
750
|
+
* - Preparing HTTP request with proper headers
|
|
751
|
+
* - Executing the fetch request
|
|
752
|
+
* - Managing retries with exponential backoff
|
|
753
|
+
* - Queue cleanup on success/failure
|
|
754
|
+
* - Automatic scheduling of next batch
|
|
580
755
|
*/
|
|
581
756
|
private sendBatch;
|
|
582
757
|
}
|
package/module/logger.js
CHANGED
|
@@ -219,40 +219,59 @@ function formatLokiMessage(entry, maxLabelCount, labels = {}) {
|
|
|
219
219
|
// src/transports/lokiTransport.ts
|
|
220
220
|
class LokiTransport {
|
|
221
221
|
config;
|
|
222
|
-
|
|
222
|
+
queue = [];
|
|
223
223
|
batchSize;
|
|
224
224
|
batchTimeout;
|
|
225
225
|
timeoutHandle;
|
|
226
226
|
maxLabelCount;
|
|
227
227
|
debug;
|
|
228
|
+
maxQueueSize;
|
|
229
|
+
retryCount = 0;
|
|
230
|
+
maxRetries;
|
|
231
|
+
retryBaseDelay;
|
|
232
|
+
retryTimer;
|
|
233
|
+
isSending = false;
|
|
228
234
|
constructor(config) {
|
|
229
235
|
this.config = config;
|
|
230
236
|
this.batchSize = config.batchSize || 10;
|
|
231
237
|
this.batchTimeout = config.batchTimeout || 5000;
|
|
232
238
|
this.maxLabelCount = config.maxLabelCount || 50;
|
|
233
239
|
this.debug = config.debug || false;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
this.maxQueueSize = config.maxQueueSize || 1e4;
|
|
241
|
+
this.maxRetries = config.maxRetries || 5;
|
|
242
|
+
this.retryBaseDelay = config.retryBaseDelay || 1000;
|
|
237
243
|
}
|
|
238
244
|
log(entry) {
|
|
239
|
-
const lokiMessage = formatLokiMessage(entry, this.maxLabelCount, {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
const lokiMessage = formatLokiMessage(entry, this.maxLabelCount, {
|
|
246
|
+
...this.config.labels,
|
|
247
|
+
...entry.metadata
|
|
248
|
+
});
|
|
249
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
250
|
+
if (this.debug)
|
|
251
|
+
console.warn("Loki queue full - dropping oldest log entry");
|
|
252
|
+
this.queue.shift();
|
|
245
253
|
}
|
|
254
|
+
this.queue.push(lokiMessage);
|
|
255
|
+
this.scheduleSend();
|
|
246
256
|
}
|
|
247
|
-
|
|
257
|
+
scheduleSend(immediate = false) {
|
|
258
|
+
if (this.isSending)
|
|
259
|
+
return;
|
|
248
260
|
if (this.timeoutHandle) {
|
|
249
261
|
clearTimeout(this.timeoutHandle);
|
|
250
262
|
this.timeoutHandle = undefined;
|
|
251
263
|
}
|
|
252
|
-
if (this.
|
|
264
|
+
if (this.queue.length > 0 && (immediate || this.queue.length >= this.batchSize)) {
|
|
265
|
+
this.sendBatch();
|
|
266
|
+
} else if (this.queue.length > 0) {
|
|
267
|
+
this.timeoutHandle = setTimeout(() => this.sendBatch(), this.batchTimeout);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async sendBatch() {
|
|
271
|
+
if (this.queue.length === 0 || this.isSending)
|
|
253
272
|
return;
|
|
254
|
-
|
|
255
|
-
this.
|
|
273
|
+
this.isSending = true;
|
|
274
|
+
const batchToSend = this.queue.slice(0, this.batchSize);
|
|
256
275
|
try {
|
|
257
276
|
const headers = {
|
|
258
277
|
"Content-Type": "application/json"
|
|
@@ -270,12 +289,38 @@ class LokiTransport {
|
|
|
270
289
|
streams: batchToSend.flatMap((entry) => entry.streams)
|
|
271
290
|
})
|
|
272
291
|
});
|
|
273
|
-
if (
|
|
274
|
-
|
|
292
|
+
if (response.ok) {
|
|
293
|
+
this.queue = this.queue.slice(batchToSend.length);
|
|
294
|
+
this.retryCount = 0;
|
|
295
|
+
if (this.retryTimer) {
|
|
296
|
+
clearTimeout(this.retryTimer);
|
|
297
|
+
this.retryTimer = undefined;
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
|
275
301
|
}
|
|
276
302
|
} catch (error) {
|
|
277
303
|
if (this.debug)
|
|
278
|
-
console.error("
|
|
304
|
+
console.error("Loki transmission error: ", error);
|
|
305
|
+
this.retryCount++;
|
|
306
|
+
if (this.retryCount <= this.maxRetries) {
|
|
307
|
+
const delay = Math.min(this.retryBaseDelay * Math.pow(2, this.retryCount - 1), 30000);
|
|
308
|
+
if (this.debug)
|
|
309
|
+
console.log(`Scheduling retry #${this.retryCount} in ${delay}ms`);
|
|
310
|
+
this.retryTimer = setTimeout(() => {
|
|
311
|
+
this.scheduleSend(true);
|
|
312
|
+
}, delay);
|
|
313
|
+
} else {
|
|
314
|
+
if (this.debug)
|
|
315
|
+
console.warn(`Max retries (${this.maxRetries}) reached. Dropping batch.`);
|
|
316
|
+
this.queue = this.queue.slice(batchToSend.length);
|
|
317
|
+
this.retryCount = 0;
|
|
318
|
+
}
|
|
319
|
+
} finally {
|
|
320
|
+
this.isSending = false;
|
|
321
|
+
if (this.queue.length > 0 && this.retryCount === 0) {
|
|
322
|
+
this.scheduleSend();
|
|
323
|
+
}
|
|
279
324
|
}
|
|
280
325
|
}
|
|
281
326
|
}
|