Node.js 之 Console 日誌模塊

Console 日誌模塊提供了調試功能,測試調試中使用很方便、也用的最多的。它和瀏覽器中的 console 相似。javascript

造輪子日誌模塊 Log

  • 初始化 Log 對象;
  • 參數校驗:當前對象是否爲 Log 實例(安全模式),是否爲可寫流實例;
  • Log 對象定義 stdout,stderr 等屬性;
  • 將原型方法上的屬性綁定到 Log 實例上;
  • 實現 log、error、warning、trace、clear 等方法;

編碼

// log.js
const util = require('util');

/** * 初始化對象 Log * @param {*} stdout * @param {*} stderr */
function Log(stdout, stderr) {
    // 安全模式,檢查當前對象是不是 Log 的實例
    if (!(this instanceof Log)) {
        return new Log(stdout, stderr);
    }

    // 檢查是不是可寫流實例
    if (!stdout || !(stdout.write instanceof Function)) {
        throw new Error('可寫流實例不存在');
    }

    // stderr 未指定就指定 stdout
    if (!stderr) {
        stderr = stdout;
    }

    let props = {
        writable: true,
        enumerable: false,
        configurable: false
    }

    // 定義屬性 _stdout
    Object.defineProperty(this, '_stdout', Object.assign(props, {
        value: stdout
    }));

    // 定義屬性 _stderr
    Object.defineProperty(this, '_stderr', Object.assign(props, {
        value: stderr
    }));

    // 定義屬性 _times
    Object.defineProperty(this, '_time', Object.assign(props, {
        value: new Map()
    }));

    // 將原型上的方法綁定到 Log 實例上
    const keys = Object.keys(Log.prototype);

    for (let key in keys) {
        const item = keys[key];
        this[item] = this[item].bind(this);
    }
}

// 定義原型上的方法
Log.prototype.info = function () {
    this._stdout.write(util.format.apply(this, arguments) + '\n');
}

Log.prototype.log = Log.prototype.info;

Log.prototype.error = function () {
    this._stderr.write(util.format.apply(this, arguments) + '\n');
}

Log.prototype.warn = Log.prototype.error;

// 堆棧信息
Log.prototype.trace = function trace(...args) {
    const err = {
        name: 'Trace',
        message: util.format.apply(null, args)
    };

    // V8 引擎的 Stack Trace API
    Error.captureStackTrace(err, trace);
    this.error(err.stack);
}

// 輸出某個對象
Log.prototype.dir = function (object, options) {
    options = Object.assign({ customInspect: false }, options);
    this._stdout.write(util.inspect(object, options) + '\n');
}

// 計時器
Log.prototype.time = function(label){
    // this._time 是一個 Map
    this._time.set(label, process.hrtime());
}

Log.prototype.timeEnd = function(label){
    const timeLabel = this._time.get(label);
    if(!timeLabel) {
        process.emitWarning(`沒有時間標誌 ${label}`);
        return
    }
    const duration = process.hrtime(timeLabel);
    const ms = duration[0] * 1000 + duration[1] / 1e6;
    this.log('標籤%s: 耗時 %sms', label, ms.toFixed(3));
    this._time.delete(label);
}

// 清除控制檯信息
Log.prototype.clear = function () {
    if (this._stdout.isTTY) {
        const { cursorTo, clearScreenDown } = require('readline');
        cursorTo(this._stdout, 0, 0); // 光標移到 TTY stream 指定位置
        clearScreenDown(this._stdout); // 從光標的當前位置向下清除給定的 TTY 流
    }
}

const stdout = process.stdout;
const stderr = process.stderr;
module.exports = new Log(stdout, stderr);
module.exports.Log = Log;
複製代碼

實例

// log.demo.js
const log = require('./log.js');
const fs = require('fs');
const { Log } = require('./log.js');

const stdout = fs.createWriteStream('./stdout.txt');
const stderr = fs.createWriteStream('./stderr.txt');

const obj = {
    name: 'pr',
    age: 30,
    favorite: {
        'ball': ['籃球', '足球']
    }
}
// 日誌輸出至終端
log.log('打印日誌');

log.info('打印基礎日誌');

log.error('打印錯誤日誌');

log.warn('打印警告日誌');

log.trace('打印錯誤堆棧');

// 打印對象
log.log(obj);
log.dir(obj, { depth: 0 });

// 程序執行消耗時間
log.time('定時器 pr');
for (let i = 0; i < 1000000000; i++) { }
log.timeEnd('定時器 pr');

log.log(log._time.get('time'));
// log.clear();

// 日誌輸出至文件
const logger = Log(stdout, stderr);
logger.log('可寫流,日誌模塊 Console 方法 log');
logger.error('可寫流,日誌模塊 Console 方法 error');
複製代碼

本次代碼 Githubjava

面試題

相關文章
相關標籤/搜索