vlambda博客
学习文章列表

使用ZeroBrane调试OpenResty Lua脚本

前言

本文介绍使用ZeroBrane调试OpenResty Lua脚本, 单是介绍如何使用ZeroBrane调试OpenResty Lua脚本的文章已有很多, 基本都与ZeroBrane Studio作者写的这篇差不多: http://notebook.kulchenko.com/zerobrane/debugging-openresty-nginx-lua-scripts-with-zerobrane-studio. 本文重点在调试时出现问题时如何处理上, 因为我这不能如文章中说的正常触发断点进行调试:(, 我在Windows 10和Linux(CentOS 7)上都尝试过, 皆不行. 所以花点时间排查问题及尝试处理该问题.

环境依赖

首先是环境, 我这在Windows 10环境上运行相关软件, OpenResty为openresty-1.15.8.3-win32, ZeroBrane为1.90版.

将下载好的OpenResty软件包解压到一个目录中, 这里我解压到D:/openresty-win32中. 然后安装ZeroBrane, 将它安装到目录D:/program/ZeroBrane.

调试配置

编辑D:/openresty-win32/conf/nginx.conf文件, 替换为以下内容:

worker_processes  1;

error_log logs/error.log warn;

events {
worker_connections 1024;
}

http {
lua_package_path 'D:/program/ZeroBrane/lualibs/?/?.lua;D:/program/ZeroBrane/lualibs/?.lua;;';

lua_package_cpath 'D:/program/ZeroBrane/bin/clibs/?.dll;;';

server {
lua_code_cache off;

location / {
default_type text/html;
content_by_lua_file lua/test.lua;
}
}
}

这里设置nginx的日志输出级别为warn, 之后会输出一些warn级别的日志, 如果设置为info, 日志输出太多, 不大好查看. 通过指令lua_code_cache off;设置不缓存lua脚本, 以方便我们修改lua脚本后不必重启openresty(nginx). lua_package_path和lua_package_cpath指令分别指定从ZeroBrane加载的额外的lua脚本和C动态库. content_by_lua_file指令指定我们的测试lua脚本为位于D:/openresty-win32/lua目录中的test.lua文件.

D:/openresty-win32/lua目录中添加test.lua文件, 并添加以下内容到该文件:

local require = require
local debugger = require "mobdebug"
debugger.start('192.168.1.101')
local uri = ngx.var.request_uri
ngx.say("Request URI is ", uri, "!")
ngx.say("done")
debugger.done()

启动openresty服务器, 在目录D:/openresty-win32里执行如下命令:

start nginx

关闭服务器的命令为:

nginx -s stop

打开ZeroBrane IDE, 点击菜单Project > Project Directory > Choose..., 在弹出的目录选择框中, 点进D:/openresty-win32/lua目录, 然后点击选择文件夹按钮导入项目. 点击菜单Project > Start Debugger Server, 启动调试服务器. 在左边Project选项卡中点击test.lua, 打开该文件, 在第四行添加断点.

遭遇问题

Can't start debugging for 'D:\openresty-win32\lua\test.lua'. Compilation error:
Debugger connection closed

具体如下图:


排查及解决问题

通过观察, ZeroBrane主要由lua编写. 通过搜索lua文件, 发现Debugger connection closed 位于D:/program/ZeroBrane/lualibs/mobdebug/mobdebug.lua文件中的debugger_loop函数内, 此函数为被调试脚本向ZeroBrane IDE响应相关请求的函数. 而ZeroBrane向被调试脚本发送请求并获取返回值的地方为handle函数. 可以确定被调试脚本端在响应IDE的请求时出现异常, 即异常出现在被调试脚本启动的mobdebug内.

为了确定具体的问题所在, 通过打印日志不失为一个好的方法. 这里有IDE端的日志输出和被调试端的日志输出(nginx日志输出).

IDE端发送命令请求首先走D:/program/ZeroBrane/src/editor/debugger.lua文件中的debugger.handle函数, 再在此函数体内调用mobdebug.luahandle函数. 该函数中有一个verbose日志输出, 默认是不输出verbose日志的, 我们将它开启, 将verbose变量设置为true, 如下:

使用ZeroBrane调试OpenResty Lua脚本

这里设置后, mobdebug.luahandle函数里的print内容将输出到IDE的Output选项卡, 如果想在handle输出自己的内容, 只要调用print即可. debugger.lua中输出日志可以调用ide:Print或displayError.

修改完后需要重启IDE才能生效, 上图在Output选项卡中输出的内容为生效后的结果.

被调试端打印日志需要打印到nginx的日志输出文件中, 即D:/openresty-win32/logs/error.log文件中. 在mobdebug.lua中添加打印nginx日志的函数, 如下:

local function getngx()
local ngx = require "ngx"
return ngx
end

local function ngxlog(msg)
if pcall(getngx) then
local ngx = getngx()
return ngx.log(ngx.WARN, "\n-------\n", msg, "\n-------\n")
end
end

调用ngxlog函数即可向error.log文件中输出日志, 输出日志级别为WARN. 因为我们设置了lua_code_cache off;指令, 所以修改mobdebug.lua文件后, 不需要重启OpenResty服务器.

我们在mobdebug.luadebug_hook函数里添加日志输出如下:

使用ZeroBrane调试OpenResty Lua脚本

使用ZeroBrane调试OpenResty Lua脚本

通过日志可以确定问题原因为debug_hook函数中出现异常导致提前退出函数, 后面的代码没有机会执行所致.

我们尝试将jit设置为nil, 并且确保ngx不为nil, 如下:

使用ZeroBrane调试OpenResty Lua脚本

再执行, 我们发现可以在IDE中触发断点了:

目前虽然可以触发断点了, 但还不能一步步进行调试, 也不能触发后面的断点. 经我在另一台计算机尝试调试, 发现可以进行一步步调试. 经过分析, 原来是项目导入时目录选择问题, 上面选择的是D:/openresty-win32/lua目录, 而nginx.conf配置的指向的脚本是lua/test.lua, 正确的目录应该是D:/openresty-win32, 才能正确定位lua/test.lua脚本, 如下图:

至此, 调试的问题解决了.

综上, 只需要在mobdebug.lua脚本中改动两个地方, 即设置jit = nil及确保ngx变量不为false或nil, 即可解决本文中提及的问题. 如果遇到其他问题, 不妨使用日志排查一下问题所在, 并尝试自己修复.