编程式调试node程序的利器chrome-remote-interface

简介

chrome-remote-interface是chrome调试协议的第三方调试客户端实现,该项目开源,提供了命令行工具,且为node程序提供了api。
chrome-remote-interface为基于chrome调试协议编写自己的node调试工具提供了便捷的途径,因为利用它,你不需要基于原始的协议通过websocket编程去开发调试工具了。
项目地址https://github.com/cyrus-and/chrome-remote-interface

使用命令行

安装

通过npm进行安装

npm install chrome-remote-interface

启动调试目标

chrome-remote-interface基于chrome调试协议,因此其支持调试chrome浏览器和node运行环境。
无论哪种调试目标,其启动时都应该指定调试端口。
如果要调试chrome浏览器,应该在启动chrome时添加--remote-debugging-port参数,如下:

goole-chrome --remote-debugging-port=9222

如果调试node,在启动时添加--inspect参数,如下:

node --inspect=9222 app.js

此时,node会输出:

Debugger listening on port 9222.
Warning: This is an experimental feature and could change at any time.
To start debugging, open the following URL in Chrome:
    chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef

题外话:如果只是希望调试node,并不打算开发一个调试node的工具的话,根据提示中的url,在chrome中打开就直接可以用chrome的开发工具调试了。

体验命令行

  • 查看全部命令

    在终端输入chrome-remote-interface并回车,可看到如下输出:

Usage: chrome-remote-interface [options] [command]

Commands:

inspect [options] [<target>] inspect a target (defaults to the current tab)
list                   list all the available tabs
new [<url>]            create a new tab
activate <id>          activate a tab by id
close <id>             close a tab by id
version                show the browser version
protocol [options]     show the currently available protocol descriptor

Options:

-h, --help         output usage information
-t, --host <host>  HTTP frontend host
-p, --port <port>  HTTP frontend port
```

其中,new和close是针对浏览器的tab的命令,不要针对node来运行。
  • 查看所有页面实例

    chrome-remote-interface -t 127.0.0.1 -p 9222 list

    输出如下:

    [ { description: 'node.js instance',
    devtoolsFrontendUrl: 'https://chrome-devtools-frontend.appspot.com/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef',
    faviconUrl: 'https://nodejs.org/static/favicon.ico',
    id: '2894362d-f2d1-4f3b-a9e8-4e27da5714ef',
    title: 'app.js',
    type: 'node',
    url: 'file:///Users/renbaogang/git/enzyme.node/app.js',
    webSocketDebuggerUrl: 'ws://localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef' } ]
    

    其中devtoolsFrontendUrl可以在chrome地址栏中回车该url,打开chrome的开发工具,可以直接用该工具调试。和启动node时,node提示的url是一个用途。
    id是当前页面标识。
    url是当前页面url。
    webSocketDebuggerUrl是node提供的ws协议的调试服务,调试客户端需要通过该url连接到调试服务。

  • 查看调试目标版本

    chrome-remote-interface -t 127.0.0.1 -p 9222 version

    输出:

    [ { Browser: 'node.js/v7.0.0', 'Protocol-Version': '1.1' } ]
    
  • 查看调试目标支持的调试协议

    chrome-remote-interface -t 127.0.0.1 -p 9222 protocol

    输出:

    { 
    remote: false,
    descriptor: {
        { version: { major: '1', minor: '2' },
        domains:[略]
    }
    }
    

    remote:false说明协议内容是工具自带的协议文件,并不是来自调试目标。
    如果要获取调试目标的协议内容要添加-r选项,如:

    chrome-remote-interface -t 127.0.0.1 -p 9222 protocol -r

    输出:

    { 
    remote: true,
    descriptor: {
        { version: { major: '1', minor: '1' },
        domains:[略]
    }
    }
    
  • 调试命令

    chrome-remote-interface -t 127.0.0.1 -p 9222 inspect -r
    -r 表示应用调试目标提供的协议描述文件
    通过inspect可以调试node或chrome所支持的各个域。

    Console域体验

Console.enable()
{ result: {} }
Console.clearMessages()
{ result: {} }
```

