@rabbit-company/logger 5.2.0 → 5.3.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 📦
@@ -162,6 +162,37 @@ const logger = new Logger({
162
162
  });
163
163
  ```
164
164
 
165
+ ### Syslog Transport
166
+
167
+ ```js
168
+ import { SyslogTransport } from "@rabbit-company/logger";
169
+
170
+ const syslogTransport = new SyslogTransport({
171
+ host: "syslog.example.com",
172
+ port: 514,
173
+ protocol: "udp", // 'udp', 'tcp', or 'tcp-tls'
174
+ facility: 16, // local0 facility
175
+ appName: "my-app",
176
+ protocolVersion: 5424, // 3164 (BSD) or 5424 (modern)
177
+ tlsOptions: {
178
+ ca: fs.readFileSync("ca.pem"),
179
+ rejectUnauthorized: true,
180
+ },
181
+ maxQueueSize: 2000, // Max queued messages during outages
182
+ debug: true, // Log connection status
183
+ });
184
+
185
+ const logger = new Logger({
186
+ transports: [syslogTransport],
187
+ });
188
+
189
+ // Features:
190
+ // - Automatic reconnection with exponential backoff
191
+ // - Message queuing during network issues
192
+ // - Supports UDP, TCP, and TLS encryption
193
+ // - Compliant with RFC 3164 and RFC 5424
194
+ ```
195
+
165
196
  ## API Reference 📚
166
197
 
167
198
  Full API documentation is available in the [TypeScript definitions](https://github.com/Rabbit-Company/Logger-JS/blob/main/src/types.ts).
@@ -322,6 +322,155 @@ export interface LokiStream {
322
322
  ]
323
323
  ];
324
324
  }
325
+ /**
326
+ * Configuration options for Syslog transport
327
+ * @interface SyslogConfig
328
+ * @description Defines the configuration parameters for establishing a connection
329
+ * to a syslog server and customizing log message formatting.
330
+ *
331
+ * @example
332
+ * // Basic UDP configuration
333
+ * const config: SyslogConfig = {
334
+ * host: 'logs.example.com',
335
+ * port: 514,
336
+ * protocol: 'udp'
337
+ * };
338
+ *
339
+ * @example
340
+ * // Secure TCP with TLS configuration
341
+ * const secureConfig: SyslogConfig = {
342
+ * host: 'secure-logs.example.com',
343
+ * port: 6514,
344
+ * protocol: 'tcp-tls',
345
+ * tlsOptions: {
346
+ * ca: fs.readFileSync('ca.pem'),
347
+ * cert: fs.readFileSync('client-cert.pem'),
348
+ * key: fs.readFileSync('client-key.pem'),
349
+ * rejectUnauthorized: true
350
+ * },
351
+ * appName: 'my-service',
352
+ * facility: 16 // local0
353
+ * };
354
+ */
355
+ export interface SyslogConfig {
356
+ /**
357
+ * Syslog server hostname or IP address
358
+ * @type {string}
359
+ * @default 'localhost'
360
+ * @example 'logs.example.com'
361
+ * @example '192.168.1.100'
362
+ */
363
+ host?: string;
364
+ /**
365
+ * Syslog server port number
366
+ * @type {number}
367
+ * @default 514
368
+ * @example 514 // Standard syslog port
369
+ * @example 6514 // Common port for syslog over TLS
370
+ */
371
+ port?: number;
372
+ /**
373
+ * Network protocol to use for syslog transmission
374
+ * @type {'udp' | 'tcp' | 'tcp-tls'}
375
+ * @default 'udp'
376
+ * @description
377
+ * - 'udp': Unreliable but fast (RFC 3164 compatible)
378
+ * - 'tcp': Reliable connection (RFC 6587)
379
+ * - 'tcp-tls': Encrypted connection (RFC 5425)
380
+ */
381
+ protocol?: "udp" | "tcp" | "tcp-tls";
382
+ /**
383
+ * Syslog facility code
384
+ * @type {number}
385
+ * @range 0-23
386
+ * @default 1 // USER
387
+ * @description
388
+ * Standard syslog facilities:
389
+ * - 0: kern - Kernel messages
390
+ * - 1: user - User-level messages
391
+ * - 2: mail - Mail system
392
+ * - 3: daemon - System daemons
393
+ * - 4: auth - Security/authentication
394
+ * - 5: syslog - Internal syslog messages
395
+ * - 6: lpr - Line printer subsystem
396
+ * - 7: news - Network news subsystem
397
+ * - 8: uucp - UUCP subsystem
398
+ * - 9: cron - Clock daemon
399
+ * - 10: authpriv - Security/authentication
400
+ * - 11: ftp - FTP daemon
401
+ * - 16-23: local0-local7 - Locally used facilities
402
+ */
403
+ facility?: number;
404
+ /**
405
+ * Application name identifier included in syslog messages
406
+ * @type {string}
407
+ * @default 'node'
408
+ * @description
409
+ * Should be a short string (typically <= 32 chars) identifying the application.
410
+ * @example 'auth-service'
411
+ * @example 'payment-processor'
412
+ */
413
+ appName?: string;
414
+ /**
415
+ * Process ID included in syslog messages
416
+ * @type {number}
417
+ * @default process.pid
418
+ * @description
419
+ * Used to identify the specific process generating the log message.
420
+ */
421
+ pid?: number;
422
+ /**
423
+ * Syslog protocol version specification
424
+ * @type {3164 | 5424}
425
+ * @default 5424
426
+ * @description
427
+ * - 3164: Traditional BSD syslog format (RFC 3164)
428
+ * - 5424: Modern structured syslog format (RFC 5424)
429
+ */
430
+ protocolVersion?: 3164 | 5424;
431
+ /**
432
+ * TLS configuration options for secure connections
433
+ * @type {Object}
434
+ * @description Required when protocol is 'tcp-tls'
435
+ * @property {string} [ca] - PEM encoded CA certificate
436
+ * @property {string} [cert] - PEM encoded client certificate
437
+ * @property {string} [key] - PEM encoded client private key
438
+ * @property {boolean} [rejectUnauthorized=true] - Verify server certificate
439
+ */
440
+ tlsOptions?: {
441
+ /** CA certificate */
442
+ ca?: string;
443
+ /** Client certificate */
444
+ cert?: string;
445
+ /** Client private key */
446
+ key?: string;
447
+ /** Whether to reject unauthorized certificates (default: true) */
448
+ rejectUnauthorized?: boolean;
449
+ };
450
+ /**
451
+ * Maximum number of log messages to buffer in memory
452
+ * @type {number}
453
+ * @default 1000
454
+ * @description
455
+ * When the queue reaches this size, oldest messages will be dropped.
456
+ * Set to 0 for unlimited (not recommended in production).
457
+ */
458
+ maxQueueSize?: number;
459
+ /**
460
+ * Initial retry delay in milliseconds (exponential backoff base)
461
+ * @description Delay doubles with each retry up to maximum 30s
462
+ * @default 1000
463
+ */
464
+ retryBaseDelay?: number;
465
+ /**
466
+ * Enable debug output for transport operations
467
+ * @type {boolean}
468
+ * @default false
469
+ * @description
470
+ * When true, outputs connection status and error details to console.
471
+ */
472
+ debug?: boolean;
473
+ }
325
474
  /**
326
475
  * Main Logger class that handles all logging functionality.
327
476
  *
@@ -755,5 +904,118 @@ export declare class LokiTransport implements Transport {
755
904
  */
756
905
  private sendBatch;
757
906
  }
907
+ /**
908
+ * Syslog transport implementation for the logger library
909
+ * @class SyslogTransport
910
+ * @implements {Transport}
911
+ * @description A robust syslog client that supports UDP, TCP, and TLS-encrypted TCP connections
912
+ * with automatic reconnection and message queuing capabilities.
913
+ *
914
+ * @example
915
+ * // Basic UDP configuration
916
+ * const transport = new SyslogTransport({
917
+ * host: 'logs.example.com',
918
+ * port: 514,
919
+ * protocol: 'udp'
920
+ * });
921
+ *
922
+ * @example
923
+ * // Secure TLS configuration
924
+ * const secureTransport = new SyslogTransport({
925
+ * host: 'secure-logs.example.com',
926
+ * port: 6514,
927
+ * protocol: 'tcp-tls',
928
+ * tlsOptions: {
929
+ * ca: fs.readFileSync('ca.pem'),
930
+ * rejectUnauthorized: true
931
+ * },
932
+ * maxQueueSize: 5000
933
+ * });
934
+ */
935
+ export declare class SyslogTransport implements Transport {
936
+ private socket;
937
+ private queue;
938
+ private isConnecting;
939
+ private retryCount;
940
+ private retryBaseDelay;
941
+ private maxQueueSize;
942
+ private debug;
943
+ private reconnectTimer;
944
+ private config;
945
+ /**
946
+ * Creates a new SyslogTransport instance
947
+ * @constructor
948
+ * @param {SyslogConfig} [config={}] - Configuration options for the transport
949
+ */
950
+ constructor(config?: SyslogConfig);
951
+ /**
952
+ * Initializes the appropriate socket based on configured protocol
953
+ * @private
954
+ * @returns {void}
955
+ */
956
+ private initializeSocket;
957
+ /**
958
+ * Initializes a UDP socket for syslog transmission
959
+ * @private
960
+ * @returns {void}
961
+ */
962
+ private initializeUdpSocket;
963
+ /**
964
+ * Initializes a TCP socket for syslog transmission
965
+ * @private
966
+ * @returns {void}
967
+ */
968
+ private initializeTcpSocket;
969
+ /**
970
+ * Initializes a TLS-secured TCP socket for syslog transmission
971
+ * @private
972
+ * @returns {void}
973
+ */
974
+ private initializeTlsSocket;
975
+ /**
976
+ * Sets up common event handlers for TCP/TLS sockets
977
+ * @private
978
+ * @returns {void}
979
+ */
980
+ private setupTcpSocketEvents;
981
+ /**
982
+ * Establishes a TCP connection to the syslog server
983
+ * @private
984
+ * @returns {void}
985
+ */
986
+ private connectTcpSocket;
987
+ /**
988
+ * Handles socket errors and initiates reconnection if needed
989
+ * @private
990
+ * @returns {void}
991
+ */
992
+ private handleSocketError;
993
+ /**
994
+ * Sends all queued messages to the syslog server
995
+ * @private
996
+ * @returns {void}
997
+ */
998
+ private flushQueue;
999
+ /**
1000
+ * Sends a single message to the syslog server
1001
+ * @private
1002
+ * @param {string} message - The formatted syslog message to send
1003
+ * @returns {void}
1004
+ */
1005
+ private sendMessage;
1006
+ /**
1007
+ * Processes a log entry by formatting and queueing it for transmission
1008
+ * @public
1009
+ * @param {LogEntry} entry - The log entry to process
1010
+ * @returns {void}
1011
+ */
1012
+ log(entry: LogEntry): void;
1013
+ /**
1014
+ * Gracefully closes the transport connection
1015
+ * @public
1016
+ * @returns {Promise<void>} A promise that resolves when the connection is closed
1017
+ */
1018
+ close(): Promise<void>;
1019
+ }
758
1020
 
759
1021
  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) => {
@@ -324,7 +327,245 @@ class LokiTransport {
324
327
  }
325
328
  }
326
329
  }
330
+ // src/formatters/syslogFormatter.ts
331
+ var SYSLOG_SEVERITY = {
332
+ [0 /* ERROR */]: 3,
333
+ [1 /* WARN */]: 4,
334
+ [2 /* AUDIT */]: 5,
335
+ [3 /* INFO */]: 6,
336
+ [4 /* HTTP */]: 6,
337
+ [5 /* DEBUG */]: 7,
338
+ [6 /* VERBOSE */]: 7,
339
+ [7 /* SILLY */]: 7
340
+ };
341
+ function formatRFC3164(entry, facility, appName, pid) {
342
+ const severity = SYSLOG_SEVERITY[entry.level];
343
+ const priority = facility << 3 | severity;
344
+ const timestamp = new Date(entry.timestamp).toLocaleString("en-US", {
345
+ month: "short",
346
+ day: "2-digit",
347
+ hour: "2-digit",
348
+ minute: "2-digit",
349
+ second: "2-digit",
350
+ hour12: false
351
+ }).replace(/,/, "").replace(" at ", " ");
352
+ const hostname = __require("os").hostname();
353
+ const msg = entry.metadata ? `${entry.message} ${JSON.stringify(entry.metadata)}` : entry.message;
354
+ return `<${priority}>${timestamp} ${hostname} ${appName}[${pid}]: ${msg}`;
355
+ }
356
+ function formatRFC5424(entry, facility, appName, pid) {
357
+ const severity = SYSLOG_SEVERITY[entry.level];
358
+ const priority = facility << 3 | severity;
359
+ const timestamp = new Date(entry.timestamp).toISOString();
360
+ const hostname = __require("os").hostname();
361
+ const msgId = "-";
362
+ const structuredData = entry.metadata ? `[example@1 ${Object.entries(entry.metadata).map(([key, val]) => `${key}="${val}"`).join(" ")}]` : "-";
363
+ return `<${priority}>1 ${timestamp} ${hostname} ${appName} ${pid} ${msgId} ${structuredData} ${entry.message}`;
364
+ }
365
+ function formatSyslogMessage(entry, config) {
366
+ const facility = config.facility ?? 1;
367
+ const appName = config.appName ?? "node";
368
+ const pid = config.pid ?? process.pid;
369
+ const protocolVersion = config.protocolVersion ?? 5424;
370
+ return protocolVersion === 3164 ? formatRFC3164(entry, facility, appName, pid) : formatRFC5424(entry, facility, appName, pid);
371
+ }
372
+
373
+ // src/transports/syslogTransport.ts
374
+ import { createSocket, Socket } from "dgram";
375
+ import { Socket as NetSocket } from "net";
376
+ import { connect as tlsConnect } from "tls";
377
+
378
+ class SyslogTransport {
379
+ socket = null;
380
+ queue = [];
381
+ isConnecting = false;
382
+ retryCount = 0;
383
+ retryBaseDelay;
384
+ maxQueueSize;
385
+ debug;
386
+ reconnectTimer = null;
387
+ config;
388
+ constructor(config = {}) {
389
+ this.maxQueueSize = config.maxQueueSize ?? 1000;
390
+ this.retryBaseDelay = config.retryBaseDelay ?? 1000;
391
+ this.debug = config.debug ?? false;
392
+ this.config = {
393
+ host: config.host ?? "localhost",
394
+ port: config.port ?? 514,
395
+ protocol: config.protocol ?? "udp",
396
+ facility: config.facility ?? 1,
397
+ appName: config.appName ?? "node",
398
+ pid: config.pid ?? process.pid,
399
+ protocolVersion: config.protocolVersion ?? 5424,
400
+ tlsOptions: config.tlsOptions || {}
401
+ };
402
+ this.initializeSocket();
403
+ }
404
+ initializeSocket() {
405
+ if (this.reconnectTimer) {
406
+ clearTimeout(this.reconnectTimer);
407
+ this.reconnectTimer = null;
408
+ }
409
+ if (this.socket) {
410
+ this.socket.removeAllListeners();
411
+ if (!("destroy" in this.socket)) {
412
+ this.socket.close();
413
+ } else {
414
+ this.socket.destroy();
415
+ }
416
+ }
417
+ if (this.config.protocol === "udp") {
418
+ this.initializeUdpSocket();
419
+ } else if (this.config.protocol === "tcp") {
420
+ this.initializeTcpSocket();
421
+ } else if (this.config.protocol === "tcp-tls") {
422
+ this.initializeTlsSocket();
423
+ }
424
+ }
425
+ initializeUdpSocket() {
426
+ this.socket = createSocket("udp4");
427
+ this.socket.on("error", (err) => {
428
+ if (this.debug)
429
+ console.error("Syslog UDP error:", err);
430
+ this.handleSocketError();
431
+ });
432
+ this.socket.on("close", () => {
433
+ if (this.debug)
434
+ console.log("Syslog UDP socket closed");
435
+ });
436
+ }
437
+ initializeTcpSocket() {
438
+ this.socket = new NetSocket;
439
+ this.setupTcpSocketEvents();
440
+ this.connectTcpSocket();
441
+ }
442
+ initializeTlsSocket() {
443
+ const tlsOptions = {
444
+ host: this.config.host,
445
+ port: this.config.port,
446
+ ...this.config.tlsOptions
447
+ };
448
+ this.socket = tlsConnect(tlsOptions, () => {
449
+ if (this.debug)
450
+ console.log("Syslog TLS connection established");
451
+ this.retryCount = 0;
452
+ this.flushQueue();
453
+ });
454
+ this.setupTcpSocketEvents();
455
+ }
456
+ setupTcpSocketEvents() {
457
+ if (!this.socket)
458
+ return;
459
+ this.socket.on("error", (err) => {
460
+ if (this.debug)
461
+ console.error("Syslog TCP/TLS error:", err);
462
+ this.handleSocketError();
463
+ });
464
+ this.socket.on("close", () => {
465
+ if (this.debug)
466
+ console.log("Syslog TCP/TLS connection closed");
467
+ this.handleSocketError();
468
+ });
469
+ this.socket.on("end", () => {
470
+ if (this.debug)
471
+ console.log("Syslog TCP/TLS connection ended");
472
+ });
473
+ }
474
+ connectTcpSocket() {
475
+ if (this.isConnecting || !(this.socket instanceof NetSocket))
476
+ return;
477
+ this.isConnecting = true;
478
+ this.socket.connect(this.config.port, this.config.host, () => {
479
+ if (this.debug)
480
+ console.log("Syslog TCP connection established");
481
+ this.isConnecting = false;
482
+ this.retryCount = 0;
483
+ this.flushQueue();
484
+ });
485
+ }
486
+ handleSocketError() {
487
+ if (this.reconnectTimer)
488
+ return;
489
+ this.socket = null;
490
+ this.isConnecting = false;
491
+ this.retryCount++;
492
+ const delay = Math.min(this.retryBaseDelay * Math.pow(2, this.retryCount - 1), 30000);
493
+ if (this.debug)
494
+ console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.retryCount})`);
495
+ this.reconnectTimer = setTimeout(() => {
496
+ this.initializeSocket();
497
+ }, delay);
498
+ }
499
+ flushQueue() {
500
+ if (!this.socket || this.queue.length === 0)
501
+ return;
502
+ while (this.queue.length > 0) {
503
+ const message = this.queue.shift();
504
+ if (message) {
505
+ this.sendMessage(message);
506
+ }
507
+ }
508
+ }
509
+ sendMessage(message) {
510
+ if (!this.socket) {
511
+ this.queue.unshift(message);
512
+ return;
513
+ }
514
+ try {
515
+ if (this.socket instanceof Socket) {
516
+ this.socket.send(message, this.config.port, this.config.host, (err) => {
517
+ if (err && this.debug)
518
+ console.error("Syslog UDP send error:", err);
519
+ });
520
+ } else {
521
+ this.socket.write(message + `
522
+ `, (err) => {
523
+ if (err && this.debug)
524
+ console.error("Syslog TCP/TLS send error:", err);
525
+ });
526
+ }
527
+ } catch (err) {
528
+ if (this.debug)
529
+ console.error("Syslog send error:", err);
530
+ this.queue.unshift(message);
531
+ }
532
+ }
533
+ log(entry) {
534
+ const message = formatSyslogMessage(entry, this.config);
535
+ if (this.queue.length >= this.maxQueueSize) {
536
+ if (this.debug)
537
+ console.warn("Syslog queue full - dropping oldest message");
538
+ this.queue.shift();
539
+ }
540
+ this.queue.push(message);
541
+ if (this.socket && !this.isConnecting) {
542
+ this.flushQueue();
543
+ } else if (!this.socket && !this.isConnecting) {}
544
+ }
545
+ close() {
546
+ return new Promise((resolve) => {
547
+ if (this.reconnectTimer) {
548
+ clearTimeout(this.reconnectTimer);
549
+ this.reconnectTimer = null;
550
+ }
551
+ if (!this.socket) {
552
+ resolve();
553
+ return;
554
+ }
555
+ const handleClose = () => {
556
+ resolve();
557
+ };
558
+ if ("destroy" in this.socket) {
559
+ this.socket.destroy();
560
+ process.nextTick(handleClose);
561
+ } else {
562
+ this.socket.close(handleClose);
563
+ }
564
+ });
565
+ }
566
+ }
327
567
  export {
568
+ SyslogTransport,
328
569
  NDJsonTransport,
329
570
  LokiTransport,
330
571
  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.3.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"