Node和Express应用程序的常见日志记录 - 最佳实践?

14

我正在开发一个使用 bunyan 记录日志(JSON 输出,多个可配置流)的 Node.js 应用程序,其中包含数十个模块。我一直在寻找如何在所有模块中实现单例的良好示例,但似乎没有看到真正干净的可以学习的示例。

下面展示了一种可行的方法,但对我来说似乎相当不雅观丑陋。我对 Node 和通用 JavaScript 的 CommonJS 相当新,因此希望获得有关如何改进它的建议。

模块:./lib/logger

// load config file (would like this to be passed in to the constructor)
nconf.file({ file: fileConfig});
var logSetting = nconf.get('log');

// instantiate the logger
var Bunyan = require('bunyan');
var log = new Bunyan({
    name: logSetting.name,
streams : [
        { stream  : process.stdout, 
        level : logSetting.stdoutLevel},
        {    path : logSetting.logfile, 
            level : logSetting.logfileLevel}
    ],
serializers : Bunyan.stdSerializers
});

function Logger() {
};

Logger.prototype.info = function info(e) { log.info(e) };
Logger.prototype.debug = function debug(e) { log.debug(e) };
Logger.prototype.trace = function trace(e) { log.trace(e) };
Logger.prototype.error = function error(e) { log.error(e) };
Logger.prototype.warn = function warn(e) {  log.warn(e) };

module.exports = Logger;

模块:主应用程序

// create the logger
var logger = require('./lib/logger)
var log = new logger();

// note: would like to pass in options -->  new logger(options)


module: any project module using logger
// open the logger (new, rely on singleton...)
var logger = require('./lib/logger');
var log = new logger();

或者查看代码片段

有什么建议吗?

编辑:

我修改了构造函数,使单例模式显式化(而不是作为“require”行为的隐式部分)。

var log = null;
function Logger(option) {

// make the singleton pattern explicit
if (!Logger.log) {
    Logger.log = this;
}
    return Logger.log;
};  

然后将初始化更改为接受一个选项参数。

// initialize the logger 
Logger.prototype.init = function init(options) {
log = new Bunyan({
    name: options.name,
    streams : [
        { stream  : process.stdout, 
            level : options.stdoutLevel},
        {    path : options.logfile, 
            level : options.logfileLevel}
    ],
    serializers : Bunyan.stdSerializers     
    });
};

你所拥有的看起来像是使用 require 功能构建的基本单例。你可以实现自己的单例,但不一定更好。或许可以进行依赖注入? - mtsr
感谢回复 - 是的,这是一个基本的单例模式。有几件事让我感到困扰:(a) 单例行为是暗示的而不是显式的,(b) 我没有找到一种简洁的方式让构造函数带参数(比如一个选项JSON),而不必要地确定它们是否已被先前的模块加载,(c) 把引用(作为参数)传递给每个单独的模块的替代方法似乎很混乱/啰嗦。 - dewd
谢谢回复!是的,它是一个基本的单例模式,尽管它似乎是一种暗示的行为而不是定义好的行为。我查看了不良实践?单例模式以及使用 require 实现单例模式,它们似乎比上面更好地解决了构造函数的行为。 - dewd
2个回答

23

在Node.js中是否需要单例模式? 实际上,在Node环境下可能不需要使用单例模式。你所需要做的就是在一个单独的文件(例如logger.js)中创建一个记录器:

var bunyan = require("bunyan"); // Bunyan依赖
var logger = bunyan.createLogger({name: "myLogger"});
module.exports = logger;

然后,从另一个模块中检索此记录器:

var logger = require("./logger");
logger.info("你喜欢的任何内容");


我喜欢这种方法,但是如何在第一次使用之前配置记录器?我想以这种方式使用它,但我想设置输出路径。我尝试在logger.js中包含“init”函数并在其中设置“module.exports”,但这不太好。 - Garret Raziel
@lixiang 这样做会不会每次需要时都创建一个新的日志记录器实例? - Z2VvZ3Vp
1
@PixMach,不会,require模块会缓存对象实例。 - lixiang
哦,这意味着 require("./logger") 实际上是一个单例,对吧? - mogsie
1
@mogsie,是的,如果我们导出一个对象,它是单例的。如果我们导出一个函数,它不是单例的。 - Sangeeth

1
如果您正在使用Node.js的Express,则可以尝试以下操作。 默认情况下,Express中禁用了日志记录。您需要执行某些操作才能使日志记录适用于您的应用程序。对于访问日志,我们需要使用Logger中间件;对于错误日志,我们将使用Forever。希望这会对您有所帮助。 这里有一个很好的例子如何在node.js中记录访问和错误。

1
谢谢,这是一个有用的参考。我已经使用了类似的模式来创建Bunyan的express日志中间件,但我在创建一个干净的包装器类方面遇到了困难,该类允许我在所有模块中使用相同的实例,并且具有允许传递选项的初始化方法。我会发布我想出的内容... - dewd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接