2022-07-07 02:18:27 +02:00
|
|
|
const date = require('./libs/dateAndTime')
|
2021-10-01 01:52:32 +02:00
|
|
|
const { LogLevel } = require('./utils/constants')
|
2024-09-11 18:58:30 +02:00
|
|
|
const util = require('util')
|
2021-08-18 00:01:11 +02:00
|
|
|
|
|
|
|
class Logger {
|
|
|
|
constructor() {
|
2024-02-15 23:46:19 +01:00
|
|
|
/** @type {import('./managers/LogManager')} */
|
|
|
|
this.logManager = null
|
|
|
|
|
2023-07-14 21:50:37 +02:00
|
|
|
this.isDev = process.env.NODE_ENV !== 'production'
|
2024-05-19 21:40:46 +02:00
|
|
|
|
2023-07-14 21:50:37 +02:00
|
|
|
this.logLevel = !this.isDev ? LogLevel.INFO : LogLevel.TRACE
|
2021-10-01 01:52:32 +02:00
|
|
|
this.socketListeners = []
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
2024-02-15 23:46:19 +01:00
|
|
|
/**
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2021-08-18 00:01:11 +02:00
|
|
|
get timestamp() {
|
2023-11-19 20:36:04 +01:00
|
|
|
return date.format(new Date(), 'YYYY-MM-DD HH:mm:ss.SSS')
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
2021-10-01 01:52:32 +02:00
|
|
|
get levelString() {
|
|
|
|
for (const key in LogLevel) {
|
|
|
|
if (LogLevel[key] === this.logLevel) {
|
|
|
|
return key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 'UNKNOWN'
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:46:19 +01:00
|
|
|
/**
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2023-01-05 19:06:36 +01:00
|
|
|
get source() {
|
2024-08-14 23:36:10 +02:00
|
|
|
const regex = global.isWin ? /^.*\\([^\\:]*:[0-9]*):[0-9]*\)*/ : /^.*\/([^/:]*:[0-9]*):[0-9]*\)*/
|
|
|
|
return Error().stack.split('\n')[3].replace(regex, '$1')
|
2023-01-05 19:06:36 +01:00
|
|
|
}
|
|
|
|
|
2021-10-01 01:52:32 +02:00
|
|
|
getLogLevelString(level) {
|
|
|
|
for (const key in LogLevel) {
|
|
|
|
if (LogLevel[key] === level) {
|
|
|
|
return key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 'UNKNOWN'
|
|
|
|
}
|
|
|
|
|
|
|
|
addSocketListener(socket, level) {
|
2024-05-19 21:40:46 +02:00
|
|
|
var index = this.socketListeners.findIndex((s) => s.id === socket.id)
|
2021-10-01 01:52:32 +02:00
|
|
|
if (index >= 0) {
|
|
|
|
this.socketListeners.splice(index, 1, {
|
|
|
|
id: socket.id,
|
|
|
|
socket,
|
|
|
|
level
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
this.socketListeners.push({
|
|
|
|
id: socket.id,
|
|
|
|
socket,
|
|
|
|
level
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
removeSocketListener(socketId) {
|
2024-05-19 21:40:46 +02:00
|
|
|
this.socketListeners = this.socketListeners.filter((s) => s.id !== socketId)
|
2021-10-01 01:52:32 +02:00
|
|
|
}
|
|
|
|
|
2024-02-15 23:46:19 +01:00
|
|
|
/**
|
Fix log source in log file
The logger should include a source containing the location where the
logger was called. This works well for logging to `stdout`. Unfortunately,
the file logs contain the locations where the file logging is called
inside of the logger. This is not helpful:
```
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] Processed db data file with 1 entities","levelName":"INFO","level":2}
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] Finished loading db data with 2 entities","levelName":"INFO","level":2}
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] 2 settings loaded","levelName":"INFO","level":2}
```
This patch fixes the issue, ensureing that the actual source location
will be logged:
```
{"timestamp":"2024-02-29 18:12:59.832","source":"DailyLog.js:132","message":"[DailyLog] 2024-02-29: Loaded 20 Logs","levelName":"DEBUG","level":1}
{"timestamp":"2024-02-29 18:12:59.638","source":"Server.js:172","message":"=== Starting Server ===","levelName":"INFO","level":2}
{"timestamp":"2024-02-29 18:12:59.638","source":"Server.js:103","message":"[Server] Init v2.8.0","levelName":"INFO","level":2}
```
2024-02-29 18:16:29 +01:00
|
|
|
*
|
|
|
|
* @param {number} level
|
2024-09-11 18:58:30 +02:00
|
|
|
* @param {string} levelName
|
Fix log source in log file
The logger should include a source containing the location where the
logger was called. This works well for logging to `stdout`. Unfortunately,
the file logs contain the locations where the file logging is called
inside of the logger. This is not helpful:
```
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] Processed db data file with 1 entities","levelName":"INFO","level":2}
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] Finished loading db data with 2 entities","levelName":"INFO","level":2}
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] 2 settings loaded","levelName":"INFO","level":2}
```
This patch fixes the issue, ensureing that the actual source location
will be logged:
```
{"timestamp":"2024-02-29 18:12:59.832","source":"DailyLog.js:132","message":"[DailyLog] 2024-02-29: Loaded 20 Logs","levelName":"DEBUG","level":1}
{"timestamp":"2024-02-29 18:12:59.638","source":"Server.js:172","message":"=== Starting Server ===","levelName":"INFO","level":2}
{"timestamp":"2024-02-29 18:12:59.638","source":"Server.js:103","message":"[Server] Init v2.8.0","levelName":"INFO","level":2}
```
2024-02-29 18:16:29 +01:00
|
|
|
* @param {string[]} args
|
|
|
|
* @param {string} src
|
2024-02-15 23:46:19 +01:00
|
|
|
*/
|
2024-09-11 18:58:30 +02:00
|
|
|
async #logToFileAndListeners(level, levelName, args, src) {
|
|
|
|
const expandedArgs = args.map((arg) => (typeof arg !== 'string' ? util.inspect(arg) : arg))
|
2021-10-31 23:55:28 +01:00
|
|
|
const logObj = {
|
|
|
|
timestamp: this.timestamp,
|
Fix log source in log file
The logger should include a source containing the location where the
logger was called. This works well for logging to `stdout`. Unfortunately,
the file logs contain the locations where the file logging is called
inside of the logger. This is not helpful:
```
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] Processed db data file with 1 entities","levelName":"INFO","level":2}
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] Finished loading db data with 2 entities","levelName":"INFO","level":2}
{"timestamp":"2023-11-19 16:35:43","source":"Logger.js:114","message":"[oldDbFiles] 2 settings loaded","levelName":"INFO","level":2}
```
This patch fixes the issue, ensureing that the actual source location
will be logged:
```
{"timestamp":"2024-02-29 18:12:59.832","source":"DailyLog.js:132","message":"[DailyLog] 2024-02-29: Loaded 20 Logs","levelName":"DEBUG","level":1}
{"timestamp":"2024-02-29 18:12:59.638","source":"Server.js:172","message":"=== Starting Server ===","levelName":"INFO","level":2}
{"timestamp":"2024-02-29 18:12:59.638","source":"Server.js:103","message":"[Server] Init v2.8.0","levelName":"INFO","level":2}
```
2024-02-29 18:16:29 +01:00
|
|
|
source: src,
|
2024-09-11 18:58:30 +02:00
|
|
|
message: expandedArgs.join(' '),
|
|
|
|
levelName,
|
2021-10-31 23:55:28 +01:00
|
|
|
level
|
|
|
|
}
|
|
|
|
|
2024-02-15 23:46:19 +01:00
|
|
|
// Emit log to sockets that are listening to log events
|
2021-10-01 01:52:32 +02:00
|
|
|
this.socketListeners.forEach((socketListener) => {
|
2024-09-11 21:05:38 +02:00
|
|
|
if (level >= LogLevel.FATAL || level >= socketListener.level) {
|
2021-10-31 23:55:28 +01:00
|
|
|
socketListener.socket.emit('log', logObj)
|
2021-10-01 01:52:32 +02:00
|
|
|
}
|
|
|
|
})
|
2024-02-15 23:46:19 +01:00
|
|
|
|
|
|
|
// Save log to file
|
2024-09-11 18:58:30 +02:00
|
|
|
if (level >= LogLevel.FATAL || level >= this.logLevel) {
|
2024-03-23 20:56:32 +01:00
|
|
|
await this.logManager?.logToFile(logObj)
|
2024-02-15 23:46:19 +01:00
|
|
|
}
|
2021-10-01 01:52:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
setLogLevel(level) {
|
|
|
|
this.logLevel = level
|
|
|
|
this.debug(`Set Log Level to ${this.levelString}`)
|
|
|
|
}
|
|
|
|
|
2024-09-11 18:58:30 +02:00
|
|
|
static ConsoleMethods = {
|
|
|
|
TRACE: 'trace',
|
|
|
|
DEBUG: 'debug',
|
|
|
|
INFO: 'info',
|
|
|
|
WARN: 'warn',
|
|
|
|
ERROR: 'error',
|
|
|
|
FATAL: 'error',
|
|
|
|
NOTE: 'log'
|
|
|
|
}
|
|
|
|
|
|
|
|
#log(levelName, source, ...args) {
|
|
|
|
const level = LogLevel[levelName]
|
|
|
|
if (level < LogLevel.FATAL && level < this.logLevel) return
|
|
|
|
const consoleMethod = Logger.ConsoleMethods[levelName]
|
|
|
|
console[consoleMethod](`[${this.timestamp}] ${levelName}:`, ...args)
|
|
|
|
this.#logToFileAndListeners(level, levelName, args, source)
|
|
|
|
}
|
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
trace(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('TRACE', this.source, ...args)
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
debug(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('DEBUG', this.source, ...args)
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
info(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('INFO', this.source, ...args)
|
2021-08-26 00:36:54 +02:00
|
|
|
}
|
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
warn(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('WARN', this.source, ...args)
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
error(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('ERROR', this.source, ...args)
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fatal(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('FATAL', this.source, ...args)
|
2021-10-01 01:52:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
note(...args) {
|
2024-09-11 18:58:30 +02:00
|
|
|
this.#log('NOTE', this.source, ...args)
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
}
|
2024-05-19 21:40:46 +02:00
|
|
|
module.exports = new Logger()
|