节点:登录文件而不是控制台


174

是否可以配置console.log为将日志写在文件上,而不是在控制台上打印?


1
可以,但是对于长时间运行的生产应用程序来说,很难只找到特定日期的日志,对吗?@racar

Answers:


72

2013年更新-围绕Node v0.2和v0.4编写;现在,围绕日志记录有更好的工具。我强烈推荐温斯顿

2013年末更新-我们仍然使用winston,但现在有了记录器库,用于围绕自定义对象的记录和格式来包装功能。这是我们logger.js的示例 https://gist.github.com/rtgibbons/7354879


应该像这样简单。

var access = fs.createWriteStream(dir + '/node.access.log', { flags: 'a' })
      , error = fs.createWriteStream(dir + '/node.error.log', { flags: 'a' });

// redirect stdout / stderr
proc.stdout.pipe(access);
proc.stderr.pipe(error);

1
Nvm,这意味着我认为是过程...这是如何工作的?console.log(whatever);仍然去控制台,而不是文件。
trusktr 2012年

12
由于最近的更改,您不能再调用stderr.pipe()了-现在需process.__defineGetter__('stderr', function() { return fs.createWriteStream(__dirname + '/error.log', {flags:'a'}) })
要这样做

15
我个人会避开温斯顿。我们已经使用了一年多,最终决定完全删除它,因为它在我们的生产环境中引起了许多问题。该项目现在似乎维护极差。只要您的手臂有问题清单。许多问题已经有其他用户提交了拉取请求,但是项目维护者甚至没有花时间来合并这些请求。对于其他选择,node-bunyan和Caterpillar可能值得一看。
UpTheCreek

令人遗憾的是,维护人员没有做出响应。我认为它是由Nodejitsu员工维护的。我们已经在多个站点上使用它,每天的浏览量超过一百万,并且日志记录没有任何问题。可能与所记录的内容及其数量有关。
瑞安·吉本斯

1
截至2015年4月,Winston似乎是最新的,并且收到了大量活动。
Mike Grace 2015年

180

您也可以只重载默认的console.log函数:

var fs = require('fs');
var util = require('util');
var log_file = fs.createWriteStream(__dirname + '/debug.log', {flags : 'w'});
var log_stdout = process.stdout;

console.log = function(d) { //
  log_file.write(util.format(d) + '\n');
  log_stdout.write(util.format(d) + '\n');
};

上面的示例将日志记录到debug.log和stdout。

编辑: 也在此页面上参见Clément的多参数版本


9
我不会打扰console.log。只需创建自己的函数即可记录到特定文件。
亚历山大·米尔斯

此外,这不适用于多个参数。例如console.log(p1,p2,p3)
user603749

10
好答案!另外,如果你想捕捉多个参数的console.log,干脆抢“参数”对象中的“d”的地方- developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
查理

@AlexMills嗨,也许您可​​以提供一个示例,说明如何完成此操作?
乔纳森

6
但是我需要在每个js文件中使用它,是否可以使其全局化?
stackdave

60

如果您正在生产中寻找某些东西,温斯顿可能是最佳选择。

如果您只是想快速进行开发工作,请直接输出到文件(我认为这仅适用于* nix系统):

nohup node simple-server.js > output.log &

7
使用>重定向STDOUT在Windows上也适用。 nohup不。
布拉德

1
不用nohup* nix 即可愉快地工作,即node simple-server.js > output.log。然后,如果您想按照原木写的原样写tail -f output.log
讨厌的糊状,2016年

2我将参数传递给流程时,此方法
不起作用

在生产中这样做的缺点是什么?
marcelino

1
顺便说一句,这取决于您的操作系统,“做某事”可能只是日志旋转,这是一个很好的解决方案imo-abdussamad.com/archives/541-Log-rotation-in-CentOS-Linux.html
ferr

58

我经常对console.log()console.error()使用很多参数,所以我的解决方案是:

