brunoyang / blog

134 stars 13 forks source link

debug包源码分析 #2

Open brunoyang opened 9 years ago

brunoyang commented 9 years ago

仓库:github.com/visionmedia/debug

debug是由tj开发的一款精巧的node debug工具。

本文根据debug@2.2.0版本所撰写。

使用方法

官方示例

源码分析

在require进debug时,会再跟上一个参数,也就是所谓的namespace,以区分这条debug日志是从哪一个模块打出来。调用debug函数时,自动带上namespace前缀。

根据package.json,入口为node.js。所以我们先来看node.js。

exports = module.exports = debug;

可能有的读者会感到奇怪,为什么要将module.exports等于exports?

其实这是一个小技巧,如果我们不写上面这一句,要暴露多个变量至外部时,就需要像下面这样写:

module.exports = debug;

function createApplication() {
    // ...
}

module.exports.color = color;
module.exports.enabled = false;

而当我们加上exports = module.exports时,就可以省下一些字符:

exports = module.exports = debug;

function debug() {
    // ...
}

exports.color = color;
exports.enabled = false;

这两种写法的结果是一样的,都是一个debug函数带着几个属性:

{
    [Function: debug]
    color: [object],
    enabled: false
}

我们接下去看,直接看最后一行,exports.enable(load())load函数取得环境变量DEBUG并传递给enable函数,如输入DEBUG=http,worker node index.js,DEBUG的值就是http,worker。接着传递给enable函数,enable函数先做一步非空校验,再通过逗号或空格分割成数组后,根据前缀是否带有-分别传递给skipsnames数组,skips数组里存有不在命令行里输出的namespace。

再来看debug.js中的debug函数,该函数是这个debug包的核心。该函数内有两个函数,分别为enabledisable,当这个namaspace在names数组中时,会返回enable函数;而遇到其他情况,如命令行中DEBUG参数和程序中的namespace不相符,或DEBUG参数前带有-号,则返回enable函数(见该函数最后几行)。

disable函数没什么好讲的,只是带了个enable属性为false的空函数。enable函数带有几个属性,包括为了统计操作耗时的diffprevcurr,是否使用颜色的useColor,和使用的颜色coloruseColor通过判断命令行是否输入DEBUG_COLOR和输入的值来返回一个布尔值,color则是通过selectColor函数为每个namespace分配一个颜色,colors数组里面的值是ansi color的的值。

再接下去看对传入参数的处理。通过slice函数将arguments转成一个真正的数组,然后通过coerce函数判断传入的是或否为一个Error对象,是的话则需要打印出stackmessage。接着通过正则将第一个参数中的占位符替换成后面跟着的参数。若参数为函数,则先取得返回值再replace。随后,被传递进去的参数用splice方法删掉。重复上述步骤就可以将一段带有占位符字符串替换为格式化后的字符串,和console的功能相同。

debug('my name is %s, %d years old', 'bruno', 20); // my name is bruno, 20 years old    

大家可能会有疑问,既然有console为什么还需要多此一举?其实是因为console出来的字符串不够美观,信息量也不够大,需要进行进一步处理,调用formatArgs函数为上面这段字符串加上颜色和运行时间。

到这一步,需要显示出来的字符串已经处理完成,接下来就该处理显示到哪里了。debug除了可以将日志打印到控制台,也可以输出到文件。log函数就是来完成这件事的。

log函数的后半段就是一个console,但console到哪里呢,由stream决定。stream是由fd决定的,fd又是一个从命令行输入的参数DEBUG_FD,默认为2,比较有意思的是createWritableStdioStream函数,其中process.binding是没有在官方api文档里提及的api,是一个比较底层的api。通过这个函数可以决定输出的位置。

至于browser.js中的内容,是将日志输出值到浏览器的 console tab,差别并不大,不再赘述。