Debugger域体验

```

Debugger.enable()
{ result: {} }
Debugger.setBreakpointsActive({active:true})
{ result: {} }
```

HeapProfiler域体验

```

HeapProfiler.enable()
{ result: {} }
HeapProfiler.startTrackingHeapObjects({trackAllocations:true})
{ result: {} }
```

Profiler域体验

```

Profiler.enable()
{ result: {} }
Profiler.start()
{ result: {} }
Profiler.stop()
{ result:
{ profile:
{ nodes:
[ { id: 1,
callFrame:
{ functionName: '(root)',
scriptId: '0',
url: '',
lineNumber: -1,
columnNumber: -1 },
hitCount: 0,
children: [ 2 ] },
...

      ]
    }
 }

}
```

Runtime域体验  
>>> Runtime.evaluate({expression:"1+1"})
{ result: { result: { type: 'number', value: 2, description: '2' } } }

Schema域体验

>>> Schema.getDomains()
{ result: 
   { domains: 
      [ { name: 'Runtime', version: '1.1' },
        { name: 'Debugger', version: '1.1' },
        { name: 'Profiler', version: '1.1' },
        { name: 'HeapProfiler', version: '1.1' },
        { name: 'Schema', version: '1.1' } ] } }
    ```
    
    体验事件处理
    
    scriptParsed是Debugger域提供的脚本解析事件
    
    ```
>>> Debugger.scriptParsed(params=>params.url)
{ 'Debugger.scriptParsed': 'params=>params.url' }
{ 'Debugger.scriptParsed': '' }
{ 'Debugger.scriptParsed': '/Users/renbaogang/git/enzyme.node/node_modules/negotiator/lib/encoding.js' }
    ```
    
## API ##
1. module([options], [callback])

    基于chrome调试协议连接调试目标
    
    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - chooseTab 决定调试哪个tab。该参数可以为三种类型:
        - function 提供一个返回tab序号的函数
        - object 正如new 和 list返回的对象一样
        - string websocket url
        默认为一个函数,返回当前激活状态的tab的序号,如:function(tabs){return 0;}
    - protocol 协议描述符,默认由remote选项来定是否使用调试目标提供的协议描述符
    - remote 默认false,如果protocol设置了,该选项不会起作用
    
  callback   
    如果callback是函数类型,则该函数在connect事件发生后会得到回调,并返回EventEmitter对象,如果不是,则返回Promise对象。
    
    EventEmitter对象支持如下事件:  
    
    - connect
    
        ```
    function (chrome) {}
    ```
    当websocket连接建立后触发,chrome是Chome类型的实例。
    
    - disconnect
        
        ```
        function () {}
        ```
        关闭websocket连接时触发
        
    - error
    
        ```
        function (err) {}
        ```
    当通过host:port/json无法到达,或websocket连接无法建立时触发  
        err,Error类型的错误对象
        
  使用样例,针对chrome浏览器:
    
    ```
const Chrome = require('chrome-remote-interface');
Chrome(function (chrome) {
    with (chrome) {
        Network.requestWillBeSent(function (params) {
            console.log(params.request.url);
        });
        Page.loadEventFired(function () {
            close();
        });
        Network.enable();
        Page.enable();
        once('ready', function () {
            Page.navigate({'url': 'https://github.com'});
        });
    }
}).on('error', function (err) {
    console.error('Cannot connect to Chrome:', err);
});
    ```
        
2. module.Protocol([options],[callback])

    获取chrome调试协议描述符
    
    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - remote 默认false
    
    callback 回调函数,在获取到协议内容后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - protocol 包含以下属性
        - remote 是否远程协议
        - descriptor chrome调试协议描述符
    
  使用样例:
    
    ```