var fs = require('fs');
var util = require('util');
var logFile = fs.createWriteStream('log.txt', { flags: 'a' });
  // Or 'w' to truncate the file every time the process starts.
var logStdout = process.stdout;

console.log = function () {
  logFile.write(util.format.apply(null, arguments) + '\n');
  logStdout.write(util.format.apply(null, arguments) + '\n');
}
console.error = console.log;

32

Winston是用于日志记录的非常受欢迎的npm模块。

这是一个方法。
在您的项目中将winston安装为:

npm install winston --save

这是一个现成的配置,可以立即使用,我在项目中经常使用它们作为utils下的logger.js。

 /**
 * Configurations of logger.
 */
const winston = require('winston');
const winstonRotator = require('winston-daily-rotate-file');

const consoleConfig = [
  new winston.transports.Console({
    'colorize': true
  })
];

const createLogger = new winston.Logger({
  'transports': consoleConfig
});

const successLogger = createLogger;
successLogger.add(winstonRotator, {
  'name': 'access-file',
  'level': 'info',
  'filename': './logs/access.log',
  'json': false,
  'datePattern': 'yyyy-MM-dd-',
  'prepend': true
});

const errorLogger = createLogger;
errorLogger.add(winstonRotator, {
  'name': 'error-file',
  'level': 'error',
  'filename': './logs/error.log',
  'json': false,
  'datePattern': 'yyyy-MM-dd-',
  'prepend': true
});

module.exports = {
  'successlog': successLogger,
  'errorlog': errorLogger
};

然后只需按以下要求导入即可:

const errorLog = require('../util/logger').errorlog;
const successlog = require('../util/logger').successlog;

然后,您可以将成功记录为:

successlog.info(`Success Message and variables: ${variable}`);

错误为:

errorlog.error(`Error Message : ${error}`);

您还可以按日期将所有成功日志和错误日志记录到日志目录下的文件中。
日志记录


日志在控制台中可见。并没有立即创建文件!我想念什么吗?
尼吉兰'17

1
您能否共享您的配置?还是按照我的建议使用了它?如果配置正确,请检查一次导入(winstonwinston-daily-rotate-file)。它们应logs在项目的根目录中命名的文件夹中创建。请原谅我延迟的答复。
keshavDulal

@Nigilan,您如何解决此问题?我正面临着同样的问题。
user2180794

@ user2180794在Nigilan的评论之上,看看我的评论。让我们知道您是否仍然无法完成。
keshavDulal '17

1
不要忘记将它们添加到logger-util const winston = require('winston'); const winstonRotator = require('winston-daily-rotate-file'); 以及const errorLog = require('../util/logger').errorlog; const successlog = require('../util/logger').successlog; 您想要记录任何内容的位置。
keshavDulal '17

14
const fs = require("fs");
const {keys} = Object;
const {Console} = console;

/**
 * Redirect console to a file.  Call without path or with false-y
 * value to restore original behavior.
 * @param {string} [path]
 */
function file(path) {
    const con = path ? new Console(fs.createWriteStream(path)) : null;

    keys(Console.prototype).forEach(key => {
        if (path) {
            this[key] = (...args) => con[key](...args);
        } else {
            delete this[key];
        }
    });
};

// patch global console object and export
module.exports = console.file = file;

要使用它,请执行以下操作:

require("./console-file");
console.file("/path/to.log");
console.log("write to file!");
console.error("also write to file!");
console.file();    // go back to writing to stdout

是否可以保存错误?
stackdave

无需循环遍历Console.prototype键,而只需显式设置即可this.error
rich remer

这会破坏console.log吗?
G_V

它不会破裂console.log。尽管您可以通过调用恢复原来的行为,但它会更改其行为console.file()
rich remer


6

另一个尚未提及的解决方案是将Writable流挂在process.stdout和中process.stderr。这样,您无需覆盖输出到stdout和stderr的所有控制台功能。此实现将stdout和stderr都重定向到日志文件:

var log_file = require('fs').createWriteStream(__dirname + '/log.txt', {flags : 'w'})

