Open brunoyang opened 9 years ago
仓库:github.com/visionmedia/debug
debug是由tj开发的一款精巧的node debug工具。
本文根据debug@2.2.0版本所撰写。
debug@2.2.0
官方示例
在require进debug时,会再跟上一个参数,也就是所谓的namespace,以区分这条debug日志是从哪一个模块打出来。调用debug函数时,自动带上namespace前缀。
namespace
根据package.json,入口为node.js。所以我们先来看node.js。
exports = module.exports = debug;
可能有的读者会感到奇怪,为什么要将module.exports等于exports?
module.exports
exports
其实这是一个小技巧,如果我们不写上面这一句,要暴露多个变量至外部时,就需要像下面这样写:
module.exports = debug; function createApplication() { // ... } module.exports.color = color; module.exports.enabled = false;
而当我们加上exports = module.exports时,就可以省下一些字符:
exports = module.exports
exports = module.exports = debug; function debug() { // ... } exports.color = color; exports.enabled = false;
这两种写法的结果是一样的,都是一个debug函数带着几个属性:
debug
{ [Function: debug] color: [object], enabled: false }
我们接下去看,直接看最后一行,exports.enable(load()),load函数取得环境变量DEBUG并传递给enable函数,如输入DEBUG=http,worker node index.js,DEBUG的值就是http,worker。接着传递给enable函数,enable函数先做一步非空校验,再通过逗号或空格分割成数组后,根据前缀是否带有-分别传递给skips和names数组,skips数组里存有不在命令行里输出的namespace。
exports.enable(load())
load
DEBUG
enable
DEBUG=http,worker node index.js
-
skips
names
再来看debug.js中的debug函数,该函数是这个debug包的核心。该函数内有两个函数,分别为enable和disable,当这个namaspace在names数组中时,会返回enable函数;而遇到其他情况,如命令行中DEBUG参数和程序中的namespace不相符,或DEBUG参数前带有-号,则返回enable函数(见该函数最后几行)。
disable
disable函数没什么好讲的,只是带了个enable属性为false的空函数。enable函数带有几个属性,包括为了统计操作耗时的diff、prev、curr,是否使用颜色的useColor,和使用的颜色color。useColor通过判断命令行是否输入DEBUG_COLOR和输入的值来返回一个布尔值,color则是通过selectColor函数为每个namespace分配一个颜色,colors数组里面的值是ansi color的的值。
false
diff
prev
curr
useColor
color
selectColor
colors
再接下去看对传入参数的处理。通过slice函数将arguments转成一个真正的数组,然后通过coerce函数判断传入的是或否为一个Error对象,是的话则需要打印出stack及message。接着通过正则将第一个参数中的占位符替换成后面跟着的参数。若参数为函数,则先取得返回值再replace。随后,被传递进去的参数用splice方法删掉。重复上述步骤就可以将一段带有占位符字符串替换为格式化后的字符串,和console的功能相同。
slice
arguments
coerce
Error
stack
message
replace
splice
console
debug('my name is %s, %d years old', 'bruno', 20); // my name is bruno, 20 years old
大家可能会有疑问,既然有console为什么还需要多此一举?其实是因为console出来的字符串不够美观,信息量也不够大,需要进行进一步处理,调用formatArgs函数为上面这段字符串加上颜色和运行时间。
formatArgs
到这一步,需要显示出来的字符串已经处理完成,接下来就该处理显示到哪里了。debug除了可以将日志打印到控制台,也可以输出到文件。log函数就是来完成这件事的。
log
log函数的后半段就是一个console,但console到哪里呢,由stream决定。stream是由fd决定的,fd又是一个从命令行输入的参数DEBUG_FD,默认为2,比较有意思的是createWritableStdioStream函数,其中process.binding是没有在官方api文档里提及的api,是一个比较底层的api。通过这个函数可以决定输出的位置。
stream
fd
DEBUG_FD
2
createWritableStdioStream
process.binding
至于browser.js中的内容,是将日志输出值到浏览器的 console tab,差别并不大,不再赘述。
仓库: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
?其实这是一个小技巧,如果我们不写上面这一句,要暴露多个变量至外部时,就需要像下面这样写:
而当我们加上
exports = module.exports
时,就可以省下一些字符:这两种写法的结果是一样的,都是一个
debug
函数带着几个属性:我们接下去看,直接看最后一行,
exports.enable(load())
,load
函数取得环境变量DEBUG
并传递给enable
函数,如输入DEBUG=http,worker node index.js
,DEBUG的值就是http,worker。接着传递给enable函数,enable函数先做一步非空校验,再通过逗号或空格分割成数组后,根据前缀是否带有-
分别传递给skips
和names
数组,skips
数组里存有不在命令行里输出的namespace。再来看debug.js中的debug函数,该函数是这个debug包的核心。该函数内有两个函数,分别为
enable
和disable
,当这个namaspace在names数组中时,会返回enable
函数;而遇到其他情况,如命令行中DEBUG参数和程序中的namespace不相符,或DEBUG参数前带有-
号,则返回enable
函数(见该函数最后几行)。disable
函数没什么好讲的,只是带了个enable
属性为false
的空函数。enable
函数带有几个属性,包括为了统计操作耗时的diff
、prev
、curr
,是否使用颜色的useColor
,和使用的颜色color
。useColor
通过判断命令行是否输入DEBUG_COLOR和输入的值来返回一个布尔值,color
则是通过selectColor
函数为每个namespace
分配一个颜色,colors
数组里面的值是ansi color的的值。再接下去看对传入参数的处理。通过
slice
函数将arguments
转成一个真正的数组,然后通过coerce
函数判断传入的是或否为一个Error
对象,是的话则需要打印出stack
及message
。接着通过正则将第一个参数中的占位符替换成后面跟着的参数。若参数为函数,则先取得返回值再replace
。随后,被传递进去的参数用splice
方法删掉。重复上述步骤就可以将一段带有占位符字符串替换为格式化后的字符串,和console
的功能相同。大家可能会有疑问,既然有
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,差别并不大,不再赘述。