var fs = require('fs'); var moment = require('moment'); var helper = require('../lib/helper'); var _ = require('lodash'); var alarm = require('./alarm'); /* * parameters: * dirname: string * rotation: interger (ms) * maxsize: interger (byte) * interval: interger (ms) * data: array (array of object) * alarm: object (alarm object) */ function stat(dirname, opts, alarmData) { opts = opts || {}; alarmData = alarmData || {}; this.dirname = dirname; this.rotation = opts.rotation || 15 * 60 * 1000; this.maxsize = opts.maxsize || 20000; this.filename = opts.filename || '[log]YYYY-MM-DDTHH-mm-ss[_${count}.txt]'; this.currentsize = 0; this.timestamp = 0; this.foldername = 'stat/'; this.intervalId = undefined; this.interval = opts.interval || 60 * 1000; this.data = transformKeys(opts.data); this.rules = opts.data; this.alarm = new alarm(dirname, alarmData); helper.mkdirIfNotExist(`${this.dirname}/${this.foldername}`); }; function transformKeys(data) { keys = _.map(data, function (obj) { var fakey = {}; fakey[obj.key] = 0; return fakey; }); return _.reduce(keys, function (new_obj, obj) { return _.merge(new_obj, obj); }, {}); }; /* * parameters: * data: obj * rules: [obj] */ function alarmDataOutOfThreshold(data, rules) { _alarm = []; _.forEach(data, function (v, k) { rule = _.find(rules, ['key', k]); var alarmObj = {}; if (v < rule.threshold_inv) { _alarm.push({ key: k, count: v, threshold_inv: rule.threshold_inv, message: `${k} count is below inverted threshold` }); } if (v > rule.threshold) { _alarm.push({ key: k, count: v, threshold: rule.threshold, message: `${k} count is above threshold` }); } }); return _alarm }; /* * parameters: * data: any */ stat.prototype.appendStat = function (data) { data = this.formatData(data); this.currentsize = this.currentsize + helper.getLengthOfContent(data); fs.appendFile(this.getDir(), data, function (err) {}); }; /* * parameters: * none */ stat.prototype.start = function () { var self = this; this.intervalId = setInterval(function () { var alarmData = alarmDataOutOfThreshold(self.data, self.rules); if (!_.isEmpty(alarmData)) { self.alarm.appendAlarm(alarmData); } self.appendStat(self.data); self.reset(); }, self.interval); }; /* * parameters: * none */ stat.prototype.stop = function () { clearInterval(this.intervalId); }; /* * parameters: * data: string */ stat.prototype.increment = function (data) { if (_.has(this.data, data)) { this.data[data]++; } else this.data[data] = 1; }; /* * parameters: * none */ stat.prototype.reset = function () { var self = this; _.forEach(this.data, function (v, k) { self.data[k] = 0; }); }; /* * parameters: * none */ stat.prototype.getDir = function () { var time = moment(Math.floor((+moment()) / this.rotation) * this.rotation); this.resetCurrentSize(time.unix()); var count = this.getCount(); time = time.format(this.filename.replace("${count}", count)); return `${this.dirname}${this.foldername}${time}`; }; /* * parameters: * time_unix: string */ stat.prototype.resetCurrentSize = function (time_unix) { if (time_unix > this.timestamp) { this.currentsize = 0 this.timestamp = time_unix; } }; /* * parameters: * none */ stat.prototype.getCount = function () { var count = Math.floor((this.currentsize / this.maxsize) + 1); return ((count * 1e-5).toFixed(5)).split('.')[1]; }; /* * parameters: * data: any */ stat.prototype.formatData = function (data) { var date = moment().toISOString().trim(); var timestamp = moment().unix(); data = this._formatObject(data).trim(); return `${date} ${timestamp} ${data}\r\n`; }; /* * parameters: * data: any */ stat.prototype._formatObject = function (data) { return JSON.stringify(data); }; module.exports = stat;