My demo, Evolving your APIs, features a custom Apache APISIX plugin. I believe that the process of creating a custom plugin is relatively well-documented. However, I wanted to check the parameters of the _M.access(conf, ctx) function, especially the ctx one.

The documentation states:

The ctx parameter caches data information related to the request. You can use core.log.warn(core.json.encode(ctx, true)) to output it to error.log for viewing.

Unfortunately, core.log ultimately depends on nginx's logging, and its buffer is limited in size. Thanks to my colleague Abhishek for finding the info. For this reason, the ctx display is (heavily) truncated. I had to log data bit by bit; however, it was instructive.

The context

The ctx parameter is a Lua table. In Lua, table data structures are used for regular indexed access (akin to arrays) and key access (like hash maps). A single ctx instance is used for each request.

The Apache APISIX engine reads and writes data in the ctx table. It's responsible for forwarding the latter from plugin to plugin. In turn, each plugin can also read and write data.

I resorted to a custom plugin to conditionally apply rate-limiting in the demo. The custom plugin is a copy-paste of the limit-count plugin. Note that the analysis is done in a specific context. Refrain from assuming the same data is available in your own. However, it should be a good starting point.

Overview of the ctx parameter

The data available in the ctx parameter is overwhelming. To better understand it, we shall go from the more general to the more particular. Let's start from the overview.

Matched route

The matched_route row is a complex data tree that deserves a detailed description.

curl http://apisix:9180/apisix/admin/routes/2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "name": "Versioned Route to Old API",
  "methods": ["GET"],
  "uris": ["/v1/hello", "/v1/hello/", "/v1/hello/*"],
  "upstream_id": 1,
  "plugin_config_id": 1
}'

Plugins

The plugins value contains plugin-related data in an indexed-based Lua table. Each plugin has two entries: the first (even-indexed) entry contains data related to the plugin in general, e.g., its schema; while the second (odd-index) entry data is related to its configuration in the current route.

My setup has two plugins, hence four entries, but to keep things simpler, I kept only a single plugin in the following diagram:

Key values match directly to the plugin schema and configuration; you can check the whole descriptions directly in the plugin.

A final trick

I initially had issues printing the ctx table because of the nginx buffer limit and had to do it bit by bit. However, you can print it to a file.

Here's the function, courtesy of my colleague Zeping Bai:

local file, err = io.open("conf/ctx.json", "w+")
if not file then
    ngx.log(ngx.ERR, "failed to open file: ", err)
    return
end
file.write(core.json.encode(ctx, true) .. "\n")
file.close()

Here's the whole data representation, in case you have good eyes:

Alternatively, here's the PlantUML representation.

Conclusion

In this post, I described the structure of the ctx parameter in the access() function. While specific entries vary from configuration to configuration, it gives a good entry point into data manipulated by plugins.

It's also a good reminder that even if you're not fluent in a language or a codebase, you can get quite a lot of information by logging some variables.

To go further:


Originally published at A Java Geek on September 24th, 2023