function hook_stream(stream, callback) {
    var old_write = stream.write

    stream.write = (function(write) {
        return function(string, encoding, fd) {
            write.apply(stream, arguments)  // comments this line if you don't want output in the console
            callback(string, encoding, fd)
        }
    })(stream.write)

    return function() {
        stream.write = old_write
    }
}

console.log('a')
console.error('b')

var unhook_stdout = hook_stream(process.stdout, function(string, encoding, fd) {
    log_file.write(string, encoding)
})

var unhook_stderr = hook_stream(process.stderr, function(string, encoding, fd) {
    log_file.write(string, encoding)
})

console.log('c')
console.error('d')

unhook_stdout()
unhook_stderr()

console.log('e')
console.error('f')

它应该在控制台中打印

a
b
c
d
e
f

并在日志文件中:

c
d

有关更多信息,请检查此要点


正确答案。
尼克翁克(Nikk wong)

6

对于简单的情况,我们可以通过'>''2>&1'标准输出(STDOUT)和标准错误(STDERR)流直接重定向到文件

例:

// test.js
(function() {
    // Below outputs are sent to Standard Out (STDOUT) stream
    console.log("Hello Log");
    console.info("Hello Info");
    // Below outputs are sent to Standard Error (STDERR) stream
    console.error("Hello Error");
    console.warn("Hello Warning");
})();

节点test.js> test.log 2>&1

根据POSIX标准,“输入”,“输出”和“错误”流由正整数文件描述符(0、1、2)标识。即,stdin为0,stdout为1,stderr为2。

'2>&1'将从2(stderr)重定向到1(stdout)

'>'将从1(stdout)重定向到文件(test.log)


5

覆盖console.log是解决方法。但是,要使其在必需的模块中工作,还需要导出它。

module.exports = console;

为了省去编写日志文件,旋转和填充内容的麻烦,您可以考虑使用像winston这样的简单记录器模块:

// Include the logger module
var winston = require('winston');
// Set up log file. (you can also define size, rotation etc.)
winston.add(winston.transports.File, { filename: 'somefile.log' });
// Overwrite some of the build-in console functions
console.error = winston.error;
console.log = winston.info;
console.info = winston.info;
console.debug = winston.debug;
console.warn = winston.warn;
module.exports = console;

是的 抱歉。我的错
SimonRigét17年

您可以覆盖global对象上的控制台属性。为什么module.exports呢?
r3wt

3

方法标准和标准

这种方法可以为您提供帮助(我在项目中使用了类似的方法),并且适用于所有方法,包括console.log,console.warn,console.error,console.info

此方法将以stdout和stderr写入的字节写入文件。比更改console.log,console.warn,console.error,console.info方法更好,因为输出将与此方法输出完全相同


var fs= require("fs")
var os= require("os")
var HOME= os.homedir()
var stdout_r = fs.createWriteStream(HOME + '/node.stdout.log', { flags: 'a' })
var stderr_r = fs.createWriteStream(HOME + '/node.stderr.log', { flags: 'a' })

var attachToLog= function(std, std_new){

    var originalwrite= std.write
    std.write= function(data,enc){
        try{
            var d= data
            if(!Buffer.isBuffer(d))
                d= Buffer.from(data, (typeof enc === 'string') ? enc : "utf8")
            std_new.write.apply(std_new, d)
        }catch(e){}
        return originalwrite.apply(std, arguments)
    }


}
attachToLog(process.stdout, stdout_r)
attachToLog(process.stderr, stderr_r)

// recommended catch error on stdout_r and stderr_r
// stdout_r.on("error", yourfunction)
// stderr_r.on("error", yourfunction)

我将在我的npm模块中使用此代码,并将此代码归因于您。那很酷吗?
r3wt

2

直接来自控制台上的nodejs的API文档

const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
// custom simple logger
const logger = new Console(output, errorOutput);
// use it like console
const count = 5;
logger.log('count: %d', count);
// in stdout.log: count 5

1

