import { existsSync, mkdirSync } from "node:fs" import { homedir } from "node:os" import { basename, join } from "node:path" import { env } from "node:process" import micromatch from "micromatch" import { createLogger, format, transports, type Logger as WinstonLogger, type LoggerOptions } from "winston" import type TransportStream from "winston-transport" const { combine, label, printf, timestamp } = format /** * Logger function to create a Winston logger instance with custom configuration. * * @param {LoggerOptions | string | undefined} config - Configuration for the logger or a label string. * @returns {WinstonLogger} - A configured Winston logger instance. * @throws {TypeError} - If the configuration parameter is not an object or a string. */ export default function Logger(config: LoggerOptions | string | undefined = "default"): WinstonLogger { switch (typeof config) { case "string": let lbl = config if(lbl === "default") { const url = new URL(import.meta.url) lbl = basename(url.pathname).replace(/\.[jt]sx?$/, "") } return createLogger({ "format": combine( label({ "label": lbl, "message": true }), timestamp(), printf(info => `${info["timestamp"]} ${info.level}: ${info.message}`) ), "level": setLogLevel(lbl), "transports": setTransports(), "exitOnError": false }) case "object": return createLogger(config) default: throw new TypeError(`Configuration parameter is not an object or a string (${typeof config})`) } } function setLogLevel(label: string): string { let level = "info" if(env?.["NODE_DEBUG"]) { const globs = env["NODE_DEBUG"].split(",") globs.some(glob => { if(micromatch.isMatch(label, glob)) { level = "debug" } }) } return level } function setLogDir(): string { const logdir = env?.["NODE_IMERGO_LOGDIR"] || join(homedir(), ".cache", "imergo", "log") if (!existsSync(logdir)) { mkdirSync(logdir, { "recursive": true }) } return logdir } function setLogFile(): string { return env?.["NODE_IMERGO_LOGFILE"] || "imergo.log" } function setTransports() { let trs: Array = [] if (!env?.["NODE_IMERGO_LOGCONSOLE"] || env?.["NODE_IMERGO_LOGCONSOLE"] === "true") { trs.push(new transports.Console({ stderrLevels: [ "debug", "error" ] })) } trs.push(new transports.File({ filename: join(setLogDir(), setLogFile()), maxFiles: 12, maxsize: 1024 * 1024 * 10, tailable: true, zippedArchive: true })) return trs }