Initial
This commit is contained in:
1
resources/app/node_modules/file-stream-rotator/.nvmrc
generated
vendored
Normal file
1
resources/app/node_modules/file-stream-rotator/.nvmrc
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
4
|
||||
702
resources/app/node_modules/file-stream-rotator/FileStreamRotator.js
generated
vendored
Normal file
702
resources/app/node_modules/file-stream-rotator/FileStreamRotator.js
generated
vendored
Normal file
@@ -0,0 +1,702 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* FileStreamRotator
|
||||
* Copyright(c) 2012-2017 Holiday Extras.
|
||||
* Copyright(c) 2017 Roger C.
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var moment = require('moment');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var EventEmitter = require('events');
|
||||
|
||||
/**
|
||||
* FileStreamRotator:
|
||||
*
|
||||
* Returns a file stream that auto-rotates based on date.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `filename` Filename including full path used by the stream
|
||||
*
|
||||
* - `frequency` How often to rotate. Options are 'daily', 'custom' and 'test'. 'test' rotates every minute.
|
||||
* If frequency is set to none of the above, a YYYYMMDD string will be added to the end of the filename.
|
||||
*
|
||||
* - `verbose` If set, it will log to STDOUT when it rotates files and name of log file. Default is TRUE.
|
||||
*
|
||||
* - `date_format` Format as used in moment.js http://momentjs.com/docs/#/displaying/format/. The result is used to replace
|
||||
* the '%DATE%' placeholder in the filename.
|
||||
* If using 'custom' frequency, it is used to trigger file change when the string representation changes.
|
||||
*
|
||||
* - `size` Max size of the file after which it will rotate. It can be combined with frequency or date format.
|
||||
* The size units are 'k', 'm' and 'g'. Units need to directly follow a number e.g. 1g, 100m, 20k.
|
||||
*
|
||||
* - `max_logs` Max number of logs to keep. If not set, it won't remove past logs. It uses its own log audit file
|
||||
* to keep track of the log files in a json format. It won't delete any file not contained in it.
|
||||
* It can be a number of files or number of days. If using days, add 'd' as the suffix.
|
||||
*
|
||||
* - `audit_file` Location to store the log audit file. If not set, it will be stored in the root of the application.
|
||||
*
|
||||
* - `end_stream` End stream (true) instead of the default behaviour of destroy (false). Set value to true if when writing to the
|
||||
* stream in a loop, if the application terminates or log rotates, data pending to be flushed might be lost.
|
||||
*
|
||||
* - `file_options` An object passed to the stream. This can be used to specify flags, encoding, and mode.
|
||||
* See https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options. Default `{ flags: 'a' }`.
|
||||
*
|
||||
* - `utc` Use UTC time for date in filename. Defaults to 'FALSE'
|
||||
*
|
||||
* - `extension` File extension to be appended to the filename. This is useful when using size restrictions as the rotation
|
||||
* adds a count (1,2,3,4,...) at the end of the filename when the required size is met.
|
||||
*
|
||||
* - `watch_log` Watch the current file being written to and recreate it in case of accidental deletion. Defaults to 'FALSE'
|
||||
*
|
||||
* - `create_symlink` Create a tailable symlink to the current active log file. Defaults to 'FALSE'
|
||||
*
|
||||
* - `symlink_name` Name to use when creating the symbolic link. Defaults to 'current.log'
|
||||
*
|
||||
* - `audit_hash_type` Use specified hashing algorithm for audit. Defaults to 'md5'. Use 'sha256' for FIPS compliance.
|
||||
*
|
||||
* To use with Express / Connect, use as below.
|
||||
*
|
||||
* var rotatingLogStream = require('FileStreamRotator').getStream({filename:"/tmp/test.log", frequency:"daily", verbose: false})
|
||||
* app.use(express.logger({stream: rotatingLogStream, format: "default"}));
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
var FileStreamRotator = {};
|
||||
|
||||
module.exports = FileStreamRotator;
|
||||
|
||||
var staticFrequency = ['daily', 'test', 'm', 'h', 'custom'];
|
||||
var DATE_FORMAT = ('YYYYMMDDHHmm');
|
||||
|
||||
|
||||
/**
|
||||
* Returns frequency metadata for minute/hour rotation
|
||||
* @param type
|
||||
* @param num
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
var _checkNumAndType = function (type, num) {
|
||||
if (typeof num == 'number') {
|
||||
switch (type) {
|
||||
case 'm':
|
||||
if (num < 0 || num > 60) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (num < 0 || num > 24) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return {type: type, digit: num};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns frequency metadata for defined frequency
|
||||
* @param freqType
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
var _checkDailyAndTest = function (freqType) {
|
||||
switch (freqType) {
|
||||
case 'custom':
|
||||
case 'daily':
|
||||
return {type: freqType, digit: undefined};
|
||||
break;
|
||||
case 'test':
|
||||
return {type: freqType, digit: 0};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns frequency metadata
|
||||
* @param frequency
|
||||
* @returns {*}
|
||||
*/
|
||||
FileStreamRotator.getFrequency = function (frequency) {
|
||||
var _f = frequency.toLowerCase().match(/^(\d+)([mh])$/)
|
||||
if(_f){
|
||||
return _checkNumAndType(_f[2], parseInt(_f[1]));
|
||||
}
|
||||
|
||||
var dailyOrTest = _checkDailyAndTest(frequency);
|
||||
if (dailyOrTest) {
|
||||
return dailyOrTest;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number based on the option string
|
||||
* @param size
|
||||
* @returns {Number}
|
||||
*/
|
||||
FileStreamRotator.parseFileSize = function (size) {
|
||||
if(size && typeof size == "string"){
|
||||
var _s = size.toLowerCase().match(/^((?:0\.)?\d+)([kmg])$/);
|
||||
if(_s){
|
||||
switch(_s[2]){
|
||||
case 'k':
|
||||
return _s[1]*1024
|
||||
case 'm':
|
||||
return _s[1]*1024*1024
|
||||
case 'g':
|
||||
return _s[1]*1024*1024*1024
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns date string for a given format / date_format
|
||||
* @param format
|
||||
* @param date_format
|
||||
* @param {boolean} utc
|
||||
* @returns {string}
|
||||
*/
|
||||
FileStreamRotator.getDate = function (format, date_format, utc) {
|
||||
date_format = date_format || DATE_FORMAT;
|
||||
let currentMoment = utc ? moment.utc() : moment().local()
|
||||
if (format && staticFrequency.indexOf(format.type) !== -1) {
|
||||
switch (format.type) {
|
||||
case 'm':
|
||||
var minute = Math.floor(currentMoment.minutes() / format.digit) * format.digit;
|
||||
return currentMoment.minutes(minute).format(date_format);
|
||||
break;
|
||||
case 'h':
|
||||
var hour = Math.floor(currentMoment.hour() / format.digit) * format.digit;
|
||||
return currentMoment.hour(hour).format(date_format);
|
||||
break;
|
||||
case 'daily':
|
||||
case 'custom':
|
||||
case 'test':
|
||||
return currentMoment.format(date_format);
|
||||
}
|
||||
}
|
||||
return currentMoment.format(date_format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read audit json object from disk or return new object or null
|
||||
* @param max_logs
|
||||
* @param audit_file
|
||||
* @param log_file
|
||||
* @returns {Object} auditLogSettings
|
||||
* @property {Object} auditLogSettings.keep
|
||||
* @property {Boolean} auditLogSettings.keep.days
|
||||
* @property {Number} auditLogSettings.keep.amount
|
||||
* @property {String} auditLogSettings.auditLog
|
||||
* @property {Array} auditLogSettings.files
|
||||
* @property {String} auditLogSettings.hashType
|
||||
*/
|
||||
FileStreamRotator.setAuditLog = function (max_logs, audit_file, log_file){
|
||||
var _rtn = null;
|
||||
if(max_logs){
|
||||
var use_days = max_logs.toString().substr(-1);
|
||||
var _num = max_logs.toString().match(/^(\d+)/);
|
||||
|
||||
if(Number(_num[1]) > 0) {
|
||||
var baseLog = path.dirname(log_file.replace(/%DATE%.+/,"_filename"));
|
||||
try{
|
||||
if(audit_file){
|
||||
var full_path = path.resolve(audit_file);
|
||||
_rtn = JSON.parse(fs.readFileSync(full_path, { encoding: 'utf-8' }));
|
||||
}else{
|
||||
var full_path = path.resolve(baseLog + "/" + ".audit.json")
|
||||
_rtn = JSON.parse(fs.readFileSync(full_path, { encoding: 'utf-8' }));
|
||||
}
|
||||
}catch(e){
|
||||
if(e.code !== "ENOENT"){
|
||||
return null;
|
||||
}
|
||||
_rtn = {
|
||||
keep: {
|
||||
days: false,
|
||||
amount: Number(_num[1])
|
||||
},
|
||||
auditLog: audit_file || baseLog + "/" + ".audit.json",
|
||||
files: []
|
||||
};
|
||||
}
|
||||
|
||||
_rtn.keep = {
|
||||
days: use_days === 'd',
|
||||
amount: Number(_num[1])
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
return _rtn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write audit json object to disk
|
||||
* @param {Object} audit
|
||||
* @param {Object} audit.keep
|
||||
* @param {Boolean} audit.keep.days
|
||||
* @param {Number} audit.keep.amount
|
||||
* @param {String} audit.auditLog
|
||||
* @param {Array} audit.files
|
||||
* @param {String} audit.hashType
|
||||
* @param {Boolean} verbose
|
||||
*/
|
||||
FileStreamRotator.writeAuditLog = function(audit, verbose){
|
||||
try{
|
||||
mkDirForFile(audit.auditLog);
|
||||
fs.writeFileSync(audit.auditLog, JSON.stringify(audit,null,4));
|
||||
}catch(e){
|
||||
if (verbose) {
|
||||
console.error(new Date(),"[FileStreamRotator] Failed to store log audit at:", audit.auditLog,"Error:", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes old log file
|
||||
* @param file
|
||||
* @param file.hash
|
||||
* @param file.name
|
||||
* @param file.date
|
||||
* @param file.hashType
|
||||
* @param {Boolean} verbose
|
||||
*/
|
||||
function removeFile(file, verbose){
|
||||
if(file.hash === crypto.createHash(file.hashType).update(file.name + "LOG_FILE" + file.date).digest("hex")){
|
||||
try{
|
||||
if (fs.existsSync(file.name)) {
|
||||
fs.unlinkSync(file.name);
|
||||
}
|
||||
}catch(e){
|
||||
if (verbose) {
|
||||
console.error(new Date(), "[FileStreamRotator] Could not remove old log file: ", file.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create symbolic link to current log file
|
||||
* @param {String} logfile
|
||||
* @param {String} name Name to use for symbolic link
|
||||
* @param {Boolean} verbose
|
||||
*/
|
||||
function createCurrentSymLink(logfile, name, verbose) {
|
||||
let symLinkName = name || "current.log"
|
||||
let logPath = path.dirname(logfile)
|
||||
let logfileName = path.basename(logfile)
|
||||
let current = logPath + "/" + symLinkName
|
||||
try {
|
||||
let stats = fs.lstatSync(current)
|
||||
if(stats.isSymbolicLink()){
|
||||
fs.unlinkSync(current)
|
||||
fs.symlinkSync(logfileName, current)
|
||||
}
|
||||
} catch (err) {
|
||||
if(err && err.code == "ENOENT") {
|
||||
try {
|
||||
fs.symlinkSync(logfileName, current)
|
||||
} catch (e) {
|
||||
if (verbose) {
|
||||
console.error(new Date(), "[FileStreamRotator] Could not create symlink file: ", current, ' -> ', logfileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} logfile
|
||||
* @param {Boolean} verbose
|
||||
* @param {function} cb
|
||||
*/
|
||||
function createLogWatcher(logfile, verbose, cb){
|
||||
if(!logfile) return null
|
||||
// console.log("Creating log watcher")
|
||||
try {
|
||||
let stats = fs.lstatSync(logfile)
|
||||
return fs.watch(logfile, function(event,filename){
|
||||
// console.log(Date(), event, filename)
|
||||
if(event == "rename"){
|
||||
try {
|
||||
let stats = fs.lstatSync(logfile)
|
||||
// console.log("STATS:", stats)
|
||||
}catch(err){
|
||||
// console.log("ERROR:", err)
|
||||
cb(err,logfile)
|
||||
}
|
||||
}
|
||||
})
|
||||
}catch(err){
|
||||
if(verbose){
|
||||
console.log(new Date(),"[FileStreamRotator] Could not add watcher for " + logfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write audit json object to disk
|
||||
* @param {String} logfile
|
||||
* @param {Object} audit
|
||||
* @param {Object} audit.keep
|
||||
* @param {Boolean} audit.keep.days
|
||||
* @param {Number} audit.keep.amount
|
||||
* @param {String} audit.auditLog
|
||||
* @param {String} audit.hashType
|
||||
* @param {Array} audit.files
|
||||
* @param {EventEmitter} stream
|
||||
* @param {Boolean} verbose
|
||||
*/
|
||||
FileStreamRotator.addLogToAudit = function(logfile, audit, stream, verbose){
|
||||
if(audit && audit.files){
|
||||
// Based on contribution by @nickbug - https://github.com/nickbug
|
||||
var index = audit.files.findIndex(function(file) {
|
||||
return (file.name === logfile);
|
||||
});
|
||||
if (index !== -1) {
|
||||
// nothing to do as entry already exists.
|
||||
return audit;
|
||||
}
|
||||
var time = Date.now();
|
||||
audit.files.push({
|
||||
date: time,
|
||||
name: logfile,
|
||||
hash: crypto.createHash(audit.hashType).update(logfile + "LOG_FILE" + time).digest("hex")
|
||||
});
|
||||
|
||||
if(audit.keep.days){
|
||||
var oldestDate = moment().subtract(audit.keep.amount,"days").valueOf();
|
||||
var recentFiles = audit.files.filter(function(file){
|
||||
if(file.date > oldestDate){
|
||||
return true;
|
||||
}
|
||||
file.hashType = audit.hashType
|
||||
removeFile(file, verbose);
|
||||
stream.emit("logRemoved", file)
|
||||
return false;
|
||||
});
|
||||
audit.files = recentFiles;
|
||||
}else{
|
||||
var filesToKeep = audit.files.splice(-audit.keep.amount);
|
||||
if(audit.files.length > 0){
|
||||
audit.files.filter(function(file){
|
||||
file.hashType = audit.hashType
|
||||
removeFile(file, verbose);
|
||||
stream.emit("logRemoved", file)
|
||||
return false;
|
||||
})
|
||||
}
|
||||
audit.files = filesToKeep;
|
||||
}
|
||||
|
||||
FileStreamRotator.writeAuditLog(audit, verbose);
|
||||
}
|
||||
|
||||
return audit;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param options
|
||||
* @param options.filename
|
||||
* @param options.frequency
|
||||
* @param options.verbose
|
||||
* @param options.date_format
|
||||
* @param options.size
|
||||
* @param options.max_logs
|
||||
* @param options.audit_file
|
||||
* @param options.file_options
|
||||
* @param options.utc
|
||||
* @param options.extension File extension to be added at the end of the filename
|
||||
* @param options.watch_log
|
||||
* @param options.create_symlink
|
||||
* @param options.symlink_name
|
||||
* @param options.audit_hash_type Hash to be used to add to the audit log (md5, sha256)
|
||||
* @returns {Object} stream
|
||||
*/
|
||||
FileStreamRotator.getStream = function (options) {
|
||||
var frequencyMetaData = null;
|
||||
var curDate = null;
|
||||
var self = this;
|
||||
|
||||
if (!options.filename) {
|
||||
console.error(new Date(),"[FileStreamRotator] No filename supplied. Defaulting to STDOUT");
|
||||
return process.stdout;
|
||||
}
|
||||
|
||||
if (options.frequency) {
|
||||
frequencyMetaData = self.getFrequency(options.frequency);
|
||||
}
|
||||
|
||||
let auditLog = self.setAuditLog(options.max_logs, options.audit_file, options.filename);
|
||||
// Thanks to Means88 for PR.
|
||||
if (auditLog != null) {
|
||||
auditLog.hashType = (options.audit_hash_type !== undefined ? options.audit_hash_type : 'md5');
|
||||
}
|
||||
self.verbose = (options.verbose !== undefined ? options.verbose : true);
|
||||
|
||||
var fileSize = null;
|
||||
var fileCount = 0;
|
||||
var curSize = 0;
|
||||
if(options.size){
|
||||
fileSize = FileStreamRotator.parseFileSize(options.size);
|
||||
}
|
||||
|
||||
var dateFormat = (options.date_format || DATE_FORMAT);
|
||||
if(frequencyMetaData && frequencyMetaData.type == "daily"){
|
||||
if(!options.date_format){
|
||||
dateFormat = "YYYY-MM-DD";
|
||||
}
|
||||
if(moment().format(dateFormat) != moment().endOf("day").format(dateFormat) || moment().format(dateFormat) == moment().add(1,"day").format(dateFormat)){
|
||||
if(self.verbose){
|
||||
console.log(new Date(),"[FileStreamRotator] Changing type to custom as date format changes more often than once a day or not every day");
|
||||
}
|
||||
frequencyMetaData.type = "custom";
|
||||
}
|
||||
}
|
||||
|
||||
if (frequencyMetaData) {
|
||||
curDate = (options.frequency ? self.getDate(frequencyMetaData,dateFormat, options.utc) : "");
|
||||
}
|
||||
|
||||
options.create_symlink = options.create_symlink || false;
|
||||
options.extension = options.extension || ""
|
||||
var filename = options.filename;
|
||||
var oldFile = null;
|
||||
var logfile = filename + (curDate ? "." + curDate : "");
|
||||
if(filename.match(/%DATE%/)){
|
||||
logfile = filename.replace(/%DATE%/g,(curDate?curDate:self.getDate(null,dateFormat, options.utc)));
|
||||
}
|
||||
|
||||
if(fileSize){
|
||||
var lastLogFile = null;
|
||||
var t_log = logfile;
|
||||
var f = null;
|
||||
if(auditLog && auditLog.files && auditLog.files instanceof Array && auditLog.files.length > 0){
|
||||
var lastEntry = auditLog.files[auditLog.files.length - 1].name;
|
||||
if(lastEntry.match(t_log)){
|
||||
var lastCount = lastEntry.match(t_log + "\\.(\\d+)");
|
||||
// Thanks for the PR contribution from @andrefarzat - https://github.com/andrefarzat
|
||||
if(lastCount){
|
||||
t_log = lastEntry;
|
||||
fileCount = lastCount[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fileCount == 0 && t_log == logfile) {
|
||||
t_log += options.extension
|
||||
}
|
||||
|
||||
while(f = fs.existsSync(t_log)){
|
||||
lastLogFile = t_log;
|
||||
fileCount++;
|
||||
t_log = logfile + "." + fileCount + options.extension;
|
||||
}
|
||||
if(lastLogFile){
|
||||
var lastLogFileStats = fs.statSync(lastLogFile);
|
||||
if(lastLogFileStats.size < fileSize){
|
||||
t_log = lastLogFile;
|
||||
fileCount--;
|
||||
curSize = lastLogFileStats.size;
|
||||
}
|
||||
}
|
||||
logfile = t_log;
|
||||
} else {
|
||||
logfile += options.extension
|
||||
}
|
||||
|
||||
if (self.verbose) {
|
||||
console.log(new Date(),"[FileStreamRotator] Logging to: ", logfile);
|
||||
}
|
||||
|
||||
mkDirForFile(logfile);
|
||||
|
||||
var file_options = options.file_options || {flags: 'a'};
|
||||
var rotateStream = fs.createWriteStream(logfile, file_options);
|
||||
if ((curDate && frequencyMetaData && (staticFrequency.indexOf(frequencyMetaData.type) > -1)) || fileSize > 0) {
|
||||
if (self.verbose) {
|
||||
console.log(new Date(),"[FileStreamRotator] Rotating file: ", frequencyMetaData?frequencyMetaData.type:"", fileSize?"size: " + fileSize:"");
|
||||
}
|
||||
var stream = new EventEmitter();
|
||||
stream.auditLog = auditLog;
|
||||
stream.end = function(){
|
||||
rotateStream.end.apply(rotateStream,arguments);
|
||||
};
|
||||
BubbleEvents(rotateStream,stream);
|
||||
|
||||
stream.on('close', function(){
|
||||
if (logWatcher) {
|
||||
logWatcher.close()
|
||||
}
|
||||
})
|
||||
|
||||
stream.on("new",function(newLog){
|
||||
// console.log("new log", newLog)
|
||||
stream.auditLog = self.addLogToAudit(newLog,stream.auditLog, stream, self.verbose)
|
||||
if(options.create_symlink){
|
||||
createCurrentSymLink(newLog, options.symlink_name, self.verbose)
|
||||
}
|
||||
if(options.watch_log){
|
||||
stream.emit("addWatcher", newLog)
|
||||
}
|
||||
});
|
||||
|
||||
var logWatcher;
|
||||
stream.on("addWatcher", function(newLog){
|
||||
if (logWatcher) {
|
||||
logWatcher.close()
|
||||
}
|
||||
if(!options.watch_log){
|
||||
return
|
||||
}
|
||||
// console.log("ADDING WATCHER", newLog)
|
||||
logWatcher = createLogWatcher(newLog, self.verbose, function(err,newLog){
|
||||
stream.emit('createLog', newLog)
|
||||
})
|
||||
})
|
||||
|
||||
stream.on("createLog",function(file){
|
||||
try {
|
||||
let stats = fs.lstatSync(file)
|
||||
}catch(err){
|
||||
if(rotateStream && rotateStream.end == "function"){
|
||||
rotateStream.end();
|
||||
}
|
||||
rotateStream = fs.createWriteStream(file, file_options);
|
||||
stream.emit('new',file);
|
||||
BubbleEvents(rotateStream,stream);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
stream.write = (function (str, encoding) {
|
||||
var newDate = frequencyMetaData ? this.getDate(frequencyMetaData, dateFormat, options.utc) : curDate;
|
||||
if (newDate != curDate || (fileSize && curSize > fileSize)) {
|
||||
var newLogfile = filename + (curDate && frequencyMetaData ? "." + newDate : "");
|
||||
if(filename.match(/%DATE%/) && curDate){
|
||||
newLogfile = filename.replace(/%DATE%/g,newDate);
|
||||
}
|
||||
|
||||
if(fileSize && curSize > fileSize){
|
||||
fileCount++;
|
||||
newLogfile += "." + fileCount + options.extension;
|
||||
}else{
|
||||
// reset file count
|
||||
fileCount = 0;
|
||||
newLogfile += options.extension
|
||||
}
|
||||
curSize = 0;
|
||||
|
||||
if (self.verbose) {
|
||||
console.log(new Date(),require('util').format("[FileStreamRotator] Changing logs from %s to %s", logfile, newLogfile));
|
||||
}
|
||||
curDate = newDate;
|
||||
oldFile = logfile;
|
||||
logfile = newLogfile;
|
||||
// Thanks to @mattberther https://github.com/mattberther for raising it again.
|
||||
if(options.end_stream === true){
|
||||
rotateStream.end();
|
||||
}else{
|
||||
rotateStream.destroy();
|
||||
}
|
||||
|
||||
mkDirForFile(logfile);
|
||||
|
||||
rotateStream = fs.createWriteStream(newLogfile, file_options);
|
||||
stream.emit('new',newLogfile);
|
||||
stream.emit('rotate',oldFile, newLogfile);
|
||||
BubbleEvents(rotateStream,stream);
|
||||
}
|
||||
rotateStream.write(str, encoding);
|
||||
// Handle length of double-byte characters
|
||||
curSize += Buffer.byteLength(str, encoding);
|
||||
}).bind(this);
|
||||
process.nextTick(function(){
|
||||
stream.emit('new',logfile);
|
||||
})
|
||||
stream.emit('new',logfile)
|
||||
return stream;
|
||||
} else {
|
||||
if (self.verbose) {
|
||||
console.log(new Date(),"[FileStreamRotator] File won't be rotated: ", options.frequency, options.size);
|
||||
}
|
||||
process.nextTick(function(){
|
||||
rotateStream.emit('new',logfile);
|
||||
})
|
||||
return rotateStream;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and make parent directory
|
||||
* @param pathWithFile
|
||||
*/
|
||||
var mkDirForFile = function(pathWithFile){
|
||||
var _path = path.dirname(pathWithFile);
|
||||
_path.split(path.sep).reduce(
|
||||
function(fullPath, folder) {
|
||||
fullPath += folder + path.sep;
|
||||
// Option to replace existsSync as deprecated. Maybe in a future release.
|
||||
// try{
|
||||
// var stats = fs.statSync(fullPath);
|
||||
// console.log('STATS',fullPath, stats);
|
||||
// }catch(e){
|
||||
// fs.mkdirSync(fullPath);
|
||||
// console.log("STATS ERROR",e)
|
||||
// }
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
try{
|
||||
fs.mkdirSync(fullPath);
|
||||
}catch(e){
|
||||
if(e.code !== 'EEXIST'){
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullPath;
|
||||
},
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bubbles events to the proxy
|
||||
* @param emitter
|
||||
* @param proxy
|
||||
* @constructor
|
||||
*/
|
||||
var BubbleEvents = function BubbleEvents(emitter,proxy){
|
||||
emitter.on('close',function(){
|
||||
proxy.emit('close');
|
||||
})
|
||||
emitter.on('finish',function(){
|
||||
proxy.emit('finish');
|
||||
})
|
||||
emitter.on('error',function(err){
|
||||
proxy.emit('error',err);
|
||||
})
|
||||
emitter.on('open',function(fd){
|
||||
proxy.emit('open',fd);
|
||||
})
|
||||
}
|
||||
17
resources/app/node_modules/file-stream-rotator/LICENSE.txt
generated
vendored
Normal file
17
resources/app/node_modules/file-stream-rotator/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
The MIT License (MIT)
|
||||
Copyright © 2022 Roger Castells
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the “Software”), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
1
resources/app/node_modules/file-stream-rotator/index.js
generated
vendored
Normal file
1
resources/app/node_modules/file-stream-rotator/index.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('./FileStreamRotator');
|
||||
15
resources/app/node_modules/file-stream-rotator/package.json
generated
vendored
Normal file
15
resources/app/node_modules/file-stream-rotator/package.json
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "file-stream-rotator",
|
||||
"version": "0.6.1",
|
||||
"description": "Automated stream rotation useful for log files",
|
||||
"main": "FileStreamRotator.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/rogerc/file-stream-rotator.git"
|
||||
},
|
||||
"author": "Roger Castells",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"moment": "^2.29.1"
|
||||
}
|
||||
}
|
||||
208
resources/app/node_modules/file-stream-rotator/test.js
generated
vendored
Normal file
208
resources/app/node_modules/file-stream-rotator/test.js
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
var fsr = require('./index');
|
||||
var assert = require('assert');
|
||||
var fs = require('fs');
|
||||
|
||||
var tests = {
|
||||
testFileSizes: function () {
|
||||
[
|
||||
["3k",3072],
|
||||
["5M",5242880],
|
||||
["0.5G",536870912],
|
||||
["0.5T",null],
|
||||
["1mega",null],
|
||||
["10 giga",null]
|
||||
].map(function(fsize){
|
||||
console.log(fsize[0],fsize[1],fsr.parseFileSize(fsize[0]));
|
||||
assert.ok(fsize[1] == fsr.parseFileSize(fsize[0]));
|
||||
})
|
||||
},
|
||||
testFrequency: function () {
|
||||
var opt1 = 'M544';
|
||||
var opt2 = '5m';
|
||||
var opt3 = '1H';
|
||||
var opt4 = '3h';
|
||||
var opt5 = 'daily';
|
||||
var opt6 = 'test';
|
||||
var opt7 = '3W';
|
||||
var opt8 = '-1h';
|
||||
var opt9 = '25h';
|
||||
var opt10 = '24h';
|
||||
var opt11 = '23h';
|
||||
var opt12 = '59m';
|
||||
var opt13 = '60m';
|
||||
var opt14 = '61m';
|
||||
var opt15 = '-1m';
|
||||
|
||||
|
||||
assert.ok(!fsr.getFrequency(opt1));
|
||||
|
||||
var obj = fsr.getFrequency(opt2);
|
||||
console.log('obj =', obj);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'm');
|
||||
assert.equal(obj.digit, 5);
|
||||
|
||||
obj = fsr.getFrequency(opt3);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'h');
|
||||
assert.equal(obj.digit, 1);
|
||||
|
||||
obj = fsr.getFrequency(opt4);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'h');
|
||||
assert.equal(obj.digit, 3);
|
||||
|
||||
obj = fsr.getFrequency(opt5);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'daily');
|
||||
assert.equal(obj.digit, undefined);
|
||||
|
||||
obj = fsr.getFrequency(opt6);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'test');
|
||||
assert.equal(obj.digit, 0);
|
||||
|
||||
obj = fsr.getFrequency(opt7);
|
||||
assert.ok(obj === false);
|
||||
|
||||
obj = fsr.getFrequency(opt8);
|
||||
assert.ok(obj === false);
|
||||
|
||||
obj = fsr.getFrequency(opt9);
|
||||
assert.ok(obj === false);
|
||||
|
||||
obj = fsr.getFrequency(opt10);
|
||||
console.log('obj =', obj);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'h');
|
||||
assert.equal(obj.digit, 24);
|
||||
|
||||
obj = fsr.getFrequency(opt11);
|
||||
console.log('obj =', obj);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'h');
|
||||
assert.equal(obj.digit, 23);
|
||||
|
||||
obj = fsr.getFrequency(opt12);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'm');
|
||||
assert.equal(obj.digit, 59);
|
||||
|
||||
obj = fsr.getFrequency(opt13);
|
||||
assert.ok(typeof obj == 'object');
|
||||
assert.equal(obj.type, 'm');
|
||||
assert.equal(obj.digit, 60);
|
||||
|
||||
obj = fsr.getFrequency(opt14);
|
||||
assert.ok(obj === false);
|
||||
|
||||
obj = fsr.getFrequency(opt15);
|
||||
assert.ok(obj === false);
|
||||
},
|
||||
testGetDate: function () {
|
||||
var opt = {type: 'test', digit: 0};
|
||||
var opt1 = {type: 'daily', digit: 0};
|
||||
var opt2 = {type: 'h', digit: 1};
|
||||
var opt3 = {type: 'm', digit: 30};
|
||||
var opt4 = {type: 'm', digit: 45};
|
||||
var opt5 = {type: 'h', digit: 3};
|
||||
var opt6 = {type: 'm', digit: 5};
|
||||
|
||||
var format1 = 'YYYYMMDD';
|
||||
var format2 = 'YYYY-MM-DD';
|
||||
var format3 = 'YYYYMMDD.HHmmss';
|
||||
var format4 = 'YYYY-MM-DD:HH:mm:ss';
|
||||
|
||||
console.log(fsr.getDate(opt));
|
||||
console.log(fsr.getDate(opt1));
|
||||
console.log(fsr.getDate(opt2));
|
||||
console.log(fsr.getDate(opt3));
|
||||
console.log(fsr.getDate(opt4));
|
||||
console.log(fsr.getDate(opt5));
|
||||
console.log(fsr.getDate(opt6));
|
||||
|
||||
console.log(fsr.getDate({type: 'test', digit: 0},format1));
|
||||
console.log(fsr.getDate({type: 'test', digit: 0},format2));
|
||||
console.log(fsr.getDate({type: 'test', digit: 0},format3));
|
||||
console.log(fsr.getDate({type: 'test', digit: 0},format4));
|
||||
},
|
||||
testAuditSettings: function(){
|
||||
var a = fsr.setAuditLog("10d","","/tmp/a/b/c/files/%DATE%/logs");
|
||||
console.log(a)
|
||||
assert.equal(a.auditLog,"/tmp/a/b/c/files/.audit.json");
|
||||
assert.equal(a.keep.amount,10);
|
||||
assert.equal(a.keep.days,true);
|
||||
assert.equal(a.files.length,0);
|
||||
|
||||
a = fsr.setAuditLog("10","/tmp/a/b/log_audit_file.json","/tmp/a/b/c/files/%DATE%/logs1");
|
||||
console.log(a)
|
||||
assert.equal(a.auditLog,"/tmp/a/b/log_audit_file.json");
|
||||
assert.equal(a.keep.amount,10);
|
||||
assert.equal(a.keep.days,false);
|
||||
assert.equal(a.files.length,0);
|
||||
|
||||
// var b = fsr.writeAuditLog({keep:{days:true,amount:10},auditLog: "/tmp/aa/a/e/a/b/c/a/b/b/ba_log.json",files:[{date:Date.now(), name:"/tmp/a/b/c.log"}]});
|
||||
|
||||
},
|
||||
testGetStream: function() {
|
||||
return;
|
||||
|
||||
var logdir = __dirname + '/log/';
|
||||
|
||||
var test = function() {
|
||||
|
||||
var options1 = { filename: logdir + 'program1.log', frequency: '1m', verbose: true, date_format: 'YYYY-MM-DD' };
|
||||
var options2 = { filename: logdir + 'program2.log', frequency: '1m', verbose: true};
|
||||
var options3 = { filename: logdir + 'program3-%DATE%.log', frequency: '1m', verbose: true, date_format: 'YYYY-MM-DD'};
|
||||
var options4 = { filename: logdir + 'program4-%DATE%.log', verbose: true, date_format: 'YYYY-MM-DD'};
|
||||
var options5 = { filename: logdir + 'program5-%DATE%.log', verbose: true};
|
||||
|
||||
var stream1 = fsr.getStream(options1);
|
||||
stream1.write('formatted date');
|
||||
var stream2 = fsr.getStream(options2);
|
||||
stream2.write('default date');
|
||||
var stream3 = fsr.getStream(options3);
|
||||
stream3.write('date mid filename');
|
||||
var stream4 = fsr.getStream(options4);
|
||||
stream4.write('date mid filename without rotation');
|
||||
var stream5 = fsr.getStream(options5);
|
||||
stream5.write('dafault date mid filename without rotation');
|
||||
|
||||
|
||||
var options = { filename: logdir + 'program-%DATE%.log', frequency: '1m', verbose: true, date_format: 'YYYY-MM-DD:HH:mm' };
|
||||
|
||||
var stream = fsr.getStream(options);
|
||||
process.__defineGetter__('stdout', function() { return stream;});
|
||||
process.__defineGetter__('stderr', function() { return stream;});
|
||||
|
||||
setTimeout(function(){
|
||||
stream.write('Foo bar');
|
||||
}, 3000)
|
||||
|
||||
setTimeout(function(){
|
||||
stream.write('Foo bar');
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
fs.exists(logdir, function(exists) {
|
||||
if(!exists) {
|
||||
console.log('Creating the log directory as one doesnt exist');
|
||||
fs.mkdir(logdir, function(err) {
|
||||
if(err) {
|
||||
console.error('Trouble creating directory %s', logdir);
|
||||
throw err;
|
||||
}
|
||||
test();
|
||||
});
|
||||
}else{
|
||||
test();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
Object.keys(tests).forEach(function (test) {
|
||||
if (typeof tests[test] == 'function') {
|
||||
tests[test]();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user