diff --git a/src/Defaults.ts b/src/Defaults.ts index 31b495c..3d85dae 100644 --- a/src/Defaults.ts +++ b/src/Defaults.ts @@ -43,6 +43,7 @@ const customStreams: string[] = []; const customTypes: string[] = []; const guard = '_logger'; const webhook: { url?: string, id?: string, token?: string } = {}; +const pruneDays = 3; type LogLevelType = { debug: LogLevel, @@ -82,6 +83,7 @@ export type LoggerMasterOptions = SharedOptionsType & { customColours?: { [key: string]: number | string }, broadcastLevel?: number, webhook?: WebhookClientOptions, + pruneDays?: number } const MasterOptions: LoggerMasterOptions = { @@ -93,6 +95,7 @@ const MasterOptions: LoggerMasterOptions = { customColours, broadcastLevel: 4, webhook, + pruneDays }; export type LoggerClientOptions = SharedOptionsType; diff --git a/src/MasterLogger.ts b/src/MasterLogger.ts index bb053ac..4a6496d 100644 --- a/src/MasterLogger.ts +++ b/src/MasterLogger.ts @@ -49,6 +49,8 @@ class MasterLogger implements Logger #rotationFreq: number; #startDay: number; #rotateTO: NodeJS.Timeout; + #pruneInterval: NodeJS.Timer; + #pruneDays: number; constructor (config = Defaults.MasterOptions) { @@ -56,7 +58,7 @@ class MasterLogger implements Logger const { directory, customTypes = [], customStreams = [], customTypeMapping, customColours, guard, fileRotationFreq, logLevel, logLevelMapping, - webhook, broadcastLevel + webhook, broadcastLevel, pruneDays } = { ...Defaults.MasterOptions, ...config }; if (!directory) @@ -64,6 +66,7 @@ class MasterLogger implements Logger this.#directory = path.resolve(directory); if (!fs.existsSync(this.#directory)) fs.mkdirSync(this.#directory, { recursive: true }); + this.#_broadcastLevel = broadcastLevel as number; this.#_logLevel = logLevel as number; this.#_logLevelMapping = { ...Defaults.MasterOptions.logLevelMapping, ...logLevelMapping }; @@ -102,7 +105,7 @@ class MasterLogger implements Logger this.#streamTypes = [ ...customStreams, 'error', 'default' ]; this.#streamTypeMapping = { ...Defaults.TypeStream, ...customTypeMapping }; - this.#writeStreams = this.#streamTypes.reduce((acc, type) => + this.#writeStreams = this.#streamTypes.reduce((acc, type) => { acc[type] = this.loadFile(type); return acc; @@ -112,8 +115,11 @@ class MasterLogger implements Logger this.#rotationFreq = fileRotationFreq as number * DAY; this.#startDay = Math.floor(Date.now() / this.#rotationFreq) * this.#rotationFreq; this.#rotateTO = setTimeout(this.rotateLogFiles.bind(this), this.#startDay + this.#rotationFreq - Date.now()); + this.#pruneDays = pruneDays as number; + this.#pruneInterval = setInterval(this.pruneLogFiles.bind(this), 60 * 60 * 1000); + this.pruneLogFiles(); - if (webhook && webhook.url) + if (webhook && webhook.url) { this.#webhook = new DiscordWebhook(webhook); this.#webhook.fetch(); @@ -121,6 +127,16 @@ class MasterLogger implements Logger } + close () + { + clearInterval(this.#pruneInterval); + + const streams = Object.keys(this.#writeStreams); + for (const type of streams) + this.#writeStreams[type].end(); + clearTimeout(this.#rotateTO); + } + get guard () { return this.#_guard; @@ -136,6 +152,23 @@ class MasterLogger implements Logger return Object.keys(this.#_logLevelMapping); } + pruneLogFiles () + { + const directory = fs.readdirSync(this.#directory, { withFileTypes: true }); + const now = Date.now(); + const limit = this.#pruneDays * 24 * 60 * 60 * 1000; + for (const entry of directory) + { + if (!entry.isFile() || !(/\d{4}-\d{2}-\d{2}-[a-z]+\.log/u).test(entry.name)) + continue; + const [ year, month, day ] = entry.name.split('-'); + const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)).getTime(); + if ((now - date) < limit) + continue; + fs.unlinkSync(path.join(this.#directory, entry.name)); + } + } + setLogLevel (level: number) { if (LogLevel[level] in this.#_logLevelMapping)