const Chrome = require('chrome-remote-interface');
Chrome.Protocol(function (err, protocol) {
    if (!err) {
        console.log(JSON.stringify(protocol.descriptor, null, 4));
    }
});
    ```
    
3. module.List([options], [callback])

    获取所有的tabs,当然在node中只会有一个  
    
    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - remote 默认false
    
    callback 回调函数,在获取到tabs内容后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - tabs 获取到的tab数组
    
  使用样例:
    
    ```
const Chrome = require('chrome-remote-interface');
Chrome.List(function (err, tabs) {
    if (!err) {
        console.log(tabs);
    }
});
    ```
    
4. module.New([options], [callback])

    创建新的tab
    
    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - url 新tab加载的url 默认about:blank
    
    callback 回调函数,在新tab创建后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - tab 新增的tab
    
  使用样例:  
    
    ```
const Chrome = require('chrome-remote-interface');
Chrome.New(function (err, tab) {
    if (!err) {
        console.log(tab);
    }
});
  1. module.Activate([options], [callback])

    激活指定tab

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222
    • id 目标tab的id

    callback 回调函数,在新tab创建后调用,函数接收如下参数:

    • err Error一个错误对象,如果有错误的话

使用样例:

```

const Chrome = require('chrome-remote-interface');
Chrome.Activate({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
if (!err) {
console.log('success! tab is closing');
}
});
```

  1. module.Close([options], [callback])

    关闭指定tab

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222
    • id 目标tab的id

    callback 回调函数,在新tab创建后调用,函数接收如下参数:

    • err Error一个错误对象,如果有错误的话

使用样例:

```

const Chrome = require('chrome-remote-interface');
Chrome.Close({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
if (!err) {
console.log('success! tab is closing');
}
});
```

  1. module.Version([options], [callback])

    获取版本信息

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222

    callback 回调函数,在新tab创建后调用,函数接收如下参数:

    • err Error一个错误对象,如果有错误的话
    • info json object

使用样例:

```

const Chrome = require('chrome-remote-interface');
Chrome.Version(function (err, info) {
if (!err) {
console.log(info);
}
});
```

  1. Chrome 类

    支持的事件:

    • event

      在远程调试目标发送通知时触发,一般是远程对象执行了客户端提交的方法后

      function (message) {}
      

      message包含如下属性:

      • method 通知内容,方法名 如:'Network.requestWillBeSent'
      • params 参数内容

    使用样例:

     ```
     chrome.on('event', function (message) {
         if (message.method === 'Network.requestWillBeSent') {
             console.log(message.params);
         }   
     });
     ```
    
    • <method>

      调试目标通过websocket发送了一个指定方法名的通知

      function (params) {}
      

      使用样例:

      chrome.on('Network.requestWillBeSent', console.log);
      
    • ready

      每次没有调试命令等待调试目标返回时触发

      function () {}
      

      使用样例:

      只在Network和Page激活后加载一个url

      chrome.Network.enable();
      chrome.Page.enable();
      chrome.once('ready', function () {
          chrome.Page.navigate({'url': 'https://github.com'});
      });
      

支持的方法
- chrome.send(method, [params], [callback])

    发送一个调试命令
    
    method 命令名称  
    params 参数  
    callback 当远程对象对该命令发送一个应答后调用,函数具有以下参数:  
    - error boolean 是否成功
    - response 如果error===true,返回一个error对象,{error:...},否则返回一个应答,{result:...}
    
    注意:在chrome调试规范里提到的id字段,在这里被内部管理不会暴露给用户
    
使用样例:
    
    ```
    chrome.send('Page.navigate', {'url': 'https://github.com'}, console.log);
    ```
    
- chrome.<domain>.<method>([params], [callback])
    
    是chrome.send('<domain>.<method>', params, callback);的一种变形
    
    例如:  
    
    ```
    chrome.Page.navigate({'url': 'https://github.com'}, console.log);
    ```
- chrome.<domain>.<event>(callback)

    是chrome.on('<domain>.<event>', callback)的变形
    
    例如:
    
    ```
    chrome.Network.requestWillBeSent(console.log);
    ```
- chrome.close([callback])

    关闭与远程调试目标的连接  
    callback会在websocket关闭成功后调用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容