debug模块源码
1.debug日志打印模块
主要实现功能:带命名空间(模块名)、时间戳、色彩输出日志;将日志写入文件;浏览器端使用;格式化函数;支持自定义方法。
实现思路:通过环境变量设置配置项如是否启用有彩色输出、设定打印方法、设定命名空间(模块名);
也可以通过require("debug").enable(namespace)添加命名空间,require("debug").enabled.useColors是否启用有彩色输出,require("debug").enabled.log自定义打印方法,require("debug").enabled.color社会颜色1|2|3|4|5|6,exports.formatters.a=function(){}添加格式化函数
2.使用:默认使用process.stderr('%s',"error")标准错误输出,替换%s字符
require("debug").enable("app") // 添加待测试模块
var debug=require("debug")("app") // 选择测试模块
debug("%o funciton(){}") // 打印日志3.源码:服务器端启动文件为node.js,通过node.js加载debug.js
node.js
// package.json约定node.js为入口文件
var tty=require('tty');
var util=require('util');
exports=module.exports=require('./debug');
exports.log=log;
// 带色彩打印消息
exports.formatArgs=formatArgs;
// 更新process.env.DEBUG环境变量
exports.save=save;
// 待测试的模块通过环境变DEBUG量赋值
exports.load=load;
// 是否开启带颜色打印消息
exports.useColors=useColors;
// 颜色码
exports.colors=[6, 2, 3, 4, 5, 1];
/**
* The file descriptor to write the `debug()` calls to.
* Set the `DEBUG_FD` env variable to override with another value. i.e.:
*
* $ DEBUG_FD=3 node script.js 3>debug.log
*/
var fd=parseInt(process.env.DEBUG_FD, 10) || 2;
var stream= 1===fd ? process.stdout :// 标准输出
2===fd ? process.stderr :// 标准错误输出,默认
createWritableStdioStream(fd);// 流式写入文件
// 是否开启带颜色输出
function useColors(){
var debugColors=(process.env.DEBUG_COLORS || '').trim().toLowerCase();
if ( 0===debugColors.length ){
return tty.isatty(fd);
}else{
return '0'!==debugColors
&& 'no'!==debugColors
&& 'false'!==debugColors
&& 'disabled'!==debugColors;
}
}
// util.inspect(obj,showHidden,depth,color)方法将对象转换为字符串,
// showHidden显示更多数据,depth递归的层数,color设置颜色
var inspect=( 4===util.inspect.length ?
// node <= 0.8.x
function (v, colors){
return util.inspect(v,void 0,void 0,colors);//
} :
// node > 0.8.x
function (v, colors){
return util.inspect(v,{colors:colors});
}
);
// 类型不是字符串时,使用util.inspect方法转化成字符串后输出
exports.formatters.o=function(v){
return inspect(v,this.useColors).replace(/\s*\n\s*/g, ' ');
};
// 带色彩打印消息
function formatArgs() {
var args=arguments;
var useColors=this.useColors;
var name=this.namespace;
if ( useColors ){
var c=this.color;
args[0]=' \u001b[3'+c+';1m'+name+' '
+'\u001b[0m'
+args[0]+
'\u001b[3'+c+'m'+' +'+exports.humanize(this.diff)+'\u001b[0m';
}else{
args[0]=new Date().toUTCString()+' '+name+' '+args[0];
}
return args;
}
/**
* Invokes `console.error()` with the specified arguments.
*/
function log(){
return stream.write(util.format.apply(this,arguments)+'\n');
}
// 更新process.env.DEBUG环境变量
function save(namespaces){
if ( null==namespaces ){
delete process.env.DEBUG;
}else{
process.env.DEBUG = namespaces;
}
}
// 待测试的模块通过环境变DEBUG量赋值
function load(){
return process.env.DEBUG;
}
/**
* Copied from `node/src/node.js`.
*
* XXX: It's lame that node doesn't expose this API out-of-the-box. It also
* relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
*/
// 写入文件
function createWritableStdioStream (fd){
var stream;
var tty_wrap=process.binding('tty_wrap');
// Note stream._type is used for test-module-load-list.js
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';
break;
case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true
});
// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false;
stream.read = null;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
}
// For supporting legacy API we put the FD here.
stream.fd=fd;
stream._isStdio=true;
return stream;
}
// 待测试的模块通过环境变DEBUG量赋值
exports.enable(load());debug.js
// 使用前需要调用require("debug").enable(namespace)添加待测试模块,debug函数方可用
// 因此可用于区分开发环境和线上环境,开发环境用enable函数开启测试功能,线上则否
exports=module.exports=debug;
// 若为错误,返回错误的堆栈信息或错误消息,否则原样输出
exports.coerce=coerce;
// 更新process.env.DEBUG环境变量,没有清空exports.skips、exports.names忽略和待测试模块队列
exports.disable=disable;
// exports.names、exports.skips添加待测试模块及忽略模块
exports.enable=enable;
// 待测试的模块返回真值,其余返回否,exports.skips需要忽略的模块
exports.enabled=enabled;
exports.humanize = require('ms');
exports.names=[];// 需要测试的模块
exports.skips=[];// 需要忽略的模块
// 以小写字母作为键(o存储非字符串时的格式化函数),存储debug函数打印信息的格式化函数
// require("debug").formatters={a:function(str){return str.replace(/%/,"")}}
// var debug=require("debug").debug("app")
// debug("%123") 将打印app 123 +0ms
exports.formatters={};
var prevColor=0;// exports.colors起始位
var prevTime;// 缓存上次执行的时间
// 获取颜色码
function selectColor(){
// 取余数,选择颜色码,用以拼接颜色字符串"\u001b[3"+"6|2|3|4|5|1"
return exports.colors[prevColor++ % exports.colors.length];
}
// 参数namespace为模块名,返回函数,用于打印消息
function debug(namespace){
function disabled(){
}
disabled.enabled=false;
function enabled(){
var self=enabled;
// 计算执行时间
var curr=+new Date();
var ms=curr-(prevTime || curr);
self.diff=ms;
self.prev=prevTime;
self.curr=curr;
prevTime=curr;
if ( null==self.useColors ) self.useColors=exports.useColors();// 是否开启带颜色输出
if ( null==self.color && self.useColors ) self.color=selectColor();// 颜色码6|2|3|4|5|1
var args=Array.prototype.slice.call(arguments);
args[0]=exports.coerce(args[0]);// 处理错误(返回错误堆栈或错误消息)
// exports.formatters.o方法,类型不是字符串时,使用util.inspect方法转化成字符串后输出
if ( 'string'!==typeof args[0] ){
args=['%o'].concat(args);
}
var index=0;
// match匹配项,format括号内匹配项,没有匹配项将不执行回调函数
args[0]=args[0].replace(/%([a-z%])/g, function(match,format){
// %% 起始原样打印
if ( match==='%%' ) return match;
// %[a-z] 起始,且exports.formatters存在相应格式化函数,执行格式化、替换前两项后输出
index++;
var formatter=exports.formatters[format];
if ( 'function'===typeof formatter ){
var val=args[index];
match=formatter.call(self, val);
args.splice(index, 1);
index--;
}
return match;
});
// 带色彩打印消息
if ( 'function'===typeof exports.formatArgs ){
args=exports.formatArgs.apply(self, args);
}
// enabled.log用户自定义打印方法; exports.log利用stream流打印消息
var logFn=enabled.log || exports.log || console.log.bind(console);
logFn.apply(self,args);
}
enabled.enabled=true;
// 倘若namespace为待测试模块,fn引用enabled函数,否则引用disabled函数
var fn=exports.enabled(namespace) ? enabled : disabled;
fn.namespace=namespace;
return fn;
}
// 字符串形式设置待测试以及忽略的模块,多个模块以空格分割,忽略的模块以"-"起始
// 将模块的正则式推入exports.names或exports.skips中
function enable(namespaces){
exports.save(namespaces);// 更新process.env.DEBUG环境变量
var split=(namespaces || '').split(/[\s,]+/);
var len=split.length;
for ( var i=0; i<len; i++ ){
if (!split[i]) continue; // ignore empty strings
namespaces=split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-'){
exports.skips.push(new RegExp('^'+namespaces.substr(1)+'$'));
}else{
exports.names.push(new RegExp('^'+namespaces+'$'));
}
}
}
// 没有清空exports.skips、exports.names忽略和待测试模块队列,函数的意义何在???
function disable() {
exports.enable('');
}
// 待测试的模块返回真值,其余返回否,exports.skips需要忽略的模块
function enabled(name){
var i,len;
for ( i=0, len=exports.skips.length; i<len; i++ ){
if ( exports.skips[i].test(name) ){
return false;
}
}
for ( i=0, len=exports.names.length; i<len; i++ ){
if ( exports.names[i].test(name) ){
return true;
}
}
return false;
}
// 若为错误,返回错误的堆栈信息或错误消息,否则原样输出
function coerce(val){
if ( val instanceof Error ) return val.stack || val.message;
return val;
}4.存疑:日志输出为文件,客户端使用
相关推荐
瓜牛呱呱 2020-11-12
柳木木的IT 2020-11-04
yifouhu 2020-11-02
lei0 2020-11-02
源码zanqunet 2020-10-26
码代码的陈同学 2020-10-14
lukezhong 2020-10-14
clh0 2020-09-18
changcongying 2020-09-17
星辰大海的路上 2020-09-13
abfdada 2020-08-26
mzy000 2020-08-24
shenlanse 2020-08-18
zhujiangtaotaise 2020-08-18
xiemanR 2020-08-17