Node.js 基于流将日志、错误分别写入不同文件

 2016年07月31日    395     声明


在一个用户进程中存在标准输入(stdin)、标准输出(stdout)、标准错误(stderr)三种流。Node.js中,对日志的操作是基于stdoutstderr两种流,如:console.log方法是向stdout写入数据,console.error是向stderr写入数据。通过stdoutstderr,或实现自定义Console对象,可以将普通日志及错误日志分别写入到不同的文件中。

  1. Console类及console对象
  2. 基于进程对象process将日志、错误写入不同文件
  3. 基于自定义Console实例将日志、错误写入不同文件

1. Console类及console对象

1.1 console对象

console对象是Node.js全局对象之一,它是一个Console类的实例。该对象中包含以下方法,可以将日志数据分别写入stdoutstderr

  • console.log:向stdout写入一行数据,console.info是其别名方法
  • console.error:向stderr写入一行数据,console.warn是其别名方法

1.2 Console

Console类有以下两种访问方式,自定义Console类实例中同样有console.logconsole.error等方法。

通过console获取Console类:

// 自定义 Console 实例
const myConsole = new console.Console(process.stdin, process.stderr);

// 向 stdin 写入数据
myConsole.log('hello world');
// 向 stderr 写入数据
myConsole.error(new Error('Whoops, something bad happened'));

通过require('console')获取Console类:

const Console = require('console').Console;
// 自定义 Console 实例
const myConsole = new Console(process.stdin, process.stderr);

// 向 stdin 写入数据
myConsole.log('hello world');
// 向 stderr 写入数据
myConsole.error(new Error('Whoops, something bad happened'));


2. 基于进程对象process将日志、错误写入不同文件

process同样是一个全局对象,它提供了对标准流的访问接口。而process.stdoutprocess.stderr都是可写流,要将process.stdoutprocess.stderr中的数据写入不同的文件,首先需要将其转换为一个可读流双工流

const fs = require('fs');
const stream = require('stream');
const rawStdout = process.stdout; 
const rawStderr = process.stderr; 

// 创建一个PassThrough流
const newStdout = new stream.PassThrough();
const newStderr = new stream.PassThrough();

// 重新定义 process.stdout 的Getter
process.__defineGetter__('stdout',function(){
  // 原样返回  PassThrough
  return newStdout;
});
// 重新定义 process.stderr 的Getter
process.__defineGetter__('stderr',function(){
  // 原样返回  PassThrough
  return newStderr;
});
// 将 process.stdout 中的数据写入 stdout.log 文件
newStdout.pipe(fs.createWriteStream('./stdout.log'));
// 将 process.stderr 中的数据写入 stderr.log 文件
newStderr.pipe(fs.createWriteStream('./stderr.log'));

console.log('向 stdout 中写入数据');
console.error('向 stderr 中写入数据');

在上面我们通过定义stdoutstderr的访问器(Getter),将其重新实现为一个PassThrough流,并通过这个流将标准输出和标准错误中的数据分别写入了不同的文件。

注意:PassThrough是一种特殊的Transform,它会将写入的数据原样输出,而Transform同时又是一个双工流。


3. 基于自定义Console实例将日志、错误写入不同文件

Console类的构造函数结构如下:

new Console(stdout[, stderr])

其构造函数中包含一个标准输出和一个可选的标准错误参数。全局console对象的构造如下:

new Console(process.stdout, process.stderr);

要实现日志、错误写入不同文件,创建自定义Console并将两个文件流参数传入构造函数即可:

const fs = require('fs');
const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
// 自定义日志打印
var logger = new console.Console(output, errorOutput);

logger.log('向 stdout 中写入数据');
logger.error('向 stderr 中写入数据');

这样就通过自定义Console对象将日志、错误写入了不同的文件。要全局使用这个自定义对象,将其添加到Global对象,并使用logger代替console即可。