2025-06-21 00:41:45 +03:00

161 lines
4.1 KiB
JavaScript

const fs = require("fs")
const path = require("path")
class Logger {
constructor(config = {}) {
this.config = {
level: config.level || "info",
saveToFile: config.saveToFile || true,
maxFileSize: config.maxFileSize || "10MB",
maxFiles: config.maxFiles || 5,
logDir: config.logDir || "./logs",
}
this.levels = {
debug: 0,
info: 1,
warn: 2,
error: 3,
success: 1,
}
this.colors = {
debug: "\x1b[36m", // Cyan
info: "\x1b[34m", // Blue
warn: "\x1b[33m", // Yellow
error: "\x1b[31m", // Red
success: "\x1b[32m", // Green
reset: "\x1b[0m",
}
this.initLogDirectory()
}
initLogDirectory() {
if (this.config.saveToFile && !fs.existsSync(this.config.logDir)) {
fs.mkdirSync(this.config.logDir, { recursive: true })
}
}
formatMessage(level, message, data = null) {
const timestamp = new Date().toISOString()
const upperLevel = level.toUpperCase().padEnd(7)
let logMessage = `[${timestamp}] [${upperLevel}] ${message}`
if (data) {
logMessage += ` ${JSON.stringify(data, null, 2)}`
}
return logMessage
}
shouldLog(level) {
return this.levels[level] >= this.levels[this.config.level]
}
writeToFile(message) {
if (!this.config.saveToFile) return
const logFile = path.join(this.config.logDir, `openbrawl-${new Date().toISOString().split("T")[0]}.log`)
try {
fs.appendFileSync(logFile, message + "\n")
this.rotateLogsIfNeeded(logFile)
} catch (error) {
console.error("Failed to write to log file:", error.message)
}
}
rotateLogsIfNeeded(logFile) {
try {
const stats = fs.statSync(logFile)
const maxSize = this.parseSize(this.config.maxFileSize)
if (stats.size > maxSize) {
const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
const rotatedFile = logFile.replace(".log", `-${timestamp}.log`)
fs.renameSync(logFile, rotatedFile)
this.cleanOldLogs()
}
} catch (error) {
console.error("Failed to rotate logs:", error.message)
}
}
cleanOldLogs() {
try {
const files = fs
.readdirSync(this.config.logDir)
.filter((file) => file.startsWith("openbrawl-") && file.endsWith(".log"))
.map((file) => ({
name: file,
path: path.join(this.config.logDir, file),
time: fs.statSync(path.join(this.config.logDir, file)).mtime,
}))
.sort((a, b) => b.time - a.time)
if (files.length > this.config.maxFiles) {
files.slice(this.config.maxFiles).forEach((file) => {
fs.unlinkSync(file.path)
})
}
} catch (error) {
console.error("Failed to clean old logs:", error.message)
}
}
parseSize(sizeStr) {
const units = { B: 1, KB: 1024, MB: 1024 * 1024, GB: 1024 * 1024 * 1024 }
const match = sizeStr.match(/^(\d+)(B|KB|MB|GB)$/i)
if (!match) return 10 * 1024 * 1024 // Default 10MB
return Number.parseInt(match[1]) * units[match[2].toUpperCase()]
}
log(level, message, data = null) {
if (!this.shouldLog(level)) return
const formattedMessage = this.formatMessage(level, message, data)
const coloredMessage = `${this.colors[level]}${formattedMessage}${this.colors.reset}`
console.log(coloredMessage)
this.writeToFile(formattedMessage)
}
debug(message, data = null) {
this.log("debug", message, data)
}
info(message, data = null) {
this.log("info", message, data)
}
warn(message, data = null) {
this.log("warn", message, data)
}
error(message, data = null) {
this.log("error", message, data)
}
success(message, data = null) {
this.log("success", message, data)
}
packet(direction, type, size, data = null) {
const arrow = direction === "in" ? "📨" : "📤"
const message = `${arrow} ${direction.toUpperCase()} | Type: ${type} | Size: ${size} bytes`
this.debug(message, data)
}
connection(status, details = null) {
const emoji = status === "connected" ? "✅" : status === "disconnected" ? "❌" : "🔄"
this.info(`${emoji} Connection ${status}`, details)
}
}
module.exports = Logger