@rabbit-company/logger 5.2.0 → 5.4.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 +43 -7
- package/module/logger.d.ts +279 -9
- package/module/logger.js +263 -8
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -10,10 +10,10 @@ A high-performance, multi-transport logging library for Node.js and browser envi
|
|
|
10
10
|
|
|
11
11
|
- **Multi-level logging**: 8 severity levels from ERROR to SILLY
|
|
12
12
|
- **Structured logging**: Attach rich metadata to log entries
|
|
13
|
-
- **Multiple transports**: Console, NDJSON,
|
|
13
|
+
- **Multiple transports**: Console, NDJSON, Grafana Loki and Syslog
|
|
14
14
|
- **Advanced formatting**: Customizable console output with extensive datetime options
|
|
15
15
|
- **Production-ready**: Batching, retries, and queue management for Loki
|
|
16
|
-
- **Cross-platform**: Works in Node.js, Deno
|
|
16
|
+
- **Cross-platform**: Works in Node.js, Deno and Bun
|
|
17
17
|
- **Type-safe**: Full TypeScript definitions included
|
|
18
18
|
|
|
19
19
|
## Installation 📦
|
|
@@ -96,13 +96,18 @@ The console transport supports extensive datetime formatting:
|
|
|
96
96
|
- `{type}`: Log level (INFO, ERROR, etc.)
|
|
97
97
|
- `{message}`: The log message
|
|
98
98
|
|
|
99
|
+
#### Metadata Placeholders:
|
|
100
|
+
|
|
101
|
+
- `{metadata}`: JSON-stringified metadata (if provided)
|
|
102
|
+
- `{metadata-ml}`: Multi-line JSON-formatted metadata (if provided)
|
|
103
|
+
|
|
99
104
|
```js
|
|
100
105
|
import { ConsoleTransport } from "@rabbit-company/logger";
|
|
101
106
|
|
|
102
107
|
// Custom format examples
|
|
103
|
-
new ConsoleTransport("[{datetime-local}] {type} {message}");
|
|
104
|
-
new ConsoleTransport("{time} | {type} | {message}", false);
|
|
105
|
-
new ConsoleTransport("EPOCH:{ms} {message}");
|
|
108
|
+
new ConsoleTransport("[{datetime-local}] {type} {message} {metadata}");
|
|
109
|
+
new ConsoleTransport("{time} | {type} | {message} | {metadata}", false);
|
|
110
|
+
new ConsoleTransport("EPOCH:{ms} - {message} - {metadata}");
|
|
106
111
|
```
|
|
107
112
|
|
|
108
113
|
## Transports 🚚
|
|
@@ -115,7 +120,7 @@ import { ConsoleTransport } from "@rabbit-company/logger";
|
|
|
115
120
|
const logger = new Logger({
|
|
116
121
|
transports: [
|
|
117
122
|
new ConsoleTransport(
|
|
118
|
-
"[{time-local}] {type} {message}", // Custom format
|
|
123
|
+
"[{time-local}] {type} {message} {metadata}", // Custom format
|
|
119
124
|
true // Enable colors
|
|
120
125
|
),
|
|
121
126
|
],
|
|
@@ -162,6 +167,37 @@ const logger = new Logger({
|
|
|
162
167
|
});
|
|
163
168
|
```
|
|
164
169
|
|
|
170
|
+
### Syslog Transport
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
import { SyslogTransport } from "@rabbit-company/logger";
|
|
174
|
+
|
|
175
|
+
const syslogTransport = new SyslogTransport({
|
|
176
|
+
host: "syslog.example.com",
|
|
177
|
+
port: 514,
|
|
178
|
+
protocol: "udp", // 'udp', 'tcp', or 'tcp-tls'
|
|
179
|
+
facility: 16, // local0 facility
|
|
180
|
+
appName: "my-app",
|
|
181
|
+
protocolVersion: 5424, // 3164 (BSD) or 5424 (modern)
|
|
182
|
+
tlsOptions: {
|
|
183
|
+
ca: fs.readFileSync("ca.pem"),
|
|
184
|
+
rejectUnauthorized: true,
|
|
185
|
+
},
|
|
186
|
+
maxQueueSize: 2000, // Max queued messages during outages
|
|
187
|
+
debug: true, // Log connection status
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const logger = new Logger({
|
|
191
|
+
transports: [syslogTransport],
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Features:
|
|
195
|
+
// - Automatic reconnection with exponential backoff
|
|
196
|
+
// - Message queuing during network issues
|
|
197
|
+
// - Supports UDP, TCP, and TLS encryption
|
|
198
|
+
// - Compliant with RFC 3164 and RFC 5424
|
|
199
|
+
```
|
|
200
|
+
|
|
165
201
|
## API Reference 📚
|
|
166
202
|
|
|
167
203
|
Full API documentation is available in the [TypeScript definitions](https://github.com/Rabbit-Company/Logger-JS/blob/main/src/types.ts).
|
|
@@ -172,7 +208,7 @@ Full API documentation is available in the [TypeScript definitions](https://gith
|
|
|
172
208
|
|
|
173
209
|
```js
|
|
174
210
|
new ConsoleTransport(
|
|
175
|
-
"{type} - {date} - {message}", // Custom format
|
|
211
|
+
"{type} - {date} - {message} - {metadata}", // Custom format
|
|
176
212
|
false // Disable colors
|
|
177
213
|
);
|
|
178
214
|
```
|
package/module/logger.d.ts
CHANGED
|
@@ -186,26 +186,30 @@ export interface Transport {
|
|
|
186
186
|
* - `{type}`: Log level name (e.g., "INFO", "ERROR")
|
|
187
187
|
* - `{message}`: The actual log message content
|
|
188
188
|
*
|
|
189
|
+
* ## Metadata Placeholders
|
|
190
|
+
* - `{metadata}`: JSON-stringified metadata (if provided)
|
|
191
|
+
* - `{metadata-ml}`: Multi-line JSON-formatted metadata (if provided)
|
|
192
|
+
*
|
|
189
193
|
* @property {Transport[]} [transports=[ConsoleTransport]] - Array of transports to use
|
|
190
194
|
*
|
|
191
195
|
* @example <caption>Default Format</caption>
|
|
192
196
|
* {
|
|
193
|
-
* format: "[{datetime-local}] {type} {message}"
|
|
197
|
+
* format: "[{datetime-local}] {type} {message} {metadata}"
|
|
194
198
|
* }
|
|
195
199
|
*
|
|
196
200
|
* @example <caption>UTC Time Format</caption>
|
|
197
201
|
* {
|
|
198
|
-
* format: "[{datetime} UTC] {type}: {message}"
|
|
202
|
+
* format: "[{datetime} UTC] {type}: {message} {metadata}"
|
|
199
203
|
* }
|
|
200
204
|
*
|
|
201
205
|
* @example <caption>Detailed Local Format</caption>
|
|
202
206
|
* {
|
|
203
|
-
* format: "{date-local} {time-local} [{type}] {message}"
|
|
207
|
+
* format: "{date-local} {time-local} [{type}] {message} {metadata}"
|
|
204
208
|
* }
|
|
205
209
|
*
|
|
206
210
|
* @example <caption>Epoch Timestamp</caption>
|
|
207
211
|
* {
|
|
208
|
-
* format: "{ms} - {type} - {message}"
|
|
212
|
+
* format: "{ms} - {type} - {message} - {metadata}"
|
|
209
213
|
* }
|
|
210
214
|
*/
|
|
211
215
|
export interface LoggerConfig {
|
|
@@ -213,7 +217,7 @@ export interface LoggerConfig {
|
|
|
213
217
|
level?: Levels;
|
|
214
218
|
/** Enable colored output (default: true) */
|
|
215
219
|
colors?: boolean;
|
|
216
|
-
/** Format string using placeholders (default: "[{datetime-local}] {type} {message}") */
|
|
220
|
+
/** Format string using placeholders (default: "[{datetime-local}] {type} {message} {metadata}") */
|
|
217
221
|
format?: string;
|
|
218
222
|
/** Array of transports to use (default: [ConsoleTransport]) */
|
|
219
223
|
transports?: Transport[];
|
|
@@ -322,6 +326,155 @@ export interface LokiStream {
|
|
|
322
326
|
]
|
|
323
327
|
];
|
|
324
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Configuration options for Syslog transport
|
|
331
|
+
* @interface SyslogConfig
|
|
332
|
+
* @description Defines the configuration parameters for establishing a connection
|
|
333
|
+
* to a syslog server and customizing log message formatting.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* // Basic UDP configuration
|
|
337
|
+
* const config: SyslogConfig = {
|
|
338
|
+
* host: 'logs.example.com',
|
|
339
|
+
* port: 514,
|
|
340
|
+
* protocol: 'udp'
|
|
341
|
+
* };
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* // Secure TCP with TLS configuration
|
|
345
|
+
* const secureConfig: SyslogConfig = {
|
|
346
|
+
* host: 'secure-logs.example.com',
|
|
347
|
+
* port: 6514,
|
|
348
|
+
* protocol: 'tcp-tls',
|
|
349
|
+
* tlsOptions: {
|
|
350
|
+
* ca: fs.readFileSync('ca.pem'),
|
|
351
|
+
* cert: fs.readFileSync('client-cert.pem'),
|
|
352
|
+
* key: fs.readFileSync('client-key.pem'),
|
|
353
|
+
* rejectUnauthorized: true
|
|
354
|
+
* },
|
|
355
|
+
* appName: 'my-service',
|
|
356
|
+
* facility: 16 // local0
|
|
357
|
+
* };
|
|
358
|
+
*/
|
|
359
|
+
export interface SyslogConfig {
|
|
360
|
+
/**
|
|
361
|
+
* Syslog server hostname or IP address
|
|
362
|
+
* @type {string}
|
|
363
|
+
* @default 'localhost'
|
|
364
|
+
* @example 'logs.example.com'
|
|
365
|
+
* @example '192.168.1.100'
|
|
366
|
+
*/
|
|
367
|
+
host?: string;
|
|
368
|
+
/**
|
|
369
|
+
* Syslog server port number
|
|
370
|
+
* @type {number}
|
|
371
|
+
* @default 514
|
|
372
|
+
* @example 514 // Standard syslog port
|
|
373
|
+
* @example 6514 // Common port for syslog over TLS
|
|
374
|
+
*/
|
|
375
|
+
port?: number;
|
|
376
|
+
/**
|
|
377
|
+
* Network protocol to use for syslog transmission
|
|
378
|
+
* @type {'udp' | 'tcp' | 'tcp-tls'}
|
|
379
|
+
* @default 'udp'
|
|
380
|
+
* @description
|
|
381
|
+
* - 'udp': Unreliable but fast (RFC 3164 compatible)
|
|
382
|
+
* - 'tcp': Reliable connection (RFC 6587)
|
|
383
|
+
* - 'tcp-tls': Encrypted connection (RFC 5425)
|
|
384
|
+
*/
|
|
385
|
+
protocol?: "udp" | "tcp" | "tcp-tls";
|
|
386
|
+
/**
|
|
387
|
+
* Syslog facility code
|
|
388
|
+
* @type {number}
|
|
389
|
+
* @range 0-23
|
|
390
|
+
* @default 1 // USER
|
|
391
|
+
* @description
|
|
392
|
+
* Standard syslog facilities:
|
|
393
|
+
* - 0: kern - Kernel messages
|
|
394
|
+
* - 1: user - User-level messages
|
|
395
|
+
* - 2: mail - Mail system
|
|
396
|
+
* - 3: daemon - System daemons
|
|
397
|
+
* - 4: auth - Security/authentication
|
|
398
|
+
* - 5: syslog - Internal syslog messages
|
|
399
|
+
* - 6: lpr - Line printer subsystem
|
|
400
|
+
* - 7: news - Network news subsystem
|
|
401
|
+
* - 8: uucp - UUCP subsystem
|
|
402
|
+
* - 9: cron - Clock daemon
|
|
403
|
+
* - 10: authpriv - Security/authentication
|
|
404
|
+
* - 11: ftp - FTP daemon
|
|
405
|
+
* - 16-23: local0-local7 - Locally used facilities
|
|
406
|
+
*/
|
|
407
|
+
facility?: number;
|
|
408
|
+
/**
|
|
409
|
+
* Application name identifier included in syslog messages
|
|
410
|
+
* @type {string}
|
|
411
|
+
* @default 'node'
|
|
412
|
+
* @description
|
|
413
|
+
* Should be a short string (typically <= 32 chars) identifying the application.
|
|
414
|
+
* @example 'auth-service'
|
|
415
|
+
* @example 'payment-processor'
|
|
416
|
+
*/
|
|
417
|
+
appName?: string;
|
|
418
|
+
/**
|
|
419
|
+
* Process ID included in syslog messages
|
|
420
|
+
* @type {number}
|
|
421
|
+
* @default process.pid
|
|
422
|
+
* @description
|
|
423
|
+
* Used to identify the specific process generating the log message.
|
|
424
|
+
*/
|
|
425
|
+
pid?: number;
|
|
426
|
+
/**
|
|
427
|
+
* Syslog protocol version specification
|
|
428
|
+
* @type {3164 | 5424}
|
|
429
|
+
* @default 5424
|
|
430
|
+
* @description
|
|
431
|
+
* - 3164: Traditional BSD syslog format (RFC 3164)
|
|
432
|
+
* - 5424: Modern structured syslog format (RFC 5424)
|
|
433
|
+
*/
|
|
434
|
+
protocolVersion?: 3164 | 5424;
|
|
435
|
+
/**
|
|
436
|
+
* TLS configuration options for secure connections
|
|
437
|
+
* @type {Object}
|
|
438
|
+
* @description Required when protocol is 'tcp-tls'
|
|
439
|
+
* @property {string} [ca] - PEM encoded CA certificate
|
|
440
|
+
* @property {string} [cert] - PEM encoded client certificate
|
|
441
|
+
* @property {string} [key] - PEM encoded client private key
|
|
442
|
+
* @property {boolean} [rejectUnauthorized=true] - Verify server certificate
|
|
443
|
+
*/
|
|
444
|
+
tlsOptions?: {
|
|
445
|
+
/** CA certificate */
|
|
446
|
+
ca?: string;
|
|
447
|
+
/** Client certificate */
|
|
448
|
+
cert?: string;
|
|
449
|
+
/** Client private key */
|
|
450
|
+
key?: string;
|
|
451
|
+
/** Whether to reject unauthorized certificates (default: true) */
|
|
452
|
+
rejectUnauthorized?: boolean;
|
|
453
|
+
};
|
|
454
|
+
/**
|
|
455
|
+
* Maximum number of log messages to buffer in memory
|
|
456
|
+
* @type {number}
|
|
457
|
+
* @default 1000
|
|
458
|
+
* @description
|
|
459
|
+
* When the queue reaches this size, oldest messages will be dropped.
|
|
460
|
+
* Set to 0 for unlimited (not recommended in production).
|
|
461
|
+
*/
|
|
462
|
+
maxQueueSize?: number;
|
|
463
|
+
/**
|
|
464
|
+
* Initial retry delay in milliseconds (exponential backoff base)
|
|
465
|
+
* @description Delay doubles with each retry up to maximum 30s
|
|
466
|
+
* @default 1000
|
|
467
|
+
*/
|
|
468
|
+
retryBaseDelay?: number;
|
|
469
|
+
/**
|
|
470
|
+
* Enable debug output for transport operations
|
|
471
|
+
* @type {boolean}
|
|
472
|
+
* @default false
|
|
473
|
+
* @description
|
|
474
|
+
* When true, outputs connection status and error details to console.
|
|
475
|
+
*/
|
|
476
|
+
debug?: boolean;
|
|
477
|
+
}
|
|
325
478
|
/**
|
|
326
479
|
* Main Logger class that handles all logging functionality.
|
|
327
480
|
*
|
|
@@ -493,7 +646,7 @@ export declare class Logger {
|
|
|
493
646
|
* @example
|
|
494
647
|
* // Custom format with local timestamps
|
|
495
648
|
* const transport = new ConsoleTransport(
|
|
496
|
-
* "[{datetime-local}] {type} - {message}",
|
|
649
|
+
* "[{datetime-local}] {type} - {message} {metadata}",
|
|
497
650
|
* true
|
|
498
651
|
* );
|
|
499
652
|
*/
|
|
@@ -518,7 +671,11 @@ export declare class ConsoleTransport implements Transport {
|
|
|
518
671
|
* - `{type}`: Log level name (e.g., "INFO")
|
|
519
672
|
* - `{message}`: The log message content
|
|
520
673
|
*
|
|
521
|
-
*
|
|
674
|
+
* ### Metadata Placeholders
|
|
675
|
+
* - `{metadata}`: JSON-stringified metadata (if provided)
|
|
676
|
+
* - `{metadata-ml}`: Multi-line JSON-formatted metadata (if provided)
|
|
677
|
+
*
|
|
678
|
+
* @default "[{datetime-local}] {type} {message} {metadata}"
|
|
522
679
|
*
|
|
523
680
|
* @param colors Enable ANSI color output. When disabled:
|
|
524
681
|
* - Improves performance in non-TTY environments
|
|
@@ -527,11 +684,11 @@ export declare class ConsoleTransport implements Transport {
|
|
|
527
684
|
*
|
|
528
685
|
* @example
|
|
529
686
|
* // UTC format example
|
|
530
|
-
* new ConsoleTransport("{date} {time} [{type}] {message}");
|
|
687
|
+
* new ConsoleTransport("{date} {time} [{type}] {message} {metadata}");
|
|
531
688
|
*
|
|
532
689
|
* @example
|
|
533
690
|
* // Local time with colors disabled
|
|
534
|
-
* new ConsoleTransport("{time-local} - {message}", false);
|
|
691
|
+
* new ConsoleTransport("{time-local} - {message} {metadata}", false);
|
|
535
692
|
*/
|
|
536
693
|
constructor(format?: string, colors?: boolean);
|
|
537
694
|
/**
|
|
@@ -755,5 +912,118 @@ export declare class LokiTransport implements Transport {
|
|
|
755
912
|
*/
|
|
756
913
|
private sendBatch;
|
|
757
914
|
}
|
|
915
|
+
/**
|
|
916
|
+
* Syslog transport implementation for the logger library
|
|
917
|
+
* @class SyslogTransport
|
|
918
|
+
* @implements {Transport}
|
|
919
|
+
* @description A robust syslog client that supports UDP, TCP, and TLS-encrypted TCP connections
|
|
920
|
+
* with automatic reconnection and message queuing capabilities.
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* // Basic UDP configuration
|
|
924
|
+
* const transport = new SyslogTransport({
|
|
925
|
+
* host: 'logs.example.com',
|
|
926
|
+
* port: 514,
|
|
927
|
+
* protocol: 'udp'
|
|
928
|
+
* });
|
|
929
|
+
*
|
|
930
|
+
* @example
|
|
931
|
+
* // Secure TLS configuration
|
|
932
|
+
* const secureTransport = new SyslogTransport({
|
|
933
|
+
* host: 'secure-logs.example.com',
|
|
934
|
+
* port: 6514,
|
|
935
|
+
* protocol: 'tcp-tls',
|
|
936
|
+
* tlsOptions: {
|
|
937
|
+
* ca: fs.readFileSync('ca.pem'),
|
|
938
|
+
* rejectUnauthorized: true
|
|
939
|
+
* },
|
|
940
|
+
* maxQueueSize: 5000
|
|
941
|
+
* });
|
|
942
|
+
*/
|
|
943
|
+
export declare class SyslogTransport implements Transport {
|
|
944
|
+
private socket;
|
|
945
|
+
private queue;
|
|
946
|
+
private isConnecting;
|
|
947
|
+
private retryCount;
|
|
948
|
+
private retryBaseDelay;
|
|
949
|
+
private maxQueueSize;
|
|
950
|
+
private debug;
|
|
951
|
+
private reconnectTimer;
|
|
952
|
+
private config;
|
|
953
|
+
/**
|
|
954
|
+
* Creates a new SyslogTransport instance
|
|
955
|
+
* @constructor
|
|
956
|
+
* @param {SyslogConfig} [config={}] - Configuration options for the transport
|
|
957
|
+
*/
|
|
958
|
+
constructor(config?: SyslogConfig);
|
|
959
|
+
/**
|
|
960
|
+
* Initializes the appropriate socket based on configured protocol
|
|
961
|
+
* @private
|
|
962
|
+
* @returns {void}
|
|
963
|
+
*/
|
|
964
|
+
private initializeSocket;
|
|
965
|
+
/**
|
|
966
|
+
* Initializes a UDP socket for syslog transmission
|
|
967
|
+
* @private
|
|
968
|
+
* @returns {void}
|
|
969
|
+
*/
|
|
970
|
+
private initializeUdpSocket;
|
|
971
|
+
/**
|
|
972
|
+
* Initializes a TCP socket for syslog transmission
|
|
973
|
+
* @private
|
|
974
|
+
* @returns {void}
|
|
975
|
+
*/
|
|
976
|
+
private initializeTcpSocket;
|
|
977
|
+
/**
|
|
978
|
+
* Initializes a TLS-secured TCP socket for syslog transmission
|
|
979
|
+
* @private
|
|
980
|
+
* @returns {void}
|
|
981
|
+
*/
|
|
982
|
+
private initializeTlsSocket;
|
|
983
|
+
/**
|
|
984
|
+
* Sets up common event handlers for TCP/TLS sockets
|
|
985
|
+
* @private
|
|
986
|
+
* @returns {void}
|
|
987
|
+
*/
|
|
988
|
+
private setupTcpSocketEvents;
|
|
989
|
+
/**
|
|
990
|
+
* Establishes a TCP connection to the syslog server
|
|
991
|
+
* @private
|
|
992
|
+
* @returns {void}
|
|
993
|
+
*/
|
|
994
|
+
private connectTcpSocket;
|
|
995
|
+
/**
|
|
996
|
+
* Handles socket errors and initiates reconnection if needed
|
|
997
|
+
* @private
|
|
998
|
+
* @returns {void}
|
|
999
|
+
*/
|
|
1000
|
+
private handleSocketError;
|
|
1001
|
+
/**
|
|
1002
|
+
* Sends all queued messages to the syslog server
|
|
1003
|
+
* @private
|
|
1004
|
+
* @returns {void}
|
|
1005
|
+
*/
|
|
1006
|
+
private flushQueue;
|
|
1007
|
+
/**
|
|
1008
|
+
* Sends a single message to the syslog server
|
|
1009
|
+
* @private
|
|
1010
|
+
* @param {string} message - The formatted syslog message to send
|
|
1011
|
+
* @returns {void}
|
|
1012
|
+
*/
|
|
1013
|
+
private sendMessage;
|
|
1014
|
+
/**
|
|
1015
|
+
* Processes a log entry by formatting and queueing it for transmission
|
|
1016
|
+
* @public
|
|
1017
|
+
* @param {LogEntry} entry - The log entry to process
|
|
1018
|
+
* @returns {void}
|
|
1019
|
+
*/
|
|
1020
|
+
log(entry: LogEntry): void;
|
|
1021
|
+
/**
|
|
1022
|
+
* Gracefully closes the transport connection
|
|
1023
|
+
* @public
|
|
1024
|
+
* @returns {Promise<void>} A promise that resolves when the connection is closed
|
|
1025
|
+
*/
|
|
1026
|
+
close(): Promise<void>;
|
|
1027
|
+
}
|
|
758
1028
|
|
|
759
1029
|
export {};
|
package/module/logger.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
1
4
|
// src/constants/colors.ts
|
|
2
5
|
var Colors;
|
|
3
6
|
((Colors2) => {
|
|
@@ -44,7 +47,7 @@ var LevelColors = {
|
|
|
44
47
|
[7 /* SILLY */]: "\x1B[90m" /* BRIGHT_BLACK */
|
|
45
48
|
};
|
|
46
49
|
// src/formatters/consoleFormatter.ts
|
|
47
|
-
function formatConsoleMessage(message, logLevel, format, colorsEnabled) {
|
|
50
|
+
function formatConsoleMessage(message, logLevel, metadata, format, colorsEnabled) {
|
|
48
51
|
const now = new Date;
|
|
49
52
|
const type = Levels[logLevel];
|
|
50
53
|
const utcFormats = {
|
|
@@ -61,22 +64,36 @@ function formatConsoleMessage(message, logLevel, format, colorsEnabled) {
|
|
|
61
64
|
"{date-local}": now.toLocaleDateString("sv-SE"),
|
|
62
65
|
"{full-local}": now.toString()
|
|
63
66
|
};
|
|
67
|
+
const metadataFormats = {};
|
|
68
|
+
if (metadata) {
|
|
69
|
+
metadataFormats["{metadata}"] = JSON.stringify(metadata);
|
|
70
|
+
metadataFormats["{metadata-ml}"] = JSON.stringify(metadata, null, 2);
|
|
71
|
+
if (colorsEnabled) {
|
|
72
|
+
const color = LevelColors[logLevel];
|
|
73
|
+
const colorize = (text) => color + text + "\x1B[0m" /* RESET */;
|
|
74
|
+
for (const key in metadataFormats) {
|
|
75
|
+
metadataFormats[key] = colorize(metadataFormats[key]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
format = format.replace(/{metadata(-ml)?}/g, "");
|
|
80
|
+
}
|
|
64
81
|
let coloredType = type;
|
|
65
82
|
let coloredMessage = message;
|
|
66
83
|
if (colorsEnabled) {
|
|
67
84
|
const color = LevelColors[logLevel];
|
|
68
85
|
const colorize = (text) => "\x1B[90m" /* BRIGHT_BLACK */ + text + "\x1B[0m" /* RESET */;
|
|
69
|
-
|
|
86
|
+
for (const key in utcFormats) {
|
|
70
87
|
utcFormats[key] = colorize(utcFormats[key]);
|
|
71
|
-
}
|
|
72
|
-
|
|
88
|
+
}
|
|
89
|
+
for (const key in localFormats) {
|
|
73
90
|
localFormats[key] = colorize(localFormats[key]);
|
|
74
|
-
}
|
|
91
|
+
}
|
|
75
92
|
coloredType = "\x1B[1m" /* BOLD */ + color + type + "\x1B[0m" /* RESET */;
|
|
76
93
|
coloredMessage = color + message + "\x1B[0m" /* RESET */;
|
|
77
94
|
}
|
|
78
95
|
let output = format;
|
|
79
|
-
const allFormats = { ...utcFormats, ...localFormats };
|
|
96
|
+
const allFormats = { ...utcFormats, ...localFormats, ...metadataFormats };
|
|
80
97
|
for (const [placeholder, value] of Object.entries(allFormats)) {
|
|
81
98
|
output = output.replace(new RegExp(placeholder, "g"), value);
|
|
82
99
|
}
|
|
@@ -87,12 +104,12 @@ function formatConsoleMessage(message, logLevel, format, colorsEnabled) {
|
|
|
87
104
|
class ConsoleTransport {
|
|
88
105
|
format;
|
|
89
106
|
colors;
|
|
90
|
-
constructor(format = "[{datetime-local}] {type} {message}", colors = true) {
|
|
107
|
+
constructor(format = "[{datetime-local}] {type} {message} {metadata}", colors = true) {
|
|
91
108
|
this.format = format;
|
|
92
109
|
this.colors = colors;
|
|
93
110
|
}
|
|
94
111
|
log(entry) {
|
|
95
|
-
console.info(formatConsoleMessage(entry.message, entry.level, this.format, this.colors));
|
|
112
|
+
console.info(formatConsoleMessage(entry.message, entry.level, entry.metadata, this.format, this.colors));
|
|
96
113
|
}
|
|
97
114
|
}
|
|
98
115
|
|
|
@@ -324,7 +341,245 @@ class LokiTransport {
|
|
|
324
341
|
}
|
|
325
342
|
}
|
|
326
343
|
}
|
|
344
|
+
// src/formatters/syslogFormatter.ts
|
|
345
|
+
var SYSLOG_SEVERITY = {
|
|
346
|
+
[0 /* ERROR */]: 3,
|
|
347
|
+
[1 /* WARN */]: 4,
|
|
348
|
+
[2 /* AUDIT */]: 5,
|
|
349
|
+
[3 /* INFO */]: 6,
|
|
350
|
+
[4 /* HTTP */]: 6,
|
|
351
|
+
[5 /* DEBUG */]: 7,
|
|
352
|
+
[6 /* VERBOSE */]: 7,
|
|
353
|
+
[7 /* SILLY */]: 7
|
|
354
|
+
};
|
|
355
|
+
function formatRFC3164(entry, facility, appName, pid) {
|
|
356
|
+
const severity = SYSLOG_SEVERITY[entry.level];
|
|
357
|
+
const priority = facility << 3 | severity;
|
|
358
|
+
const timestamp = new Date(entry.timestamp).toLocaleString("en-US", {
|
|
359
|
+
month: "short",
|
|
360
|
+
day: "2-digit",
|
|
361
|
+
hour: "2-digit",
|
|
362
|
+
minute: "2-digit",
|
|
363
|
+
second: "2-digit",
|
|
364
|
+
hour12: false
|
|
365
|
+
}).replace(/,/, "").replace(" at ", " ");
|
|
366
|
+
const hostname = __require("os").hostname();
|
|
367
|
+
const msg = entry.metadata ? `${entry.message} ${JSON.stringify(entry.metadata)}` : entry.message;
|
|
368
|
+
return `<${priority}>${timestamp} ${hostname} ${appName}[${pid}]: ${msg}`;
|
|
369
|
+
}
|
|
370
|
+
function formatRFC5424(entry, facility, appName, pid) {
|
|
371
|
+
const severity = SYSLOG_SEVERITY[entry.level];
|
|
372
|
+
const priority = facility << 3 | severity;
|
|
373
|
+
const timestamp = new Date(entry.timestamp).toISOString();
|
|
374
|
+
const hostname = __require("os").hostname();
|
|
375
|
+
const msgId = "-";
|
|
376
|
+
const structuredData = entry.metadata ? `[example@1 ${Object.entries(entry.metadata).map(([key, val]) => `${key}="${val}"`).join(" ")}]` : "-";
|
|
377
|
+
return `<${priority}>1 ${timestamp} ${hostname} ${appName} ${pid} ${msgId} ${structuredData} ${entry.message}`;
|
|
378
|
+
}
|
|
379
|
+
function formatSyslogMessage(entry, config) {
|
|
380
|
+
const facility = config.facility ?? 1;
|
|
381
|
+
const appName = config.appName ?? "node";
|
|
382
|
+
const pid = config.pid ?? process.pid;
|
|
383
|
+
const protocolVersion = config.protocolVersion ?? 5424;
|
|
384
|
+
return protocolVersion === 3164 ? formatRFC3164(entry, facility, appName, pid) : formatRFC5424(entry, facility, appName, pid);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/transports/syslogTransport.ts
|
|
388
|
+
import { createSocket, Socket } from "dgram";
|
|
389
|
+
import { Socket as NetSocket } from "net";
|
|
390
|
+
import { connect as tlsConnect } from "tls";
|
|
391
|
+
|
|
392
|
+
class SyslogTransport {
|
|
393
|
+
socket = null;
|
|
394
|
+
queue = [];
|
|
395
|
+
isConnecting = false;
|
|
396
|
+
retryCount = 0;
|
|
397
|
+
retryBaseDelay;
|
|
398
|
+
maxQueueSize;
|
|
399
|
+
debug;
|
|
400
|
+
reconnectTimer = null;
|
|
401
|
+
config;
|
|
402
|
+
constructor(config = {}) {
|
|
403
|
+
this.maxQueueSize = config.maxQueueSize ?? 1000;
|
|
404
|
+
this.retryBaseDelay = config.retryBaseDelay ?? 1000;
|
|
405
|
+
this.debug = config.debug ?? false;
|
|
406
|
+
this.config = {
|
|
407
|
+
host: config.host ?? "localhost",
|
|
408
|
+
port: config.port ?? 514,
|
|
409
|
+
protocol: config.protocol ?? "udp",
|
|
410
|
+
facility: config.facility ?? 1,
|
|
411
|
+
appName: config.appName ?? "node",
|
|
412
|
+
pid: config.pid ?? process.pid,
|
|
413
|
+
protocolVersion: config.protocolVersion ?? 5424,
|
|
414
|
+
tlsOptions: config.tlsOptions || {}
|
|
415
|
+
};
|
|
416
|
+
this.initializeSocket();
|
|
417
|
+
}
|
|
418
|
+
initializeSocket() {
|
|
419
|
+
if (this.reconnectTimer) {
|
|
420
|
+
clearTimeout(this.reconnectTimer);
|
|
421
|
+
this.reconnectTimer = null;
|
|
422
|
+
}
|
|
423
|
+
if (this.socket) {
|
|
424
|
+
this.socket.removeAllListeners();
|
|
425
|
+
if (!("destroy" in this.socket)) {
|
|
426
|
+
this.socket.close();
|
|
427
|
+
} else {
|
|
428
|
+
this.socket.destroy();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (this.config.protocol === "udp") {
|
|
432
|
+
this.initializeUdpSocket();
|
|
433
|
+
} else if (this.config.protocol === "tcp") {
|
|
434
|
+
this.initializeTcpSocket();
|
|
435
|
+
} else if (this.config.protocol === "tcp-tls") {
|
|
436
|
+
this.initializeTlsSocket();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
initializeUdpSocket() {
|
|
440
|
+
this.socket = createSocket("udp4");
|
|
441
|
+
this.socket.on("error", (err) => {
|
|
442
|
+
if (this.debug)
|
|
443
|
+
console.error("Syslog UDP error:", err);
|
|
444
|
+
this.handleSocketError();
|
|
445
|
+
});
|
|
446
|
+
this.socket.on("close", () => {
|
|
447
|
+
if (this.debug)
|
|
448
|
+
console.log("Syslog UDP socket closed");
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
initializeTcpSocket() {
|
|
452
|
+
this.socket = new NetSocket;
|
|
453
|
+
this.setupTcpSocketEvents();
|
|
454
|
+
this.connectTcpSocket();
|
|
455
|
+
}
|
|
456
|
+
initializeTlsSocket() {
|
|
457
|
+
const tlsOptions = {
|
|
458
|
+
host: this.config.host,
|
|
459
|
+
port: this.config.port,
|
|
460
|
+
...this.config.tlsOptions
|
|
461
|
+
};
|
|
462
|
+
this.socket = tlsConnect(tlsOptions, () => {
|
|
463
|
+
if (this.debug)
|
|
464
|
+
console.log("Syslog TLS connection established");
|
|
465
|
+
this.retryCount = 0;
|
|
466
|
+
this.flushQueue();
|
|
467
|
+
});
|
|
468
|
+
this.setupTcpSocketEvents();
|
|
469
|
+
}
|
|
470
|
+
setupTcpSocketEvents() {
|
|
471
|
+
if (!this.socket)
|
|
472
|
+
return;
|
|
473
|
+
this.socket.on("error", (err) => {
|
|
474
|
+
if (this.debug)
|
|
475
|
+
console.error("Syslog TCP/TLS error:", err);
|
|
476
|
+
this.handleSocketError();
|
|
477
|
+
});
|
|
478
|
+
this.socket.on("close", () => {
|
|
479
|
+
if (this.debug)
|
|
480
|
+
console.log("Syslog TCP/TLS connection closed");
|
|
481
|
+
this.handleSocketError();
|
|
482
|
+
});
|
|
483
|
+
this.socket.on("end", () => {
|
|
484
|
+
if (this.debug)
|
|
485
|
+
console.log("Syslog TCP/TLS connection ended");
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
connectTcpSocket() {
|
|
489
|
+
if (this.isConnecting || !(this.socket instanceof NetSocket))
|
|
490
|
+
return;
|
|
491
|
+
this.isConnecting = true;
|
|
492
|
+
this.socket.connect(this.config.port, this.config.host, () => {
|
|
493
|
+
if (this.debug)
|
|
494
|
+
console.log("Syslog TCP connection established");
|
|
495
|
+
this.isConnecting = false;
|
|
496
|
+
this.retryCount = 0;
|
|
497
|
+
this.flushQueue();
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
handleSocketError() {
|
|
501
|
+
if (this.reconnectTimer)
|
|
502
|
+
return;
|
|
503
|
+
this.socket = null;
|
|
504
|
+
this.isConnecting = false;
|
|
505
|
+
this.retryCount++;
|
|
506
|
+
const delay = Math.min(this.retryBaseDelay * Math.pow(2, this.retryCount - 1), 30000);
|
|
507
|
+
if (this.debug)
|
|
508
|
+
console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.retryCount})`);
|
|
509
|
+
this.reconnectTimer = setTimeout(() => {
|
|
510
|
+
this.initializeSocket();
|
|
511
|
+
}, delay);
|
|
512
|
+
}
|
|
513
|
+
flushQueue() {
|
|
514
|
+
if (!this.socket || this.queue.length === 0)
|
|
515
|
+
return;
|
|
516
|
+
while (this.queue.length > 0) {
|
|
517
|
+
const message = this.queue.shift();
|
|
518
|
+
if (message) {
|
|
519
|
+
this.sendMessage(message);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
sendMessage(message) {
|
|
524
|
+
if (!this.socket) {
|
|
525
|
+
this.queue.unshift(message);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
if (this.socket instanceof Socket) {
|
|
530
|
+
this.socket.send(message, this.config.port, this.config.host, (err) => {
|
|
531
|
+
if (err && this.debug)
|
|
532
|
+
console.error("Syslog UDP send error:", err);
|
|
533
|
+
});
|
|
534
|
+
} else {
|
|
535
|
+
this.socket.write(message + `
|
|
536
|
+
`, (err) => {
|
|
537
|
+
if (err && this.debug)
|
|
538
|
+
console.error("Syslog TCP/TLS send error:", err);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
} catch (err) {
|
|
542
|
+
if (this.debug)
|
|
543
|
+
console.error("Syslog send error:", err);
|
|
544
|
+
this.queue.unshift(message);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
log(entry) {
|
|
548
|
+
const message = formatSyslogMessage(entry, this.config);
|
|
549
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
550
|
+
if (this.debug)
|
|
551
|
+
console.warn("Syslog queue full - dropping oldest message");
|
|
552
|
+
this.queue.shift();
|
|
553
|
+
}
|
|
554
|
+
this.queue.push(message);
|
|
555
|
+
if (this.socket && !this.isConnecting) {
|
|
556
|
+
this.flushQueue();
|
|
557
|
+
} else if (!this.socket && !this.isConnecting) {}
|
|
558
|
+
}
|
|
559
|
+
close() {
|
|
560
|
+
return new Promise((resolve) => {
|
|
561
|
+
if (this.reconnectTimer) {
|
|
562
|
+
clearTimeout(this.reconnectTimer);
|
|
563
|
+
this.reconnectTimer = null;
|
|
564
|
+
}
|
|
565
|
+
if (!this.socket) {
|
|
566
|
+
resolve();
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
const handleClose = () => {
|
|
570
|
+
resolve();
|
|
571
|
+
};
|
|
572
|
+
if ("destroy" in this.socket) {
|
|
573
|
+
this.socket.destroy();
|
|
574
|
+
process.nextTick(handleClose);
|
|
575
|
+
} else {
|
|
576
|
+
this.socket.close(handleClose);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
327
581
|
export {
|
|
582
|
+
SyslogTransport,
|
|
328
583
|
NDJsonTransport,
|
|
329
584
|
LokiTransport,
|
|
330
585
|
Logger,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rabbit-company/logger",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.0",
|
|
4
4
|
"description": "A simple and lightweight logger",
|
|
5
|
-
"main": "./module/
|
|
5
|
+
"main": "./module/logger.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/Rabbit-Company/Logger-JS",
|
|
8
8
|
"funding": "https://rabbit-company.com/donation",
|
|
@@ -35,9 +35,8 @@
|
|
|
35
35
|
"loki"
|
|
36
36
|
],
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/bun": "
|
|
39
|
-
"bun-plugin-dts": "^0.3.0"
|
|
40
|
-
"@rabbit-company/logger": "^4.0.0"
|
|
38
|
+
"@types/bun": "latest",
|
|
39
|
+
"bun-plugin-dts": "^0.3.0"
|
|
41
40
|
},
|
|
42
41
|
"peerDependencies": {
|
|
43
42
|
"typescript": "^5.5.4"
|