ntlogger 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -5,14 +5,33 @@
5
5
  */
6
6
 
7
7
  /**
8
- * TODO: [Feature] Generic Webhook Plugin for sending logs to any webhook
9
- * TODO: [Feature] Email Plugin for sending logs to an email address
10
- * TODO: [Feature] SMS Mail ID Plugin for sending logs to a phone number
11
- * TODO: [Feature] Allow for custom log levels and colors
8
+ * TODO: [FEATURE] Generic Webhook Plugin for sending logs to any webhook
9
+ * TODO: [FEATURE] Email Plugin for sending logs to an email address
10
+ * TODO: [FEATURE] SMS Mail ID Plugin for sending logs to a phone number
11
+ * TODO: [FEATURE] Allow for custom log levels and colors
12
+ * TODO: [FEATURE] Add a toggle to enable/disable individual plugins
13
+ * TODO: [FEATURE] Attempt automatically parse the environment variables for the config using special flags.
14
+ * This would allow for a more seamless integration with Docker and adjustments to the config without code changes.
15
+ * TODO: [FEATURE] Make `LOG_REPORT_PATH` a build in variable. This variable attempts to find the current file name and directory name for cleaner logs.
16
+ * LOG_REPORT_PATH=(path.basename(path.dirname(__filename)) === process.env.APP_NAME ? '/' : path.basename(path.dirname(__filename)) + '/') + path.basename(__filename, ".js")
17
+ * 2024-08-27 23:45:28 [trace ] [ID: 578060] [APP_NAME routes/instructions]: Logger initiated by routes/instructions with log level trace
18
+ * 2024-08-27 23:45:28 [trace ] [ID: 578060] [APP_NAME routes/create-and-sign]: Logger initiated by routes/create-and-sign with log level trace
19
+ * TODO: [FEATURE] Add a option to pass a banner. For example logger.banner('Project Name') would print a banner with the project name.
20
+ * TODO: [TESTING] Create a comprehensive test suite for all plugins.
21
+ * TODO: [BUG] Syslog plugin has incorrect log levels. (For example, info reports level 3, and reports as 11 in the Syslog server)
12
22
  */
13
23
 
