Skip to content
Open
30 changes: 28 additions & 2 deletions apisix/plugins/opentelemetry.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ local lrucache = core.lrucache.new({

local asterisk = string.byte("*", 1)

local function is_valid_trace_id(trace_id)
if not trace_id or #trace_id ~= 32 then
return false
end
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation function should reject trace IDs that are all zeros (00000000000000000000000000000000) according to the W3C Trace Context specification. Currently, the function only checks length and hex format, but an all-zeros trace_id is invalid and should be rejected to ensure proper fallback to the default generator.

Suggested change
end
end
-- W3C Trace Context: trace-id of all zeros is invalid
if trace_id == "00000000000000000000000000000000" then
return false
end

Copilot uses AI. Check for mistakes.

-- must be lowercase hex
if not trace_id:match("^[0-9a-f]+$") then
return false
end

-- W3C Trace Context: all-zero trace_id is invalid
if trace_id == "00000000000000000000000000000000" then
return false
end

return true
end

local metadata_schema = {
type = "object",
properties = {
Expand Down Expand Up @@ -231,9 +249,17 @@ end

local function create_tracer_obj(conf, plugin_info)
if plugin_info.trace_id_source == "x-request-id" then
local _original_new_ids = id_generator.new_ids

id_generator.new_ids = function()
local trace_id = core.request.headers()["x-request-id"] or ngx_var.request_id
return trace_id, id_generator.new_span_id()
local trace_id = core.request.headers()["x-request-id"]
or ngx_var.request_id

if is_valid_trace_id(trace_id) then
return trace_id, id_generator.new_span_id()
end

return _original_new_ids()
end
end
-- create exporter
Expand Down
101 changes: 101 additions & 0 deletions t/plugin/opentelemetry.t
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,104 @@ HEAD /specific_status
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/.*\/specific_status.*/



=== TEST 20: recreate route for invalid x-request-id test
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"opentelemetry": {
"sampler": {
"name": "always_on"
}
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/opentracing"
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t



=== TEST 21: invalid x-request-id should not crash
--- request
GET /opentracing
--- more_headers
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
--- wait: 2
--- response_body
opentracing
--- no_error_log
[error]
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test verifies that an invalid X-Request-Id (UUID format) doesn't cause a crash, but it doesn't verify that the plugin correctly falls back to generating a valid trace ID. Consider adding a check in a follow-up test to verify that the trace appears in the OpenTelemetry collector logs with a valid trace_id, similar to TEST 16 which checks the log output.

Suggested change
[error]
[error]
=== TEST 22: invalid x-request-id should still generate a trace_id
--- request
GET /opentracing
--- more_headers
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
--- wait: 2
--- grep_error_log eval
qr/trace_id/
--- grep_error_log_out
trace_id

Copilot uses AI. Check for mistakes.



=== TEST 22: invalid x-request-id should still generate a valid trace
--- request
GET /opentracing
--- more_headers
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
--- wait: 2
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/"traceId":"(?!0{32})[0-9a-f]{32}"/



=== TEST 23: all-zero x-request-id should not be used as trace id
--- request
GET /opentracing
--- more_headers
X-Request-Id: 00000000000000000000000000000000
--- wait: 2
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/"traceId":"(?!0{32})[0-9a-f]{32}"/



=== TEST 24: uppercase x-request-id should still generate a valid trace id
--- request
GET /opentracing
--- more_headers
X-Request-Id: 550E8400-E29B-41D4-A716-446655440000
--- wait: 2
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/"traceId":"(?!0{32})[0-9a-f]{32}"/



=== TEST 25: malformed length x-request-id should still generate a valid trace id
--- request
GET /opentracing
--- more_headers
X-Request-Id: 550e8400e29b41d4a7164466
--- wait: 2
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/"traceId":"(?!0{32})[0-9a-f]{32}"/
Loading