现在,您可以使用Caterpillar,它是基于流的日志记录系统,允许您对其进行日志记录,然后将输出通过管道传递到不同的变换和位置。

输出到文件很容易:

var logger = new (require('./').Logger)();
logger.pipe(require('fs').createWriteStream('./debug.log'));
logger.log('your log message');

卡特彼勒网站上的完整示例



0

我想到了将输出流交换到我的流的想法。

const LogLater                = require ('./loglater.js');
var logfile=new LogLater( 'log'+( new Date().toISOString().replace(/[^a-zA-Z0-9]/g,'-') )+'.txt' );


var PassThrough = require('stream').PassThrough;

var myout= new PassThrough();
var wasout=console._stdout;
myout.on('data',(data)=>{logfile.dateline("\r\n"+data);wasout.write(data);});
console._stdout=myout;

var myerr= new PassThrough();
var waserr=console._stderr;
myerr.on('data',(data)=>{logfile.dateline("\r\n"+data);waserr.write(data);});
console._stderr=myerr;

loglater.js:

const fs = require('fs');

function LogLater(filename, noduplicates, interval) {
    this.filename = filename || "loglater.txt";
    this.arr = [];
    this.timeout = false;
    this.interval = interval || 1000;
    this.noduplicates = noduplicates || true;
    this.onsavetimeout_bind = this.onsavetimeout.bind(this);
    this.lasttext = "";
    process.on('exit',()=>{ if(this.timeout)clearTimeout(this.timeout);this.timeout=false; this.save(); })
}

LogLater.prototype = {
    _log: function _log(text) {
        this.arr.push(text);
        if (!this.timeout) this.timeout = setTimeout(this.onsavetimeout_bind, this.interval);
    },
    text: function log(text, loglastline) {
        if (this.noduplicates) {
            if (this.lasttext === text) return;
            this.lastline = text;
        }
        this._log(text);
    },
    line: function log(text, loglastline) {
        if (this.noduplicates) {
            if (this.lasttext === text) return;
            this.lastline = text;
        }
        this._log(text + '\r\n');
    },
    dateline: function dateline(text) {
        if (this.noduplicates) {
            if (this.lasttext === text) return;
            this.lastline = text;
        }
        this._log(((new Date()).toISOString()) + '\t' + text + '\r\n');
    },
    onsavetimeout: function onsavetimeout() {
        this.timeout = false;
        this.save();
    },
    save: function save() { fs.appendFile(this.filename, this.arr.splice(0, this.arr.length).join(''), function(err) { if (err) console.log(err.stack) }); }
}

module.exports = LogLater;

0

在Andres Riofrio上进行改进,以处理任意数量的参数

var fs = require('fs');
var util = require('util');

var log_file = fs.createWriteStream(__dirname + '/debug.log', {flags : 'w'});
var log_stdout = process.stdout;

console.log = function(...args) {
    var output = args.join(' ');
    log_file.write(util.format(output) + '\r\n');
    log_stdout.write(util.format(output) + '\r\n');
};


0

我自己只是从winston那里举了一个例子,并添加了log(...)方法(因为winston命名为info(..)

Console.js:

"use strict"

// Include the logger module
const winston = require('winston');

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    transports: [
        //
        // - Write to all logs with level `info` and below to `combined.log`
        // - Write all logs error (and below) to `error.log`.
        //
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});

//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
    logger.add(new winston.transports.Console({
        format: winston.format.simple()
    }));
}

// Add log command
logger.log=logger.info;

module.exports = logger;

然后只需在您的代码中使用:

const console = require('Console')

现在,您只需在文件中使用常规日志功能,它将创建一个文件并将其记录到控制台(在调试/开发过程中)。因为if (process.env.NODE_ENV !== 'production') {(如果您也想在生产中使用它)...


温斯顿不添加诸如“消息”和日志级别之类的垃圾吗?
G_V

@G_V是什么意思?winston非常灵活,您可以对其进行配置...您是否正在寻找... github.com/winstonjs/winston#formats
TechupBusiness
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.