Set Breakpoints
APISIX ships an inspect plugin that lets you set breakpoint-style hooks on any line of any Lua file loaded by the worker. When execution reaches a hooked line, the plugin captures the local variables and upvalues at that point and runs a filter function that you define, typically to log values or to fire only when a specific condition is met.
This guide is aimed at users who write their own Lua plugins for APISIX (see Create a Plugin in Lua) and need to confirm what their plugin is seeing in a running worker without redeploying or restarting APISIX. The same mechanism can be used to inspect APISIX's own Lua source if you are debugging the gateway itself.
This guide will show you how to enable the inspect plugin, set up a hooks file, and read the captured values from the error log.
Prerequisite(s)
- A running APISIX instance. Follow the Getting Started tutorial if you do not have one.
- Shell access to the host or container where the APISIX worker is running, with write access to
/usr/local/apisix/.
Enable the Inspect Plugin
To enable the plugin, make sure inspect is listed under plugins in your configuration file:
plugins:
- inspect
# ... other plugins
You can optionally tune the hooks file path and poll interval under plugin_attr:
plugin_attr:
inspect:
delay: 3
hooks_file: "/usr/local/apisix/plugin_inspect_hooks.lua"
delay is the poll interval in seconds at which the plugin re-reads the hooks file. hooks_file is the path the plugin watches; whenever its contents change, the plugin re-installs hooks on the next poll without a restart.
Reload APISIX after changing the configuration file.
Set Up a Hook
Create the hooks file at the path you configured above. The example below hooks a custom plugin at apisix/plugins/my-plugin.lua and logs the value of a local variable named conf whenever line 42 of that file is reached:
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
)
Within the poll interval (three seconds by default), the plugin picks up the new file and installs the hook. You should see a set hooks message in the error log listing the active hook keys.
To verify, send a request that exercises the plugin you hooked:
curl -i "http://127.0.0.1:9080/anything"
Check the error log. The filter runs the first time the hooked line is reached, and because the filter returns false, the hook stays installed and runs again on every subsequent hit. You should see a line similar to the following:
conf at line 42: {"some_key":"some_value"}
To register more than one hook, call dbg.set_hook again in the same file. To remove all hooks, delete the hooks file. APISIX detects the missing file on the next poll cycle and clears the hook table.
The hooks file is executed as Lua code inside the APISIX worker. Anyone with write access to the file can run arbitrary Lua in the gateway process. Restrict filesystem permissions on this path accordingly.
How It Works
Each hook is identified by a file#line key, which is the same key that the plugin uses in its set hooks and remove hook log messages.
When the Lua VM reaches the hooked line, the filter function is called with a single info table that contains:
info.finfo: the result ofdebug.getinfoat that line, including the source file, current line number, and function name.info.vals: a table of the local variables in scope at that line, keyed by name.info.uv: a table of the upvalues captured by the enclosing function, also keyed by name.
The return value of the filter controls the hook lifetime:
- Returning
falsekeeps the hook installed and active on the next hit. - Returning any other value, including
trueornil, removes the hook after this run. - If the filter raises a Lua error, the hook is removed and the error is logged.
Installing a hook disables the LuaJIT compiler for the entire worker process, not only the function containing the hooked line. While any hook is installed:
- The worker continues to serve traffic, but every Lua code path in it falls back to the interpreter and can be substantially slower under load.
- The JIT compiler is re-enabled only after every hook has been removed, either by the filter returning a value other than
falseor by deleting the hooks file.
Avoid leaving hooks installed under heavy traffic, and remove them as soon as you have the information you need.
Hooks are per-worker and held in memory only. Each worker installs hooks based on what it reads from the hooks file, and there is no cross-worker coordination. If you restart APISIX, hooks are cleared until each worker re-reads the file on its next poll cycle.