ntlogger 2.3.0 → 2.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.
@@ -0,0 +1,175 @@
1
+ /**
2
+ * @file /plugins/mysql.js
3
+ * @description Stores logs in MySQL database.
4
+ */
5
+
6
+ const Transport = require('winston-transport');
7
+ const mysql = require('mysql2/promise');
8
+
9
+ let connectionPool = null;
10
+ let tableInitialized = false;
11
+
12
+ class MySQLTransport extends Transport {
13
+ constructor(opts = {}) {
14
+ super(opts);
15
+
16
+ this.name = 'MySQL Transport for NTLogger';
17
+ this.database = opts.database || 'test';
18
+ this.table = opts.table || 'logs';
19
+
20
+ this.levels = {
21
+ internal: 6,
22
+ trace: 5,
23
+ debug: 4,
24
+ info: 3,
25
+ warn: 2,
26
+ error: 1,
27
+ fatal: 0,
28
+ };
29
+ this.logLevel = opts.level ? this.levels[opts.level] : this.levels.info;
30
+
31
+ this.poolConfig = {
32
+ host: opts.host || 'localhost',
33
+ port: opts.port || 3306,
34
+ user: opts.user || 'root',
35
+ password: opts.password || '',
36
+ database: this.database,
37
+ waitForConnections: opts.waitForConnections || true,
38
+ connectionLimit: opts.connectionLimit || 10,
39
+ queueLimit: opts.queueLimit || 0,
40
+ maxIdle: opts.maxIdle || 10,
41
+ idleTimeout: opts.idleTimeout || 60000,
42
+ enableKeepAlive: opts.enableKeepAlive || true,
43
+ keepAliveInitialDelay: opts.keepAliveInitialDelay || 0,
44
+ };
45
+
46
+ this.init();
47
+ }
48
+
49
+ /**
50
+ * Initializes the MySQL connection pool and checks the log table.
51
+ */
52
+ async init() {
53
+ try {
54
+ if (!connectionPool) {
55
+ connectionPool = mysql.createPool(this.poolConfig);
56
+ }
57
+ this.pool = connectionPool;
58
+ await this.checkLogTable();
59
+ } catch (err) {
60
+ console.error(`Failed to initialize ${this.name}:`, err);
61
+ }
62
+ }
63
+
64
+ async log(info, callback) {
65
+ setImmediate(() => {
66
+ this.emit('logged', info);
67
+ });
68
+
69
+ if (!tableInitialized) {
70
+ await this.checkLogTable();
71
+ }
72
+
73
+ const { level, message, ...meta } = info;
74
+
75
+ if (this.levels[level] > this.logLevel) {
76
+ callback();
77
+ return;
78
+ }
79
+
80
+ const metaString = JSON.stringify(meta);
81
+ const timestamp = this.formatTimestamp(meta.timeCreated);
82
+
83
+ try {
84
+ await this.pool.execute(
85
+ `INSERT INTO ${this.table} (level, message, meta, timestamp) VALUES (?, ?, ?, ?)`,
86
+ [level, message, metaString, timestamp]
87
+ );
88
+ } catch (err) {
89
+ console.error(`Failed to log message to ${this.name}:`, err);
90
+ }
91
+
92
+ callback();
93
+ }
94
+
95
+ /**
96
+ * Formats the timestamp for the MySQL database.
97
+ * @param {string} timeCreated - The time created string.
98
+ * @returns {string} - The formatted timestamp.
99
+ */
100
+ formatTimestamp(timeCreated) {
101
+ if (!timeCreated) {
102
+ return new Date().toISOString().slice(0, 19).replace('T', ' ');
103
+ }
104
+
105
+ const date = new Date();
106
+ const [time, modifier] = timeCreated.split(' ');
107
+ let [hours, minutes, seconds] = time.split(':');
108
+
109
+ if (modifier === 'PM' && hours !== '12') {
110
+ hours = parseInt(hours, 10) + 12;
111
+ }
112
+ if (modifier === 'AM' && hours === '12') {
113
+ hours = '00';
114
+ }
115
+
116
+ return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${hours}:${minutes}:${seconds}`;
117
+ }
118
+
119
+ /**
120
+ * Checks if the log table exists in the MySQL database. If it does not exist, it will create it.
121
+ */
122
+ async checkLogTable() {
123
+ if (tableInitialized) return;
124
+
125
+ try {
126
+ const [rows] = await this.pool.execute(
127
+ `SELECT COUNT(*) AS count
128
+ FROM information_schema.tables
129
+ WHERE table_schema = ?
130
+ AND table_name = ?`,
131
+ [this.database, this.table]
132
+ );
133
+
134
+ if (rows[0].count === 0) {
135
+ await this.pool.execute(
136
+ `CREATE TABLE IF NOT EXISTS ${this.table} (
137
+ id INT AUTO_INCREMENT PRIMARY KEY,
138
+ level VARCHAR(255) NOT NULL,
139
+ message TEXT NOT NULL,
140
+ meta TEXT,
141
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
142
+ )`
143
+ );
144
+ }
145
+
146
+ tableInitialized = true;
147
+ } catch (err) {
148
+ console.error(`Failed to check or create table ${this.table}:`, err);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Checks if the MySQL connection pool is active.
154
+ * @returns {boolean} - True if the connection pool is active, false otherwise.
155
+ */
156
+ isActive() {
157
+ return this.pool && !this.pool.ended;
158
+ }
159
+
160
+ /**
161
+ * Closes the MySQL connection pool.
162
+ * @returns {Promise<void>}
163
+ */
164
+ async close() {
165
+ if (connectionPool) {
166
+ await connectionPool.end();
167
+ connectionPool = null;
168
+ console.log('Closed MySQL connection pool.');
169
+ }
170
+ }
171
+ }
172
+
173
+ module.exports = {
174
+ transport: MySQLTransport,
175
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @file /plugins/sentry.js
3
+ * @description Initializes the Sentry SDK for error tracking and defines a custom Winston transport.
4
+ */
5
+
6
+ const Transport = require('winston-transport');
7
+ const Sentry = require("@sentry/node");
8
+
9
+ /**
10
+ * Custom Winston transport for sending logs to Sentry.
11
+ */
12
+ class SentryTransport extends Transport {
13
+ constructor(opts = {}) {
14
+ super(opts);
15
+
16
+ this.name = 'Sentry Transport for NTLogger';
17
+ this.init(opts);
18
+ }
19
+
20
+ /**
21
+ * Initializes the Sentry SDK with the provided configuration.
22
+ * @param {Object} config - The configuration object for Sentry initialization.
23
+ */
24
+ init(config = {}) {
25
+ try {
26
+ Sentry.init({...config});
27
+ } catch (err) {
28
+ console.error(`Failed to initialize ${this.name}:`, err);
29
+ }
30
+ }
31
+
32
+ log(info, callback) {
33
+ setImmediate(() => {
34
+ this.emit('logged', info);
35
+ });
36
+
37
+ const { level, message, ...meta } = info;
38
+
39
+ if (level === 'error') {
40
+ Sentry.captureException(new Error(message), { extra: meta });
41
+ } else {
42
+ Sentry.captureMessage(message, { level, extra: meta });
43
+ }
44
+
45
+ callback();
46
+ }
47
+ }
48
+
49
+ module.exports = {
50
+ transport: SentryTransport,
51
+ };