UNPKG

file-stream-rotator

Version:

Automated stream rotation useful for log files

244 lines (242 loc) 9.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const node_stream_1 = require("node:stream"); const fs = require("fs"); const path = require("path"); const enums_1 = require("./enums"); const DefaultOptions_1 = require("./DefaultOptions"); const Rotator_1 = require("./Rotator"); const AuditManager_1 = require("./AuditManager"); const helper_1 = require("./helper"); class FileStreamRotator extends node_stream_1.EventEmitter { // private logWatcher?: FSWatcher constructor(options, debug = false) { var _a, _b; super(); this.config = {}; this.config = this.parseOptions(options); helper_1.Logger.getInstance(options.verbose, debug); this.auditManager = new AuditManager_1.default((_a = this.config.auditSettings) !== null && _a !== void 0 ? _a : DefaultOptions_1.default.auditSettings({}), this); let lastEntry = this.auditManager.config.files.slice(-1).shift(); this.rotator = new Rotator_1.default(((_b = this.config.rotationSettings) !== null && _b !== void 0 ? _b : DefaultOptions_1.default.rotationSettings({})), lastEntry); this.rotate(); // this does not seem to work anymore. // this.on("open", (filename: string) => { // // monitor for file deletion // if (this.config.options?.watch_log) { // console.log(">>> setting up watcher", filename) // this.logWatcher = this.createLogWatcher(filename, this.processWatcherEvents.bind(this)) // } // }) } static getStream(options) { return new FileStreamRotator(options); } parseOptions(options) { var _a, _b, _c, _d; let config = {}; config.options = DefaultOptions_1.default.fileStreamRotatorOptions(options); config.fileOptions = DefaultOptions_1.default.fileOptions((_a = options.file_options) !== null && _a !== void 0 ? _a : {}); let auditSettings = DefaultOptions_1.default.auditSettings({}); if (options.audit_file) { auditSettings.auditFilename = options.audit_file; } if (options.audit_hash_type) { auditSettings.hashType = options.audit_hash_type; } if (options.extension) { auditSettings.extension = options.extension; } if (options.max_logs) { let params = DefaultOptions_1.default.extractParam(options.max_logs); auditSettings.keepSettings = { type: ((_b = params.letter) === null || _b === void 0 ? void 0 : _b.toLowerCase()) == "d" ? enums_1.KeepLogFiles.days : enums_1.KeepLogFiles.fileCount, amount: params.number }; } config.auditSettings = auditSettings; config.rotationSettings = DefaultOptions_1.default.rotationSettings({ filename: options.filename, extension: options.extension }); if (options.date_format && !options.frequency) { config.rotationSettings.frequency = enums_1.Frequency.date; } else { config.rotationSettings.frequency = enums_1.Frequency.none; } if (options.date_format) { config.rotationSettings.format = options.date_format; } config.rotationSettings.utc = (_c = options.utc) !== null && _c !== void 0 ? _c : false; switch (options.frequency) { case "daily": config.rotationSettings.frequency = enums_1.Frequency.daily; break; case "custom": case "date": config.rotationSettings.frequency = enums_1.Frequency.date; break; case "test": config.rotationSettings.frequency = enums_1.Frequency.minutes; config.rotationSettings.amount = 1; break; default: if (options.frequency) { let params = DefaultOptions_1.default.extractParam(options.frequency); if ((_d = params.letter) === null || _d === void 0 ? void 0 : _d.match(/^([mh])$/)) { config.rotationSettings.frequency = params.letter == "h" ? enums_1.Frequency.hours : enums_1.Frequency.minutes; config.rotationSettings.amount = params.number; } } } if (options.size) { let params = DefaultOptions_1.default.extractParam(options.size); switch (params.letter) { case 'k': config.rotationSettings.maxSize = params.number * 1024; break; case 'm': config.rotationSettings.maxSize = params.number * 1024 * 1024; break; case 'g': config.rotationSettings.maxSize = params.number * 1024 * 1024 * 1024; break; } } this.rotator = new Rotator_1.default(config.rotationSettings); let oldFile = this.rotator.getNewFilename(); return config; } rotate(force = false) { var _a; let oldFile = this.currentFile; this.rotator.rotate(force); this.currentFile = this.rotator.getNewFilename(); // oldfile same as new file. do nothing if (this.currentFile == oldFile) { return; } // close old file and watcher if exists. if (this.fs) { // if (this.logWatcher) { // this.logWatcher.close() // } if (((_a = this.config.options) === null || _a === void 0 ? void 0 : _a.end_stream) === true) { this.fs.end(); } else { this.fs.destroy(); } } // add old file to audit if (oldFile) { this.auditManager.addLog(oldFile); } this.createNewLog(this.currentFile); this.emit('new', this.currentFile); this.emit('rotate', oldFile, this.currentFile, force); } createNewLog(filename) { var _a; // create new directory if required (0, helper_1.makeDirectory)(filename); // add mew file tp audit this.auditManager.addLog(filename); // create new file let streamOptions = {}; if (this.config.fileOptions) { streamOptions = this.config.fileOptions; } this.fs = fs.createWriteStream(filename, streamOptions); // setup dependencies: proxy events, emit events this.bubbleEvents(this.fs, filename); // setup symlink if ((_a = this.config.options) === null || _a === void 0 ? void 0 : _a.create_symlink) { this.createCurrentSymLink(filename); } } write(str, encoding) { if (this.fs) { if (this.rotator.shouldRotate()) { this.rotate(); } this.fs.write(str, encoding !== null && encoding !== void 0 ? encoding : "utf8"); this.rotator.addBytes(Buffer.byteLength(str, encoding)); if (this.rotator.hasMaxSizeReached()) { this.rotate(); } } } end(str) { if (this.fs) { this.fs.end(str); this.fs = undefined; } } bubbleEvents(emitter, filename) { emitter.on('close', () => { this.emit('close'); }); emitter.on('finish', () => { this.emit('finish'); }); emitter.on('error', (err) => { this.emit('error', err); }); emitter.on('open', (fd) => { this.emit('open', filename); }); } createCurrentSymLink(logfile) { var _a, _b; if (!logfile) { return; } let symLinkName = (_b = (_a = this.config.options) === null || _a === void 0 ? void 0 : _a.symlink_name) !== null && _b !== void 0 ? _b : "current.log"; let logPath = path.dirname(logfile); let logfileName = path.basename(logfile); let current = logPath + path.sep + symLinkName; try { if (fs.existsSync(current)) { let stats = fs.lstatSync(current); if (stats.isSymbolicLink()) { fs.unlinkSync(current); fs.symlinkSync(logfileName, current); return; } helper_1.Logger.verbose("Could not create symlink file as file with the same name exists: ", current); } else { fs.symlinkSync(logfileName, current); } } catch (err) { helper_1.Logger.verbose("[Could not create symlink file: ", current, ' -> ', logfileName); helper_1.Logger.debug("error creating sym link", current, err); } } /* // does not seem to work anymore private createLogWatcher(logfile: string, processEvent: ((event: fs.WatchEventType, file: string) => void)): FSWatcher | undefined{ if(!logfile) return try { if (!fs.existsSync(logfile)) { Logger.verbose("Watcher error: file does not exist" + logfile); return } let stats = fs.lstatSync(logfile) return fs.watch(logfile, (event, filename) => { processEvent(event, filename) }) }catch(err){ Logger.verbose("Could not add watcher for " + logfile, err); return } } private processWatcherEvents(event: fs.WatchEventType, filename: string) { if (!this.currentFile) { return } if (filename == this.currentFile){ if (event == "rename") { if (this.logWatcher) { this.logWatcher.close() } this.createNewLog(this.currentFile) } } } */ test() { return { config: this.config, rotator: this.rotator }; } } exports.default = FileStreamRotator;