simplest-fancy-logger
Version:
A lightweight and easy-to-use logging library for Node.js applications
164 lines (135 loc) • 4.23 kB
text/typescript
import fs from "fs"
import path from "path"
import chalk from "chalk"
const logLevels = {
error: 0,
warn: 1,
info: 2,
debug: 3,
} as const
const levelColors = {
error: "red",
warn: "yellow",
info: "green",
debug: "blue",
} as const
const levelEmojis = {
error: "❌",
warn: "⚠️",
info: "✅",
debug: "🐛",
} as const
type LogLevel = "error" | "warn" | "info" | "debug"
interface SimpleLoggerOptions {
logLevel?: LogLevel
logFile?: string
logFormat?: string
dateFormat?: string
maxFileSize?: number
maxFiles?: number
}
class SimpleLogger {
logLevel: "error" | "warn" | "info" | "debug"
logFile: string | null
logFormat: string
dateFormat: string
maxFileSize: number
maxFiles: number
constructor(options: SimpleLoggerOptions = {}) {
this.logLevel = options.logLevel || "info"
this.logFile = options.logFile || null
this.logFormat =
options.logFormat || "[{level}] [{timestamp}] - {message} - {emoji}"
this.dateFormat = options.dateFormat || "yyyy-mm-dd HH:MM:ss"
this.maxFileSize = options.maxFileSize || 1024 * 1024 * 10 // 10MB
this.maxFiles = options.maxFiles || 5
}
log(level: LogLevel, message: string) {
if (logLevels[level] <= logLevels[this.logLevel]) {
const timestamp = this.formatDate(new Date())
const logMessage = this.formatMessage(timestamp, level, message)
console.log(logMessage)
if (this.logFile) {
this.writeToFile(logMessage)
}
}
}
formatDate(date: Date) {
const year = String(date.getFullYear())
const month = String(date.getMonth() + 1).padStart(2, "0")
const day = String(date.getDate()).padStart(2, "0")
const hours = String(date.getHours()).padStart(2, "0")
const minutes = String(date.getMinutes()).padStart(2, "0")
const seconds = String(date.getSeconds()).padStart(2, "0")
return this.dateFormat
.replace("yyyy", year)
.replace("mm", month)
.replace("dd", day)
.replace("HH", hours)
.replace("MM", minutes)
.replace("ss", seconds)
}
formatMessage(timestamp: string, level: LogLevel, message: string) {
const levelColor = levelColors[level] || "white"
const levelEmoji = levelEmojis[level] || ""
const formattedMessage = this.logFormat
.replace("{timestamp}", chalk.magenta(timestamp))
.replace("{level}", chalk[levelColor](level.toUpperCase()))
.replace("{message}", message)
.replace("{emoji}", levelEmoji)
return formattedMessage
}
writeToFile(logMessage: string) {
if (!this.logFile) {
return
}
const fileSize = fs.existsSync(this.logFile)
? fs.statSync(this.logFile).size
: 0
if (fileSize >= this.maxFileSize) {
this.rotateLogFile()
}
fs.appendFileSync(this.logFile, logMessage + "\n")
}
rotateLogFile() {
if (!this.logFile) {
return
}
const extension = path.extname(this.logFile)
const baseName = path.basename(this.logFile, extension)
const dirName = path.dirname(this.logFile)
// Correcting the deletion and rotation logic
const oldestFile = path.join(
dirName,
`${baseName}.${this.maxFiles}${extension}`
)
if (fs.existsSync(oldestFile)) {
fs.unlinkSync(oldestFile)
}
for (let i = this.maxFiles - 1; i > 0; i--) {
const currentFile = path.join(dirName, `${baseName}.${i}${extension}`)
const newFile = path.join(dirName, `${baseName}.${i + 1}${extension}`)
if (fs.existsSync(currentFile)) {
fs.renameSync(currentFile, newFile)
}
}
const newFirstFile = path.join(dirName, `${baseName}.1${extension}`)
if (fs.existsSync(this.logFile)) {
fs.renameSync(this.logFile, newFirstFile)
}
}
error(message: string) {
this.log("error", message)
}
warn(message: string) {
this.log("warn", message)
}
info(message: string) {
this.log("info", message)
}
debug(message: string) {
this.log("debug", message)
}
}
// module export default
export default SimpleLogger