Skip to content

Instantly share code, notes, and snippets.

@fengmk2
Created June 8, 2012 02:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fengmk2/2893211 to your computer and use it in GitHub Desktop.
Save fengmk2/2893211 to your computer and use it in GitHub Desktop.
可以自动切割的日志stream
/**
* Log file stream
*/
var fs = require('fs');
var path = require('path');
var Stream = require('stream').Stream;
var util = require('util');
var ONE_MINUTE = 60000;
var ONE_HOUR = 60 * ONE_MINUTE;
var ONE_DAY = 24 * ONE_HOUR;
// format datetime, demo: new Date().format("yyyy-MM-dd hh:mm:ss W");
var WEEK_DAYS = [ '周日', '周一', '周二', '周三', '周四', '周五', '周六' ];
Date.prototype.format = function(format) {
format = format || "yyyy-MM-dd hh:mm:ss";
var day = this.getDay();
var weeken = (day === 0 || day === 6) ? WEEK_DAYS[day] : '';
var o = {
"M+" : this.getMonth() + 1, //month
"d+" : this.getDate(), //day
"h+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"q+" : Math.floor((this.getMonth() + 3) / 3), //quarter
"W" : WEEK_DAYS[day], // weekday
"E" : weeken, // end of week
"S" : this.getMilliseconds() //millisecond
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("("+ k +")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));
}
}
return format;
};
/**
* Log stream, auto cut the log file.
*
* @param {Object} options
* - {String} logdir
* - {String} filename, if set `filename`, logstream will not auto cut.
* - {String} nameformat, default is 'yyyy-MM-dd.hhmmss.{pid}.log'
* - {Number} duration, default is one houre(24 * 3600000 ms), must >= 60s.
* return {LogStream}
*/
exports.createStream = function (options) {
return new LogStream(options);
};
function LogStream(options) {
if (!(this instanceof LogStream)) {
return new LogStream(options);
}
options = options || {};
this.logdir = options.logdir || './logs';
if (!path.existsSync(this.logdir)) {
fs.mkdirSync(this.logdir, '0777');
}
this.filename = options.filename;
this.nameformat = options.nameformat;
if (this.nameformat === null || this.nameformat === undefined) {
this.nameformat = 'yyyy-MM-dd.hhmmss.{pid}.log';
}
this.nameformat = this.nameformat.replace('{pid}', process.pid);
this.duration = options.duration || ONE_HOUR;
// must >= one second
if (this.duration < 60000) {
this.duration = 60000;
}
this.cut();
this.startTimer(this.firstDuration());
}
util.inherits(LogStream, Stream);
LogStream.prototype.firstDuration = function () {
var now = new Date();
var firstDuration = this.duration;
var end = new Date(now + this.duration);
if (this.duration > ONE_MINUTE) {
if (this.duration < ONE_HOUR) { // in minute
firstDuration = new Date(new Date(now.getTime() + this.duration).format('yyyy-MM-dd hh:mm:00')) - now;
} else if (this.duration < ONE_DAY) { // in hour
firstDuration = new Date(new Date(now.getTime() + this.duration).format('yyyy-MM-dd hh:00:00')) - now;
} else { // in day
firstDuration = new Date(new Date(now.getTime() + this.duration).format('yyyy-MM-dd 00:00:00')) - now;
}
}
return firstDuration;
};
LogStream.prototype.startTimer = function (duration) {
if (this._timer) {
clearTimeout(this._timer);
}
this._timer = setTimeout(function (self) {
this._timer = null;
self.cut();
self.startTimer(self.duration);
}, duration || this.duration, this);
};
LogStream.prototype.cut = function () {
if (this.stream) {
this.stream.end();
this.stream.destroySoon();
this.stream = null;
}
var now = new Date();
var name = (this.filename || '');
if (this.nameformat) {
name += now.format(this.nameformat);
}
var logpath = path.join(this.logdir, name);
this._reopening = true;
this.stream = fs.createWriteStream(logpath, { flags: 'a', mode: '0666' });
this.stream
.on("error", this.emit.bind(this, "error"))
.on("pipe", this.emit.bind(this, "pipe"))
.on("drain", this.emit.bind(this, "drain"))
.on("open", function () {
this._reopening = false;
}.bind(this))
.on("close", function () {
if (!this._reopening) {
this.emit("close");
}
}.bind(this));
};
LogStream.prototype.write = function (string, encoding) {
this.stream.write(string, encoding);
};
LogStream.prototype.end = function () {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
if (this.stream) {
this.stream.end();
this.stream.destroySoon();
this.stream = null;
}
};
exports.LogStream = LogStream;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment