audiobookshelf/server/Logger.js

152 lines
3.4 KiB
JavaScript

const date = require('./libs/dateAndTime')
const { LogLevel } = require('./utils/constants')
const util = require('util')
class Logger {
constructor() {
/** @type {import('./managers/LogManager')} */
this.logManager = null
this.isDev = process.env.NODE_ENV !== 'production'
this.logLevel = !this.isDev ? LogLevel.INFO : LogLevel.TRACE
this.socketListeners = []
}
/**
* @returns {string}
*/
get timestamp() {
return date.format(new Date(), 'YYYY-MM-DD HH:mm:ss.SSS')
}
get levelString() {
for (const key in LogLevel) {
if (LogLevel[key] === this.logLevel) {
return key
}
}
return 'UNKNOWN'
}
/**
* @returns {string}
*/
get source() {
const regex = global.isWin ? /^.*\\([^\\:]*:[0-9]*):[0-9]*\)*/ : /^.*\/([^/:]*:[0-9]*):[0-9]*\)*/
return Error().stack.split('\n')[3].replace(regex, '$1')
}
getLogLevelString(level) {
for (const key in LogLevel) {
if (LogLevel[key] === level) {
return key
}
}
return 'UNKNOWN'
}
addSocketListener(socket, level) {
var index = this.socketListeners.findIndex((s) => s.id === socket.id)
if (index >= 0) {
this.socketListeners.splice(index, 1, {
id: socket.id,
socket,
level
})
} else {
this.socketListeners.push({
id: socket.id,
socket,
level
})
}
}
removeSocketListener(socketId) {
this.socketListeners = this.socketListeners.filter((s) => s.id !== socketId)
}
/**
*
* @param {number} level
* @param {string} levelName
* @param {string[]} args
* @param {string} src
*/
async #logToFileAndListeners(level, levelName, args, src) {
const expandedArgs = args.map((arg) => (typeof arg !== 'string' ? util.inspect(arg) : arg))
const logObj = {
timestamp: this.timestamp,
source: src,
message: expandedArgs.join(' '),
levelName,
level
}
// Emit log to sockets that are listening to log events
this.socketListeners.forEach((socketListener) => {
if (socketListener.level <= level) {
socketListener.socket.emit('log', logObj)
}
})
// Save log to file
if (level >= LogLevel.FATAL || level >= this.logLevel) {
await this.logManager?.logToFile(logObj)
}
}
setLogLevel(level) {
this.logLevel = level
this.debug(`Set Log Level to ${this.levelString}`)
}
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)
}
trace(...args) {
this.#log('TRACE', this.source, ...args)
}
debug(...args) {
this.#log('DEBUG', this.source, ...args)
}
info(...args) {
this.#log('INFO', this.source, ...args)
}
warn(...args) {
this.#log('WARN', this.source, ...args)
}
error(...args) {
this.#log('ERROR', this.source, ...args)
}
fatal(...args) {
this.#log('FATAL', this.source, ...args)
}
note(...args) {
this.#log('NOTE', this.source, ...args)
}
}
module.exports = new Logger()