Skip to content

Commit 087aa81

Browse files
feat: add plugin execution tracing to OpenTelemetry plugin
This commit adds plugin execution tracing capability to the OpenTelemetry plugin, allowing users to trace individual plugin phases (rewrite, access, header_filter, body_filter, log) as child spans of the main request trace. Changes: - Added trace_plugins configuration option (default: false, opt-in) - Added plugin_span_kind configuration for observability provider compatibility - Enhanced plugin execution with OpenTelemetry span creation and finishing - Added comprehensive request context attributes to plugin spans - Updated documentation with examples and usage instructions - Added comprehensive test suite for the new functionality Features: - Plugin Phase Tracing: Creates child spans for each plugin phase execution - Rich Context: Includes HTTP method, URI, hostname, user agent, route info, and service info - Configurable: Can be enabled/disabled via trace_plugins configuration - Span Kind Control: Supports internal (default) and server span kinds for observability provider compatibility - Proper Hierarchy: Plugin spans are correctly nested under main request spans - Performance: Minimal overhead when disabled (default behavior) Configuration: - trace_plugins: boolean (default: false) - Enable/disable plugin tracing - plugin_span_kind: string (default: 'internal') - Span kind for plugin spans - 'internal': Standard internal operation (may be excluded from metrics) - 'server': Server-side operation (typically included in service-level metrics) This addresses GitHub issue #12510 and provides end-to-end tracing visibility for APISIX plugin execution phases.
1 parent 2c041a3 commit 087aa81

File tree

5 files changed

+797
-4
lines changed

5 files changed

+797
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ A/B testing, canary release, blue-green deployment, limit rate, defense against
123123
- **OPS friendly**
124124

125125
- Zipkin tracing: [Zipkin](docs/en/latest/plugins/zipkin.md)
126+
- OpenTelemetry tracing: [OpenTelemetry](docs/en/latest/plugins/opentelemetry.md) with plugin execution tracing
126127
- Open source APM: support [Apache SkyWalking](docs/en/latest/plugins/skywalking.md)
127128
- Works with external service discovery: In addition to the built-in etcd, it also supports [Consul](docs/en/latest/discovery/consul.md), [Consul_kv](docs/en/latest/discovery/consul_kv.md), [Nacos](docs/en/latest/discovery/nacos.md), [Eureka](docs/en/latest/discovery/eureka.md) and [Zookeeper (CP)](https://github.com/api7/apisix-seed/blob/main/docs/en/latest/zookeeper.md).
128129
- Monitoring And Metrics: [Prometheus](docs/en/latest/plugins/prometheus.md)

apisix/plugin.lua

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ local tostring = tostring
3838
local error = error
3939
local getmetatable = getmetatable
4040
local setmetatable = setmetatable
41+
local string_format = string.format
4142
-- make linter happy to avoid error: getting the Lua global "load"
4243
-- luacheck: globals load, ignore lua_load
4344
local lua_load = load
@@ -1169,6 +1170,9 @@ function _M.run_plugin(phase, plugins, api_ctx)
11691170
return api_ctx
11701171
end
11711172

1173+
-- Get OpenTelemetry plugin for tracing
1174+
local otel_plugin = _M.get("opentelemetry")
1175+
11721176
if phase ~= "log"
11731177
and phase ~= "header_filter"
11741178
and phase ~= "body_filter"
@@ -1188,11 +1192,26 @@ function _M.run_plugin(phase, plugins, api_ctx)
11881192
goto CONTINUE
11891193
end
11901194

1195+
-- Start OpenTelemetry plugin span
1196+
if otel_plugin and otel_plugin.start_plugin_span then
1197+
otel_plugin.start_plugin_span(api_ctx, plugins[i]["name"], phase)
1198+
end
1199+
11911200
run_meta_pre_function(conf, api_ctx, plugins[i]["name"])
11921201
plugin_run = true
11931202
api_ctx._plugin_name = plugins[i]["name"]
11941203
local code, body = phase_func(conf, api_ctx)
11951204
api_ctx._plugin_name = nil
1205+
1206+
-- Finish OpenTelemetry plugin span
1207+
if otel_plugin and otel_plugin.finish_plugin_span then
1208+
local error_msg = nil
1209+
if code and code >= 400 then
1210+
error_msg = "plugin returned error code: " .. tostring(code)
1211+
end
1212+
otel_plugin.finish_plugin_span(api_ctx, plugins[i]["name"], phase, error_msg)
1213+
end
1214+
11961215
if code or body then
11971216
if is_http then
11981217
if code >= 400 then
@@ -1216,7 +1235,6 @@ function _M.run_plugin(phase, plugins, api_ctx)
12161235
end
12171236
end
12181237
end
1219-
12201238
::CONTINUE::
12211239
end
12221240
return api_ctx, plugin_run
@@ -1226,11 +1244,26 @@ function _M.run_plugin(phase, plugins, api_ctx)
12261244
local phase_func = plugins[i][phase]
12271245
local conf = plugins[i + 1]
12281246
if phase_func and meta_filter(api_ctx, plugins[i]["name"], conf) then
1247+
-- Start OpenTelemetry plugin span
1248+
if otel_plugin and otel_plugin.start_plugin_span then
1249+
otel_plugin.start_plugin_span(api_ctx, plugins[i]["name"], phase)
1250+
end
1251+
12291252
plugin_run = true
12301253
run_meta_pre_function(conf, api_ctx, plugins[i]["name"])
12311254
api_ctx._plugin_name = plugins[i]["name"]
1232-
phase_func(conf, api_ctx)
1255+
1256+
local code, body = phase_func(conf, api_ctx)
12331257
api_ctx._plugin_name = nil
1258+
1259+
-- Finish OpenTelemetry plugin span
1260+
if otel_plugin and otel_plugin.finish_plugin_span then
1261+
local error_msg = nil
1262+
if code and code >= 400 then
1263+
error_msg = "plugin returned error code: " .. tostring(code)
1264+
end
1265+
otel_plugin.finish_plugin_span(api_ctx, plugins[i]["name"], phase, error_msg)
1266+
end
12341267
end
12351268
end
12361269

0 commit comments

Comments
 (0)