设置断点
APISIX 内置的 inspect 插件可以在工作进程加载的任意 Lua 文件中,为指定代码行设置类似断点的钩子。执行到该行时,插件会捕获当前作用域内的局部变量和函数闭包变量,并 运行你定义的过滤函数。过滤函数通常用于记录变量值,或仅在满足特定条件时触发。
本指南适用于为 APISIX 编写自定义 Lua 插件的用户(参见使用 Lua 创建插件)。你可以借助该机制确认插件在运行中的工作进程里实际读取到了哪些值,而无需重新部署或重启 APISIX。调试网关本身时,也可以使用相同机制检查 APISIX 的 Lua 源代码。
本指南将介绍如何启用 inspect 插件、配置钩子文件,并从错误日志中查看捕获的值。
先决条件
- 一个正在运行的 APISIX 实例。如果尚未部署,请按照快速入门教程完成安装。
- 具备可进入 APISIX 工作进程所在主机或容器的 Shell,并能访问
/usr/local/apisix/。
启用 inspect 插件
要启用该插件,请确保配置文件的 plugins 列表中包含 inspect:
plugins:
- inspect
# ... other plugins
你还可以在 plugin_attr 中调整钩子文件路径和轮询间隔:
plugin_attr:
inspect:
delay: 3
hooks_file: "/usr/local/apisix/plugin_inspect_hooks.lua"
delay 是插件重新读取钩子文件的轮询间隔,单位为秒。hooks_file 是插件监控的文件路径;文件内容发生更改后,插件会在下一次轮询时重新安装钩子,无需重启 APISIX。
修改配置文件后,请重新加载 APISIX。
设置钩子
在上述配置的路径创建钩子文件。下面的示例为自定义插件 apisix/plugins/my-plugin.lua 的第 42 行设置钩子,并在执行到该行时记录名为 conf 的局部变量:
local dbg = require("apisix.inspect.dbg")
dbg.set_hook(
"/usr/local/apisix/apisix/plugins/my-plugin.lua",
42,
nil,
function(info)
ngx.log(ngx.WARN, "conf at line 42: ",
require("cjson").encode(info.vals.conf))
return false
end
)
插件会在一个轮询周期内(默认为 3 秒)读取新文件并安装钩子。错误日志中会出现 set hooks 消息,其中列出当前生效的钩子键。
要验证配置,请发送一个能够触发该插件的请求:
curl -i "http://127.0.0.1:9080/anything"
检查错误日志。过滤函数会在首次执行到目标行时运行;由于该函数返回 false,钩子会继续保持生效,并在之后每次执行到该行时再次运行。你应该看到类似以下内容:
conf at line 42: {"some_key":"some_value"}
要注册多个钩子,请在同一文件中继续调用 dbg.set_hook。要删除所有钩子,请删除钩子文件。APISIX 会在下一次轮询时检测到文件已不存在,并清空钩子表。
钩子文件会作为 Lua 代码在 APISIX 工作进程中执行。任何对该文件拥有写权限的人都可以在网关进程中执行任意 Lua 代码,因此必须严格限制该路径的文件系统权限。
工作原理
每个钩子都由 file#line 键唯一标识;插件在 set hooks 和 remove hook 日志消息中也使用相同的键。
Lua VM 执行到目标行时,会调用过滤函数,并传入一个 info 表,其中包含:
info.finfo:在目标行调用debug.getinfo的结果,包括源文件、当前行号和函数名。info.vals:目标行作用域内的局部变量表,以变量名为键。info.uv:外层函数捕获的闭包变量表,同样以变量名为键。
过滤函数的返回值决定钩子的生命周期:
- 返回
false:保留钩子,使其在下一次执行到目标行时继续生效。 - 返回其他任意值(包括
true或nil):本次运行结束后移除钩子。 - 如果过滤函数抛出 Lua 错误:移除钩子并记录错误。
安装钩子会禁用整个工作进程的 LuaJIT 编译器,而不仅是包含目标行的函数。只要还有任意钩子处于安装状态:
- 工作进程仍会继续处理流量,但所有 Lua 代码路径都会回退到解释执行,在有负载时可能明显变慢。
- 只有移除所有钩子后,JIT 编译器才会重新启用。移除方式包括让过滤函数返回
false以外的值,或删除钩子文件。
请避免在高流量场景下长时间保留钩子,并在获取所需信息后立即将其移除。
钩子按工作进程分别安装,并且仅保存在内存中。每个工作进程根据钩子文件的内容独立安装钩子,不会在工作进程之间进行协调。重启 APISIX 后,钩子会被清除;各工作进程在下一个轮询周期重新读取文件后才会再次安装钩子。
下一步
你现在已经了解如何使用 inspect 插件从运行中的 APISIX 工作进程捕获运行时值。
- 如需编写待检查的自定义 Lua 插件,请参阅使用 Lua 创建插件。
- 如需在请求级别进行调试,而不是检查具体 Lua 代码行,请参阅使用调试模式。