@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 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, and Grafana Loki
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, Bun and modern browsers
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
  ```
@@ -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
- * @default "[{datetime-local}] {type} {message}"
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
- Object.keys(utcFormats).forEach((key) => {
86
+ for (const key in utcFormats) {
70
87
  utcFormats[key] = colorize(utcFormats[key]);
71
- });
72
- Object.keys(localFormats).forEach((key) => {
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.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "A simple and lightweight logger",
5
- "main": "./module/index.js",
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": "^1.2.10",
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"