将 Node.js 配置为记录到文件而不是控制台

Posted

技术标签:

【中文标题】将 Node.js 配置为记录到文件而不是控制台【英文标题】:Configure Node.js to log to a file instead of the console 【发布时间】:2012-01-13 16:25:51 【问题描述】:

我可以配置console.log 以便将日志写入文件而不是在控制台中打印吗?

【问题讨论】:

可以,但是对于长时间运行的生产应用程序,很难找到特定日期的日志,对吧? @racar 【参考方案1】:

2013 年更新 - 这是围绕 Node v0.2 和 v0.4 编写的;现在围绕日志记录有更好的实用程序。我强烈推荐Winston

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);

【讨论】:

Nvm,这意味着我认为的过程......这是如何工作的? console.log(whatever); 仍然进入控制台,而不是文件。 由于最近的更改,您不能再调用 stderr.pipe() - 现在需要这个:process.__defineGetter__('stderr', function() return fs.createWriteStream(__dirname + '/error.log', flags:'a') ) 我个人会避开温斯顿。我们已经使用了一年多,最终决定完全删除它,因为它在我们的生产环境中造成了许多问题。该项目现在似乎维护得很差。只要您的手臂,就有一个问题列表。许多问题都有其他用户提交的拉取请求,但项目维护人员甚至没有花时间合并这些问题。对于替代方案 node-bunyan 和 Caterpillar 可能值得一看。 维护者没有响应很糟糕。我认为它是由 Nodejitsu 人维护的。我们已经在几个网站上使用了它,每天的页面浏览量超过一百万,并且在日志记录方面没有任何问题。也许这与记录的内容和数量有关。 截至 2015 年 4 月,Winston 似乎是最新的并且收到了大量的活动。【参考方案2】:

如果这是针对应用程序,您最好使用日志记录模块。它会给你更多的灵活性。一些建议。

温斯顿https://github.com/winstonjs/winston log4js https://github.com/nomiddlename/log4js-node

【讨论】:

您的 log4js 链接现在已损坏。这个怎么样? github.com/nomiddlename/log4js-node 是的,我猜这个项目转手了。谢谢。【参考方案3】:

如果您正在寻找生产中的东西winston 可能是最好的选择。

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

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

【讨论】:

使用> 重定向STDOUT 也适用于Windows。 nohup 没有。 在 *nix 上没有nohup,即node simple-server.js > output.log,它可以正常工作。然后,如果您想按照日志中写的那样关注tail -f output.log 这不起作用 2hen 我将参数传递给进程 在生产中这样做有什么缺点? 顺便说一句,取决于您的操作系统,“do-something-about-it”可能只是日志轮换,这是一个很好的解决方案 imo - abdussamad.com/archives/541-Log-rotation-in-CentOS-Linux.html【参考方案4】:

您现在可以使用Caterpillar,它是一个基于流的日志系统,允许您登录到它,然后将输出通过管道传输到不同的转换和位置。

输出到文件很简单:

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

Caterpillar Website上的完整示例

【讨论】:

【参考方案5】:

你也可以重载默认的 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。

编辑:See multiparameter version by Clément 也在此页面上。

【讨论】:

我不会费心覆盖console.log。只需创建您自己的记录到特定文件的函数。 此外,这不适用于多个参数。例如。 console.log(p1, p2, p3) 很好的答案!此外,如果您想捕获多个 console.log 参数,只需抓住 'Arguments' 对象代替 'd' - developer.mozilla.org/en/docs/Web/javascript/Reference/… @AlexMills 嗨,也许您可​​以提供一个具体如何完成的示例? 但我需要在每个 js 文件中都有这个,是否可以将其设为全局?【参考方案6】:

我经常对 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;

【讨论】:

这很好,但util.format.apply(null, arguments) 似乎在程序启动时会输出大量数据。就像它输出每个必需的对象一样。我不知道如何阻止它,但上面的单参数方法不会发生这种情况。【参考方案7】:

覆盖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;

【讨论】:

是的。对不起。我的坏 您可以覆盖global 对象上的控制台属性。为什么module.exports【参考方案8】:

