Node.jsでサーバを作るときに気をつけること の続きのようなものです。
コードを書いたらバグは付き物。でもNode.jsの場合はプログラム全体が死亡するので、ちゃんとエラーを把握しておくことが大事です。
winstonモジュール
私が仕事で使っているのは、winstonというロガーモジュール。
こいつがいいのは、ログを出したら、複数の方法でログを記録できるということ。
例えば、
- ログレベルでerror以上だけメールで送りたい
- でもファイルにも記録しておきたい
みたいのは余裕です。
var winston = require('winston');
var transports = [
// デバッグできるようにコンソールにも出力
new (winston.transports.Console)({
level: 'verbose',
timestamp: true,
colorize: true,
json: true,
prettyPrint: true
})
];
// error以上でメール通知
transports.push(
new (require('./winston-nodemail').NodeMail)({
'host': configs.SMTP.HOST,
'username': configs.SMTP.USERNAME,
'password': configs.SMTP.PASSWORD,
'to': configs.MAIL_LOGGER.TO,
'from': configs.MAIL_LOGGER.FROM,
'secure': true,
'level': 'error'
})
);
// 日にち単位でログファイルを作成
transports.push(
new (winston.transports.DailyRotateFile)({
'dirname': __dirname + '/../logs/',
})
);
// ロガーの作成
var logger = new (winston.Logger)({
transports: transports, // 通常用のtransports
exceptionHandlers: transports, // エラーが起きたとき用のtransports
exitOnError: true // エラーが起きたら、さっさと終了させる (で、foreverで再起動)
});
logger.verbose("Hello world");
logger.error("エラーだよー"); //メールが届くはず
logger.debug({ //JSONだってそのままOK
"key1": 222,
"key2": "value2"
});
メールを送るwinston用のモジュールは(たしか)デフォルトで用意されているんだけど、私の場合は書式を整えたい都合があったので、自分で作りました。
(以下、winston-nodemail.js)
var util = require('util'),
winston = require('winston'),
sprintf = require('sprintf').sprintf,
typeOf = require('typeof'),
nodemailer = require('nodemailer'),
smtpTransport;
var NodeMail = exports.NodeMail = function (options) {
//
// Name this logger
//
this.name = 'winstonNodeMail';
//
// Set the level from your options
//
this.level = options.level || 'info';
//
// Configure for node-mailer module
//
this.username = options.username || '',
this.password = options.password || '',
this.host = options.host || 'localhost';
this.port = options.port || 25;
this.from = options.from || 'admin@' + this.host;
this.to = options.to || 'admin@' + this.host;
this.secure = options.secure || false;
this.createSmtpTransport();
};
//
// Inherit from `winston.Transport` so you can take advantage
// of the base functionality and `.handleExceptions()`.
//
util.inherits(NodeMail, winston.Transport);
NodeMail.prototype.createSmtpTransport = function () {
this.smtpTransport = nodemailer.createTransport({
host: this.host,
secure: this.secure,
auth: {
user: this.username,
pass: this.password
},
debug: true
});
}
NodeMail.prototype.log = function (level, msg, meta, callback) {
var now = new Date();
var params, formattedMessage;
if (!this.smtpTransport) {
this.createSmtpTransport();
}
formattedMessage = sprintf(
[
"%02d/%2d %02d:%02d",
"--------------------------------",
"%s",
"",
"%s"
].join("\n"),
now.getMonth() + 1, now.getDate(), now.getHours(), now.getMinutes(),
msgToString(msg), msgToString(meta)
);
var subject = sprintf("%s", msgToString(msg));
subject = subject.replace(/\n.*$/, "");
params = {
from : this.from,
to : this.to,
//subject : sprintf('report level=%s %02d:%02d', level, now.getHours(), now.getMinutes() ),
subject : sprintf('%s %02d:%02d', subject, now.getHours(), now.getMinutes() ),
text : formattedMessage
};
var self = this;
this.smtpTransport.sendMail(params, function (error, response) {
if (error) {
console.log(error);
//self.smtpTransport.close();
callback(error, response);
return;
}
//console.log(response);
//self.smtpTransport.close();
callback(null, response);
});
};
function msgToString(input) {
var converter = function(msg) {
if (msg instanceof Error) {
msg = {
name: msg.name,
message: msg.message,
fileName: msg.fileName,
lineNumber: msg.lineNumber,
stack: msg.stack.split("\n")
};
} else if (typeOf(msg) == "string") {
if (msg.substring(0, 1) == "{" &&
msg.substring(-1, 1) == "}") {
try {
msg = JSON.parse(msg);
}catch(e) {};
}
} else if (typeof(msg) == "object") {
for (var key in msg) {
msg[key] = converter(msg[key]);
}
}
return msg;
};
return JSON.stringify(converter(input), null, 4);
}
予期せぬエラーもwinston
winstonロガーはエラー処理されていない場合でも、ちゃんとキャッチしてくれます。
そのログを自作の winston-nodemail.js が適当にいい感じに書式化して、エラー情報をメールで送ってくれます。
foreverで動かしていると、プログラムが死んでもすぐに立ち上げ直してくれます。
でも単純なミスがあるとすぐに死亡して、メール通知して、また死亡して、、、と、自分のメールボックスがエラー通知で一杯になるので注意しましょう。
完璧な対応というわけではないですが、いまのところこれでかなり救われてます。
参考までにどうぞ。