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 +23 -4
- package/package.json +2 -1
- package/plugins/index.js +21 -2
- package/plugins/postgres.js +140 -0
package/index.js
CHANGED
|
@@ -5,14 +5,33 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* TODO: [
|
|
9
|
-
* TODO: [
|
|
10
|
-
* TODO: [
|
|
11
|
-
* TODO: [
|
|
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.
|
|
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
|
-
|
|
41
|
-
|
|
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
|
+
};
|