你也可以看看这个 npm 模块: https://www.npmjs.com/package/noogger

noogger

简单直接...

【讨论】:

【参考方案9】:

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`);

它还会按日期将所有成功日志和错误日志记录在日志目录下的文件中,如您在此处所见。

【讨论】:

日志在控制台中可见。并且没有立即创建文件!我错过了什么吗? 你能分享你的配置吗?还是您按照我的建议使用它?如果配置正常,请检查一次导入(winstonwinston-daily-rotate-file)。它们应该在项目根目录中名为logs 的文件夹中创建。请原谅我的延迟回复。 @Nigilan 你是怎么解决这个问题的?我也面临同样的问题。 @user2180794 看看我在 Nigilan 评论上方的评论。如果您仍然无法完成,请告诉我们。 不要忘记将这些添加到 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; 任何你想记录的地方。【参考方案10】:

另一个尚未提及的解决方案是将Writable 流挂接到process.stdoutprocess.stderr。这样你就不需要覆盖all the console functions 到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

欲了解更多信息,请查看此gist。

【讨论】:

正确答案。【参考方案11】:
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

【讨论】:

可能只是为了保存错误? 不要循环遍历 Console.prototype 键,只需明确设置 this.error 即可。 这会破坏 console.log? 它不会破坏console.log。它会更改其行为,但您可以通过调用 console.file() 来恢复旧行为。【参考方案12】:

直接来自 Console 上的 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

【讨论】:

你的例子对我有用,但你应该写 const logger = new console.Console(output, errorOutput);【参考方案13】:

我接受了将输出流交换为我的流的想法。

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;

【讨论】:

【参考方案14】:

改进 Andres Rioofrio,以处理任意数量的参数

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');
;

【讨论】:

【参考方案15】:

我只是做了一个包来做这个,希望你喜欢它;) https://www.npmjs.com/package/writelog

【讨论】:

【参考方案16】:

我自己只是简单地从 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') (如果你也想在生产中使用它)......

【讨论】:

winston 不会添加一堆像“消息”和日志级别这样的垃圾吗? @G_V 你是什么意思? winston 很灵活,可以配置...你在找...github.com/winstonjs/winston#formats?【参考方案17】:

对于简单的情况,我们可以使用 '>' 将 标准输出 (STDOUT) 和标准错误 (STDERR) 流直接重定向到文件(例如 test.log)'2>&1'

例子:

// 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");
)();

node test.js > test.log 2>&1

根据 POSIX 标准,'input'、'output' 和 'error' 流由正整数文件描述符标识(0, 1, 2)。即,stdin 为 0,stdout 为 1,stderr 为 2。

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

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

【讨论】:

这对于您只想获取应用程序日志而无需处理控制台换行等简单情况非常有用【参考方案18】:

方法标准输出和标准错误

这种方法可以帮助您(我在我的项目中使用类似的东西)并且适用于所有方法,包括 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 模块中使用它并将此代码归于你。很酷吗?【参考方案19】:

供未来的用户使用。 @keshavDulal answer 不适用于最新版本。对于最新版本3.3.3 中报告的问题,我找不到合适的解决方案。

无论如何,经过一番研究,我终于修复了它。这是winston版本的解决方案3.3.3

安装 winston 和 winston-daily-rotate-file

npm install winston 
npm install winston-daily-rotate-file

创建一个新文件 utils/logger.js

const winston = require('winston');
const winstonRotator = require('winston-daily-rotate-file');

var logger = new winston.createLogger(
  transports: [
    new (winston.transports.DailyRotateFile)(
      name: 'access-file',
      level: 'info',
      filename: './logs/access.log',
      json: false,
      datePattern: 'yyyy-MM-DD',
      prepend: true,
      maxFiles: 10
    ),
    new (winston.transports.DailyRotateFile)(
      name: 'error-file',
      level: 'error',
      filename: './logs/error.log',
      json: false,
      datePattern: 'yyyy-MM-DD',
      prepend: true,
      maxFiles: 10
    )
  ]
);


module.exports = 
  logger
;

然后在您要使用日志记录的任何文件中导入模块,如

const logger = require('./utils/logger').logger;

像下面这样使用记录器:

logger.info('Info service started');
logger.error('Service crashed');

【讨论】:

【参考方案20】:

如果您使用的是 linux,您还可以使用输出重定向。不确定 Windows。

node server.js >> file.log 2>> file.log

>> file.logstdout 重定向到文件

2>> file.logstderr 重定向到文件

其他人对stdoutstderr 都使用简写&>>,但我的mac 和ubuntu 都不接受它:(

额外:> 覆盖,而>> 追加。

顺便说一下,关于 NodeJS 记录器,我使用 pino + pino-pretty 记录器

【讨论】:

【参考方案21】:

除了上面的答案之外,对覆盖console.log 的简短而有效的代码进行了一点扩展。次要添加:设置文件名和日期,包装函数,还可以使用原始的 console.logging 来保持控制台的活动状态。

用法:在代码的开头,运行setConsoleLogToFile([FILENAME])

const fs = require("fs"),
    util = require('util');


const getPrettyDate = ()=> new Date().toString().replace(":","-").replace(/00\s\(.*\)/, "").replace(` $new Date().getFullYear()`, ",").replace(/:\d\d\s/, " ");

module.exports.getPrettyDate = getPrettyDate;

module.exports.setConsoleLogToFile = (filename) => 
    const log_file = fs.createWriteStream(`$__dirname/$filename -  $getPrettyDate().log`,  flags: 'w' ),
        log_stdout = process.stdout;

    const origConsole = console.log;
    console.log = (d) =>  
        origConsole(d);
        log_file.write(util.format(d) + '\n');
        log_stdout.write(util.format(d) + '\n');
    ;

【讨论】:

【参考方案22】:

大多数记录器都是矫枉过正,不支持正确构建console.log。因此我创建了console-log-to-file:

import  consoleLogToFile  from "console-log-to-file";
// or `const  consoleLogToFile  = require("console-log-to-file/dist/index.cjs.js")`

consoleLogToFile(
  logFilePath: "/log/default.log",
);

// all of your console.log/warn/error/info will work as it does and save to file now.

【讨论】:

【参考方案23】:

如果您使用forever 来保持您的节点应用程序运行,那么输入forever list 将向您显示console.log 正在写入的日志文件的路径

【讨论】:

【参考方案24】:

如果您正在寻找无需修改任何代码的解决方案,这里有一个简单的解决方案。

它需要 pm2,只需将其添加到您的节点模块并启动您的应用程序

pm2 start server.js

大功告成,console.logs 现在自动home/.pm2/logs/server-out.log 下注册。

【讨论】:

【参考方案25】:

创建一个 utils/logger.js 文件:

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

console.log = function ()  //
    [...arguments].forEach(element => 
        log_file.write(util.format(element) + '\n');
        log_stdout.write(util.format(element) + '\n');
    );
;

module.exports = 
    console

从任何你想要 console.log 的文件中包含 logger.js 文件,例如:

const console = require('./utils/logger').console;

创建一个日志文件夹并在其中创建一个空的 server.log 文件并运行您的应用程序 :)

【讨论】:

【参考方案26】:

基于multiparameter version by Clément,只是文本文件没有颜色代码

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

console.log = function () 
  // Storing without color codes
  logFile.write(util.format.apply(null,arguments).replace(/\033\[[0-9;]*m/g,"") + '\n');
  // Display normally, with colors to Stdout
  logStdout.write(util.format.apply(null, arguments) + '\n');

注意:因为无法发表评论,所以回答

【讨论】:

以上是关于将 Node.js 配置为记录到文件而不是控制台的主要内容,如果未能解决你的问题,请参考以下文章

将 Node 的“永久”日志记录到 syslog

将存储在 S3 存储桶中的文件直接下载到 React 客户端,而不是通过 Node.js 服务器

如何配置Railo以便cflog将其数据记录到控制台

未设置 Node.js 响应标头

Node.js 模块之 morgan中间件记录日志

如何将 Node.js V8 分析信息导出到 Chrome 的开发工具性能配置文件?