14
24
  /**
15
25
  * Change Log:
26
+ * v2.7.0 - 09/02/2024:
27
+ * [FEATURE] Added a toggle to enable/disable individual plugins. See documentation for more information.
28
+ * [NOTE] Changed how the plugins are loaded. Broken plugins will no longer crash the logger, rather they will be disabled.
29
+ *
30
+ * v2.6.2 - 08/15/2024:
31
+ * [DEPENDENCY] Added pg package for Postgres Plugin
32
+ * [FEATURE] Postgres Plugin
33
+ * a simple mysql rewrite so not even going to make a minor version bump
34
+ *
16
35
  * v2.6.1 - 08/15/2024:
17
36
  * [UPDATED] @sentry/node ^8.25.0 --> ^8.26.0
18
37
  * [UPDATED] @sentry/profiling-node ^8.25.0 --> ^8.26.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntlogger",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/NightSquawk/NightTimeLogger.git"
@@ -32,6 +32,7 @@
32
32
  "dependencies": {
33
33
  "@sentry/node": "^8.26.0",
34
34
  "mysql2": "^3.11.0",
35
+ "pg": "^8.12.0",
35
36
  "winston": "^3.14.2",
36
37
  "winston-transport": "^4.7.1"
37
38
  },
package/plugins/index.js CHANGED
@@ -7,10 +7,12 @@
7
7
  const plugins = {
8
8
  Sentry : require('./sentry'),
9
9
  MySQL : require('./mysql'),
10
+ Postgres: require('./postgres'),
10
11
  Jest : require('./jest'),
11
12
  Syslog : require('./syslog'),
12
13
  Discord : require('./discord'),
13
14
  Teams : require('./teams'),
15
+
14
16
  // WIP SMSMail : require('./smsMail'),
15
17
  };
16
18
 
@@ -37,8 +39,25 @@ function initPlugins(config = {}) {
37
39
  let pluginTransports = [];
38
40
  try {
39
41
  for (let plugin of config) {
40
- if (!plugin.name) {
41
- throw new Error('Plugin name is required');
42
+ try {
43
+ if (!plugin.name) {
44
+ throw new Error('Plugin name is required');
45
+ }
46
+ } catch (nameError) {
47
+ console.error('Error initializing plugins:', nameError.message);
48
+ console.error(nameError.stack);
49
+ continue
50
+ }
51
+
52
+ try {
53
+ if (!plugin.enabled) {
54
+ console.log(`Plugin ${plugin.name} is disabled`);
55
+ continue;
56
+ }
57
+ } catch (enabledError) {
58
+ console.error('Error initializing plugins:', enabledError.message);
59
+ console.error(enabledError.stack);
60
+ continue
42
61
  }
43
62
 
44
63
  try {
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @file /plugins/postgres.js
3
+ * @description Stores logs in PostgresSQL database.
4
+ */
5
+
6
+ const Transport = require('winston-transport');
7
+ const { Pool } = require('pg');
8
+ const levels = require('../lib/levels');
9
+
10
+ let connectionPool = null;
11
+ let tableInitialized = false;
12
+
13
+ class PostgreSQLTransport extends Transport {
14
+ constructor(opts = {}) {
15
+ super(opts);
16
+
17
+ this.name = 'PostgreSQL Transport for NTLogger';
18
+ this.database = opts.database || 'test';
19
+ this.table = opts.table || 'logs';
20
+
21
+ this.logLevel = opts.level ? levels[opts.level] : levels.info;
22
+
23
+ this.poolConfig = {
24
+ host: opts.host || 'localhost',
25
+ port: opts.port || 5432,
26
+ user: opts.user || 'postgres',
27
+ password: opts.password || '',
28
+ database: this.database,
29
+ max: opts.connectionLimit || 10,
30
+ idleTimeoutMillis: opts.idleTimeout || 60000,
31
+ connectionTimeoutMillis: opts.connectionTimeout || 2000,
32
+ };
33
+
34
+ this.init();
35
+ }
36
+
37
+ async init() {
38
+ try {
39
+ if (!connectionPool) {
40
+ connectionPool = new Pool(this.poolConfig);
41
+ }
42
+ this.pool = connectionPool;
43
+ await this.checkLogTable();
44
+ } catch (err) {
45
+ console.error(`Failed to initialize ${this.name}:`, err);
46
+ }
47
+ }
48
+
49
+ async log(info, callback) {
50
+ setImmediate(() => {
51
+ this.emit('logged', info);
52
+ });
53
+
54
+ if (!tableInitialized) {
55
+ await this.checkLogTable();
56
+ }
57
+
58
+ const { level, message, ...meta } = info;
59
+
60
+ if (levels[level] > this.logLevel) {
61
+ callback();
62
+ return;
63
+ }
64
+
65
+ const metaString = JSON.stringify(meta);
66
+ const timestamp = this.formatTimestamp(meta.timeCreated);
67
+
68
+ try {
69
+ await this.pool.query(
70
+ `INSERT INTO ${this.table} (level, message, meta, timestamp) VALUES ($1, $2, $3, $4)`,
71
+ [level, message, metaString, timestamp]
72
+ );
73
+ } catch (err) {
74
+ console.error(`Failed to log message to ${this.name}:`, err);
75
+ }
76
+
77
+ callback();
78
+ }
79
+
80
+ formatTimestamp(timeCreated) {
81
+ if (!timeCreated) {
82
+ return new Date().toISOString().slice(0, 19).replace('T', ' ');
83
+ }
84
+
85
+ const date = new Date();
86
+ const [time, modifier] = timeCreated.split(' ');
87
+ let [hours, minutes, seconds] = time.split(':');
88
+
89
+ if (modifier === 'PM' && hours !== '12') {
90
+ hours = parseInt(hours, 10) + 12;
91
+ }
92
+ if (modifier === 'AM' && hours === '12') {
93
+ hours = '00';
94
+ }
95
+
96
+ return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${hours}:${minutes}:${seconds}`;
97
+ }
98
+
99
+ async checkLogTable() {
100
+ if (tableInitialized) return;
101
+
102
+ try {
103
+ const result = await this.pool.query(
104
+ `SELECT to_regclass('${this.table}') as tablename`
105
+ );
106
+
107
+ if (!result.rows[0].tablename) {
108
+ await this.pool.query(
109
+ `CREATE TABLE IF NOT EXISTS ${this.table} (
110
+ id SERIAL PRIMARY KEY,
111
+ level VARCHAR(255) NOT NULL,
112
+ message TEXT NOT NULL,
113
+ meta JSONB,
114
+ timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
115
+ )`
116
+ );
117
+ }
118
+
119
+ tableInitialized = true;
120
+ } catch (err) {
121
+ console.error(`Failed to check or create table ${this.table}:`, err);
122
+ }
123
+ }
124
+
125
+ isActive() {
126
+ return this.pool && !this.pool.ended;
127
+ }
128
+
129
+ async close() {
130
+ if (connectionPool) {
131
+ await connectionPool.end();
132
+ connectionPool = null;
133
+ console.log('Closed PostgreSQL connection pool.');
134
+ }
135
+ }
136
+ }
137
+
138
+ module.exports = {
139
+ transport: PostgreSQLTransport,
140
+ };