From b5197a1d5a7a4aaaa303bbd0e2cf1c0f01bda8bf Mon Sep 17 00:00:00 2001 From: Nic Date: Sun, 19 Oct 2025 18:24:20 +0800 Subject: [PATCH 01/52] feat: add more spans to opentelemetry plugin Signed-off-by: Nic --- apisix/core/response.lua | 6 +++ apisix/init.lua | 16 ++++++ apisix/plugins/opentelemetry.lua | 44 +++++++++++++++++ apisix/secret.lua | 5 ++ apisix/ssl/router/radixtree_sni.lua | 9 ++++ apisix/utils/span.lua | 73 ++++++++++++++++++++++++++++ apisix/utils/stack.lua | 75 +++++++++++++++++++++++++++++ apisix/utils/tracer.lua | 64 ++++++++++++++++++++++++ 8 files changed, 292 insertions(+) create mode 100644 apisix/utils/span.lua create mode 100644 apisix/utils/stack.lua create mode 100644 apisix/utils/tracer.lua diff --git a/apisix/core/response.lua b/apisix/core/response.lua index baee97749598..27be28c572bc 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -19,6 +19,7 @@ -- -- @module core.response +local tracer = require("apisix.utils.tracer") local encode_json = require("cjson.safe").encode local ngx = ngx local arg = ngx.arg @@ -62,6 +63,7 @@ function resp_exit(code, ...) ngx.status = code end + local message for i = 1, select('#', ...) do local v = select(i, ...) if type(v) == "table" then @@ -73,6 +75,7 @@ function resp_exit(code, ...) t[idx] = body idx = idx + 1 t[idx] = "\n" + message = body end elseif v ~= nil then @@ -86,6 +89,9 @@ function resp_exit(code, ...) end if code then + if code >= 400 then + tracer.finish_current_span(tracer.status.ERROR, message or ("response code " .. code)) + end return ngx_exit(code) end end diff --git a/apisix/init.lua b/apisix/init.lua index 430572e27e48..c242ea6a436f 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -47,6 +47,7 @@ local debug = require("apisix.debug") local pubsub_kafka = require("apisix.pubsub.kafka") local resource = require("apisix.resource") local trusted_addresses_util = require("apisix.utils.trusted-addresses") +local tracer = require("apisix.utils.tracer") local ngx = ngx local get_method = ngx.req.get_method local ngx_exit = ngx.exit @@ -203,6 +204,8 @@ function _M.ssl_client_hello_phase() local api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx + local span = tracer.new_span("ssl_client_hello_phase", tracer.kind.server) + local ok, err = router.router_ssl.match_and_set(api_ctx, true, sni) ngx_ctx.matched_ssl = api_ctx.matched_ssl @@ -215,18 +218,23 @@ function _M.ssl_client_hello_phase() core.log.error("failed to fetch ssl config: ", err) end core.log.error("failed to match any SSL certificate by SNI: ", sni) + span:set_status(tracer.status.ERROR, "failed match SNI") + tracer.finish_current_span() ngx_exit(-1) end ok, err = apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols) if not ok then core.log.error("failed to set ssl protocols: ", err) + span:set_status(tracer.status.ERROR, "failed set protocols") + tracer.finish_current_span() ngx_exit(-1) end -- in stream subsystem, ngx.ssl.server_name() return hostname of ssl session in preread phase, -- so that we can't get real SNI without recording it in ngx.ctx during client_hello phase ngx.ctx.client_hello_sni = sni + tracer.finish_current_span() end @@ -666,6 +674,7 @@ end function _M.http_access_phase() + tracer.new_span("http_access_phase", tracer.kind.server) -- from HTTP/3 to HTTP/1.1 we need to convert :authority pesudo-header -- to Host header, so we set upstream_host variable here. if ngx.req.http_version() == 3 then @@ -716,19 +725,26 @@ function _M.http_access_phase() handle_x_forwarded_headers(api_ctx) + local router_match_span = tracer.new_span("http_router_match", tracer.kind.internal) router.router_http.match(api_ctx) local route = api_ctx.matched_route if not route then + tracer.new_span("run_global_rules", tracer.kind.internal) -- run global rule when there is no matching route local global_rules = apisix_global_rules.global_rules() plugin.run_global_rules(api_ctx, global_rules, nil) + tracer.finish_current_span() core.log.info("not find any matched route") + router_match_span:set_status(tracer.status.ERROR, "no matched route") + tracer.finish_current_span() return core.response.exit(404, {error_msg = "404 Route Not Found"}) end + tracer.finish_current_span() + core.log.info("matched route: ", core.json.delay_encode(api_ctx.matched_route, true)) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index d98ac44ae69d..ed10aed8b9a6 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -36,6 +36,7 @@ local span_status = require("opentelemetry.trace.span_status") local resource_new = require("opentelemetry.resource").new local attr = require("opentelemetry.attribute") +local new_context = require("opentelemetry.context").new local context = require("opentelemetry.context").new() local trace_context_propagator = require("opentelemetry.trace.propagation.text_map.trace_context_propagator").new() @@ -376,6 +377,10 @@ function _M.rewrite(conf, api_ctx) ngx_var.opentelemetry_span_id = span_context.span_id end + if not ctx:span():is_recording() then + ngx.ctx._apisix_skip_tracing = true + end + api_ctx.otel_context_token = ctx:attach() -- inject trace context into the headers of upstream HTTP request @@ -383,6 +388,41 @@ function _M.rewrite(conf, api_ctx) end +local function create_child_span(tracer, parent_span_ctx, span) + local new_span_ctx, new_span = tracer:start(parent_span_ctx, span.name, + { + kind = span.kind, + attributes = span.attributes, + }) + new_span.start_time = span.start_time + + for _, child in ipairs(span.children or {}) do + create_child_span(tracer, new_span_ctx, child) + end + + new_span:set_status(span.status, span.status) + new_span:finish(span.end_time) +end + + +local function inject_core_spans(root_span_ctx, api_ctx, conf) + local metadata = plugin.plugin_metadata(plugin_name) + local plugin_info = metadata.value + if not root_span_ctx:span():is_recording() then + return + end + local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil, + create_tracer_obj, conf, plugin_info) + if not tracer then + core.log.error("failed to fetch tracer object: ", err) + return + end + for _, sp in ipairs(ngx.ctx._apisix_spans or {}) do + create_child_span(tracer, root_span_ctx, sp) + end +end + + function _M.delayed_body_filter(conf, api_ctx) if api_ctx.otel_context_token and ngx.arg[2] then local ctx = context:current() @@ -399,6 +439,8 @@ function _M.delayed_body_filter(conf, api_ctx) span:set_attributes(attr.int("http.status_code", upstream_status)) + inject_core_spans(ctx, api_ctx, conf) + span:finish() end end @@ -418,6 +460,8 @@ function _M.log(conf, api_ctx) "upstream response status: " .. upstream_status) end + inject_core_spans(span, api_ctx, conf) + span:finish() end end diff --git a/apisix/secret.lua b/apisix/secret.lua index b8d7b19a522c..1db6e7246d82 100644 --- a/apisix/secret.lua +++ b/apisix/secret.lua @@ -18,6 +18,7 @@ local require = require local core = require("apisix.core") local string = require("apisix.core.string") +local tracer = require("apisix.utils.tracer") local local_conf = require("apisix.core.config_local").local_conf() @@ -148,6 +149,7 @@ local function fetch_by_uri(secret_uri) return nil, "no secret conf, secret_uri: " .. secret_uri end + local span = tracer.new_span("fetch_secret", tracer.kind.client) local ok, sm = pcall(require, "apisix.secret." .. opts.manager) if not ok then return nil, "no secret manager: " .. opts.manager @@ -155,9 +157,12 @@ local function fetch_by_uri(secret_uri) local value, err = sm.get(conf, opts.key) if err then + span:set_status(tracer.status.ERROR, err) + tracer.finish_current_span() return nil, err end + tracer.finish_current_span() return value end diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index ae7e5b265bf9..027cbb19a303 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -21,6 +21,7 @@ local apisix_ssl = require("apisix.ssl") local secret = require("apisix.secret") local ngx_ssl = require("ngx.ssl") local config_util = require("apisix.core.config_util") +local tracer = require("apisix.utils.tracer") local ngx = ngx local ipairs = ipairs local type = type @@ -149,11 +150,15 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) local err if not radixtree_router or radixtree_router_ver ~= ssl_certificates.conf_version then + local span = tracer.new_span("create_router", tracer.kind.internal) radixtree_router, err = create_router(ssl_certificates.values) if not radixtree_router then + span:set_status(tracer.status.ERROR, "failed create router") + tracer.finish_current_span() return false, "failed to create radixtree router: " .. err end radixtree_router_ver = ssl_certificates.conf_version + tracer.finish_current_span() end local sni = alt_sni @@ -170,6 +175,7 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) core.log.debug("sni: ", sni) local sni_rev = sni:reverse() + local span = tracer.new_span("sni_radixtree_match", tracer.kind.internal) local ok = radixtree_router:dispatch(sni_rev, nil, api_ctx) if not ok then if not alt_sni then @@ -177,8 +183,11 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) -- with it sometimes core.log.error("failed to find any SSL certificate by SNI: ", sni) end + span:set_status(tracer.status.ERROR, "failed match SNI") + tracer.finish_current_span() return false end + tracer.finish_current_span() if type(api_ctx.matched_sni) == "table" then diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua new file mode 100644 index 000000000000..c1eacd23729f --- /dev/null +++ b/apisix/utils/span.lua @@ -0,0 +1,73 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local util = require("opentelemetry.util") +local span_status = require("opentelemetry.trace.span_status") + + +local _M = {} + + +local mt = { + __index = _M +} + + +function _M.new(name, kind) + local self = { + name = name, + start_time = util.time_nano(), + end_time = 0, + kind = kind, + attributes = {}, + children = {}, + } + return setmetatable(self, mt) +end + + +function _M.append_child(self, span) + table.insert(self.children, span) +end + + +function _M.set_status(self, code, message) + code = span_status.validate(code) + local status = { + code = code, + message = "" + } + if code == span_status.ERROR then + status.message = message + end + + self.status = status +end + + +function _M.set_attributes(self, ...) + for _, attr in ipairs({ ... }) do + table.insert(self.attributes, attr) + end +end + + +function _M.finish(self) + self.end_time = util.time_nano() +end + + +return _M diff --git a/apisix/utils/stack.lua b/apisix/utils/stack.lua new file mode 100644 index 000000000000..a88e4832a185 --- /dev/null +++ b/apisix/utils/stack.lua @@ -0,0 +1,75 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local _M = {} +local mt = { __index = _M } + +function _M.new() + local self = { + _data = {}, + _n = 0, + } + return setmetatable(self, mt) +end + + +function _M.push(self, value) + self._n = self._n + 1 + self._data[self._n] = value +end + + +function _M.pop(self) + if self._n == 0 then + return nil + end + + local value = self._data[self._n] + self._data[self._n] = nil + self._n = self._n - 1 + return value +end + + +function _M.peek(self) + if self._n == 0 then + return nil + end + + return self._data[self._n] +end + + +function _M.is_empty(self) + return self._n == 0 +end + + +function _M.size(self) + return self._n +end + + +function _M.clear(self) + for i = 1, self._n do + self._data[i] = nil + end + self._n = 0 +end + + +return _M + diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua new file mode 100644 index 000000000000..d078562e2474 --- /dev/null +++ b/apisix/utils/tracer.lua @@ -0,0 +1,64 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local ngx = ngx +local stack = require("apisix.utils.stack") +local span = require("apisix.utils.span") +local span_kind = require("opentelemetry.trace.span_kind") +local span_status = require("opentelemetry.trace.span_status") + + +local _M = { + kind = span_kind, + status = span_status, +} + + +function _M.new_span(name, kind) + local ctx = ngx.ctx + if not ctx._apisix_spans then + ctx._apisix_spans = {} + end + if not ctx._apisix_span_stack then + ctx._apisix_span_stack = stack.new() + end + local sp = span.new(name, kind) + if ctx._apisix_skip_tracing then + return sp + end + if ctx._apisix_span_stack:is_empty() then + table.insert(ctx._apisix_spans, sp) + else + local parent_span = ctx._apisix_span_stack:peek() + parent_span:append_child(sp) + end + ctx._apisix_span_stack:push(sp) +end + + +function _M.finish_current_span(code, message) + if not ngx.ctx._apisix_span_stack then + return + end + local sp = ngx.ctx._apisix_span_stack:pop() + if code then + sp:set_status(code, message) + end + sp:finish() +end + + +return _M From f6414fd1337f769f2e3cf3af5e4b5ed359dfe477 Mon Sep 17 00:00:00 2001 From: Nic Date: Sun, 19 Oct 2025 18:33:10 +0800 Subject: [PATCH 02/52] add todo Signed-off-by: Nic --- apisix/plugins/opentelemetry.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index ed10aed8b9a6..15713d1c2e12 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -411,6 +411,8 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) if not root_span_ctx:span():is_recording() then return end + -- TODO: we should create another tracer object with always_on sampler in here, + -- because the root span already decided to sample, all child spans should be sampled too. local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil, create_tracer_obj, conf, plugin_info) if not tracer then From 0317cf51998758a641db74ba7582f942f2eb466b Mon Sep 17 00:00:00 2001 From: Nic Date: Sun, 19 Oct 2025 18:34:59 +0800 Subject: [PATCH 03/52] f Signed-off-by: Nic --- apisix/plugins/opentelemetry.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 15713d1c2e12..b522bacb5d15 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -36,7 +36,6 @@ local span_status = require("opentelemetry.trace.span_status") local resource_new = require("opentelemetry.resource").new local attr = require("opentelemetry.attribute") -local new_context = require("opentelemetry.context").new local context = require("opentelemetry.context").new() local trace_context_propagator = require("opentelemetry.trace.propagation.text_map.trace_context_propagator").new() From 7944e9f63b3f7f92858c79f7c658a7ce524755e7 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 12:57:38 +0530 Subject: [PATCH 04/52] return span on newspan --- apisix/utils/tracer.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua index d078562e2474..89b6c56e38b3 100644 --- a/apisix/utils/tracer.lua +++ b/apisix/utils/tracer.lua @@ -46,6 +46,7 @@ function _M.new_span(name, kind) parent_span:append_child(sp) end ctx._apisix_span_stack:push(sp) + return sp end From ec7adefed63796f129785950aefde73abf133744 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 13:16:34 +0530 Subject: [PATCH 05/52] fix lint --- apisix/utils/span.lua | 4 +++- apisix/utils/stack.lua | 1 + apisix/utils/tracer.lua | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index c1eacd23729f..45f10220cce6 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -16,7 +16,9 @@ -- local util = require("opentelemetry.util") local span_status = require("opentelemetry.trace.span_status") - +local setmetatable = setmetatable +local table = table +local ipairs = ipairs local _M = {} diff --git a/apisix/utils/stack.lua b/apisix/utils/stack.lua index a88e4832a185..030065ed7988 100644 --- a/apisix/utils/stack.lua +++ b/apisix/utils/stack.lua @@ -16,6 +16,7 @@ -- local _M = {} local mt = { __index = _M } +local setmetatable = setmetatable function _M.new() local self = { diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua index 89b6c56e38b3..a316ed036783 100644 --- a/apisix/utils/tracer.lua +++ b/apisix/utils/tracer.lua @@ -19,7 +19,7 @@ local stack = require("apisix.utils.stack") local span = require("apisix.utils.span") local span_kind = require("opentelemetry.trace.span_kind") local span_status = require("opentelemetry.trace.span_status") - +local table = table local _M = { kind = span_kind, From bb286399351db16a23dc64175273160fb0a5327a Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 14:39:04 +0530 Subject: [PATCH 06/52] fix CI --- apisix/plugins/opentelemetry.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index b522bacb5d15..cbf9c80772ee 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -398,8 +398,9 @@ local function create_child_span(tracer, parent_span_ctx, span) for _, child in ipairs(span.children or {}) do create_child_span(tracer, new_span_ctx, child) end - - new_span:set_status(span.status, span.status) + if span.status then + new_span:set_status(span.status.code, span.status.message) + end new_span:finish(span.end_time) end @@ -407,7 +408,7 @@ end local function inject_core_spans(root_span_ctx, api_ctx, conf) local metadata = plugin.plugin_metadata(plugin_name) local plugin_info = metadata.value - if not root_span_ctx:span():is_recording() then + if root_span_ctx.span and not root_span_ctx:span():is_recording() then return end -- TODO: we should create another tracer object with always_on sampler in here, @@ -419,7 +420,9 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) return end for _, sp in ipairs(ngx.ctx._apisix_spans or {}) do - create_child_span(tracer, root_span_ctx, sp) + if root_span_ctx.span_context then + create_child_span(tracer, root_span_ctx, sp) + end end end From 1b29310b57612ea7884f7523df0b04262db9671c Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 16:27:41 +0530 Subject: [PATCH 07/52] fix opentelemetry3 --- t/plugin/opentelemetry3.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/plugin/opentelemetry3.t b/t/plugin/opentelemetry3.t index ff7ea14e56e5..9a18de31e1c5 100644 --- a/t/plugin/opentelemetry3.t +++ b/t/plugin/opentelemetry3.t @@ -161,6 +161,8 @@ hello world qr/opentelemetry export span/ --- grep_error_log_out opentelemetry export span +opentelemetry export span +opentelemetry export span --- error_log eval qr/request log: \{.*"opentelemetry_context_traceparent":"00-\w{32}-\w{16}-01".*\}/ From 57cac44d33b9ada06d2cc2f2791b169cad44d268 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 18:19:46 +0530 Subject: [PATCH 08/52] add test --- t/plugin/opentelemetry.t | 270 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index daf91e39e8bd..5cb2e0a4996f 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -14,7 +14,22 @@ # See the License for the specific language governing permissions and # limitations under the License. # +BEGIN { + sub set_env_from_file { + my ($env_name, $file_path) = @_; + open my $fh, '<', $file_path or die $!; + my $content = do { local $/; <$fh> }; + close $fh; + + $ENV{$env_name} = $content; + } + # set env + set_env_from_file('TEST_CERT', 't/certs/apisix.crt'); + set_env_from_file('TEST_KEY', 't/certs/apisix.key'); + set_env_from_file('TEST2_CERT', 't/certs/test2.crt'); + set_env_from_file('TEST2_KEY', 't/certs/test2.key'); +} use t::APISIX 'no_plan'; add_block_preprocessor(sub { my ($block) = @_; @@ -434,3 +449,258 @@ HEAD /specific_status tail -n 1 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*\/specific_status.*/ + + + +=== TEST 20: test create_router span when SSL router is created +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- First, let's trigger SSL router creation by adding an SSL certificate + local code, body = t('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + [[{ + "cert": "$env://TEST_CERT", + "key": "$env://TEST_KEY", + "snis": ["test.com"] + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.say("SSL certificate added") + } + } +--- request +GET /t +--- response_body +SSL certificate added +--- wait: 1 + + + +=== TEST 21: verify create_router span in logs after SSL setup +--- exec +grep -c '"name":"create_router"' ci/pod/otelcol-contrib/data-otlp.json || echo "0" +--- response_body eval +qr/[1-9]\d*/ + + + +=== TEST 22: test sni_radixtree_match span with SSL request +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- Create a route that uses the SSL certificate + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + } + } + }, + "uri": "/hello", + "hosts": ["test.com"], + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.say("Route created") + } + } +--- request +GET /t +--- response_body +Route created + + + +=== TEST 23: trigger SSL match with SNI +--- exec +curl -k -H "Host: test.com" https://127.0.0.1:1994/hello --resolve "test.com:1994:127.0.0.1" || echo "request_completed" +--- wait: 2 + + + +=== TEST 24: verify sni_radixtree_match span in logs +--- exec +grep -c '"name":"sni_radixtree_match"' ci/pod/otelcol-contrib/data-otlp.json || echo "0" +--- response_body eval +qr/[1-9]\d*/ + + + +=== TEST 25: test multiple SSL certificates trigger multiple create_router spans +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- Add another SSL certificate to trigger router recreation + local code, body = t('/apisix/admin/ssls/2', + ngx.HTTP_PUT, + [[{ + "cert": "$env://TEST_CERT", + "key": "$env://TEST_KEY", + "snis": ["test2.com"] + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.say("Second SSL certificate added") + } + } +--- request +GET /t +--- response_body +Second SSL certificate added +--- wait: 1 + + + +=== TEST 26: verify create_router span count increased after adding second SSL +--- exec +grep -o '"name":"create_router"' ci/pod/otelcol-contrib/data-otlp.json | wc -l +--- response_body eval +qr/[2-9]\d*/ + + + +=== TEST 27: test SSL router error span status +--- config + location /t { + content_by_lua_block { + -- This test verifies that when SSL router creation fails, + -- the span status is set to ERROR + -- We'll simulate this by causing a router creation failure + + local ssl = require("apisix.ssl") + local orig_func = ssl.get_latest_certificates + + -- Temporarily replace the function to simulate failure + ssl.get_latest_certificates = function() + return nil, "simulated error" + end + + local radixtree_sni = require("apisix.ssl.router.radixtree_sni") + local api_ctx = {} + + -- This should trigger an error path in match_and_set + local ok, err = radixtree_sni.match_and_set(api_ctx, false, "test.com") + + -- Restore original function + ssl.get_latest_certificates = orig_func + + if not ok then + ngx.say("Error simulated successfully: ", err) + else + ngx.say("Unexpected success") + end + } + } +--- request +GET /t +--- response_body_like +Error simulated successfully:.* + + + +=== TEST 28: verify error status in create_router span +--- exec +tail -n 5 ci/pod/otelcol-contrib/data-otlp.json | grep -A 10 -B 10 '"name":"create_router"' | grep -c '"status":"STATUS_ERROR"' || echo "0" +--- response_body eval +qr/[0-9]+/ + + + +=== TEST 29: test SSL match failure span status +--- config + location /t { + content_by_lua_block { + local radixtree_sni = require("apisix.ssl.router.radixtree_sni") + local api_ctx = {} + + -- Try to match a non-existent SNI + local ok, err = radixtree_sni.match_and_set(api_ctx, false, "nonexistent.com") + + if not ok then + ngx.say("SNI match failed as expected: ", err) + else + ngx.say("Unexpected match success") + end + } + } +--- request +GET /t +--- response_body_like +SNI match failed as expected:.* + + + +=== TEST 30: verify error status in sni_radixtree_match span for failed match +--- exec +tail -n 5 ci/pod/otelcol-contrib/data-otlp.json | grep -A 10 -B 10 '"name":"sni_radixtree_match"' | grep -c '"status":"STATUS_ERROR"' || echo "0" +--- response_body eval +qr/[0-9]+/ + + + +=== TEST 31: test SSL router span attributes +--- exec +tail -n 10 ci/pod/otelcol-contrib/data-otlp.json | grep -A 20 '"name":"sni_radixtree_match"' | grep -q '"key":"span.kind"' && echo "span_kind_found" || echo "span_kind_not_found" +--- response_body +span_kind_found + + + +=== TEST 32: test internal span kind for SSL router spans +--- exec +tail -n 10 ci/pod/otelcol-contrib/data-otlp.json | grep -A 20 '"name":"create_router"' | grep -q '"stringValue":"SPAN_KIND_INTERNAL"' && echo "internal_kind_found" || echo "internal_kind_not_found" +--- response_body +internal_kind_found + + + +=== TEST 33: test multiple SNI matches create multiple spans +--- exec +curl -k -H "Host: test.com" https://127.0.0.1:1994/hello --resolve "test.com:1994:127.0.0.1" > /dev/null 2>&1 +curl -k -H "Host: test2.com" https://127.0.0.1:1994/hello --resolve "test2.com:1994:127.0.0.1" > /dev/null 2>&1 +echo "requests_sent" +--- wait: 2 +--- response_body +requests_sent + + + +=== TEST 34: verify multiple sni_radixtree_match spans +--- exec +grep -o '"name":"sni_radixtree_match"' ci/pod/otelcol-contrib/data-otlp.json | wc -l +--- response_body eval +qr/[2-9]\d*/ From f8d5974c31f83e53b836a173563a7bdbfea40b47 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 19:50:50 +0530 Subject: [PATCH 09/52] add test --- t/plugin/opentelemetry4.t | 192 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 t/plugin/opentelemetry4.t diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t new file mode 100644 index 000000000000..4a94aa88c4d5 --- /dev/null +++ b/t/plugin/opentelemetry4.t @@ -0,0 +1,192 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +BEGIN { + sub set_env_from_file { + my ($env_name, $file_path) = @_; + + open my $fh, '<', $file_path or die $!; + my $content = do { local $/; <$fh> }; + close $fh; + + $ENV{$env_name} = $content; + } + # set env + set_env_from_file('TEST_CERT', 't/certs/apisix.crt'); + set_env_from_file('TEST_KEY', 't/certs/apisix.key'); + set_env_from_file('TEST2_CERT', 't/certs/test2.crt'); + set_env_from_file('TEST2_KEY', 't/certs/test2.key'); +} +use t::APISIX 'no_plan'; +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->extra_yaml_config) { + my $extra_yaml_config = <<_EOC_; +plugins: + - opentelemetry +_EOC_ + $block->set_value("extra_yaml_config", $extra_yaml_config); + } + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!defined $block->response_body) { + $block->set_value("response_body", "passed\n"); + } + $block; +}); +repeat_each(1); +no_long_string(); +no_root_location(); +log_level("debug"); + +run_tests; + +__DATA__ + +=== TEST 1: add plugin metadata +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/opentelemetry', + ngx.HTTP_PUT, + [[{ + "batch_span_processor": { + "max_export_batch_size": 1, + "inactive_timeout": 0.5 + }, + "collector": { + "address": "127.0.0.1:4318", + "request_timeout": 3, + "request_headers": { + "foo": "bar" + } + }, + "trace_id_source": "x-request-id" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } + + + +=== TEST 2: set route +--- 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 3: set ssl with two certs and keys in env +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local data = { + snis = {"test.com"}, + key = "$env://TEST_KEY", + cert = "$env://TEST_CERT", + keys = {"$env://TEST2_KEY"}, + certs = {"$env://TEST2_CERT"} + } + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "snis": ["test.com"], + "key": "$env://TEST_KEY", + "cert": "$env://TEST_CERT", + "keys": ["$env://TEST2_KEY"], + "certs": ["$env://TEST2_CERT"] + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: trigger SSL match with SNI +--- exec +curl -s -k --resolve "test.com:1994:127.0.0.1" https://test.com:1994/opentracing +--- wait: 5 +--- response_body +opentracing + + + +=== TEST 5: check create router span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*create_router.*/ + + + +=== TEST 6: check sni_radixtree_match span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*sni_radixtree_match.*/ From 0c38fc7b2e9a73aa540f11fd22cc0a03d3c367a4 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 19:53:50 +0530 Subject: [PATCH 10/52] revert --- t/plugin/opentelemetry.t | 701 ++++++++++++++++++++------------------ t/plugin/opentelemetry2.t | 157 +++++++-- 2 files changed, 487 insertions(+), 371 deletions(-) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index 5cb2e0a4996f..759b248c6a80 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -14,23 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # -BEGIN { - sub set_env_from_file { - my ($env_name, $file_path) = @_; - open my $fh, '<', $file_path or die $!; - my $content = do { local $/; <$fh> }; - close $fh; - - $ENV{$env_name} = $content; - } - # set env - set_env_from_file('TEST_CERT', 't/certs/apisix.crt'); - set_env_from_file('TEST_KEY', 't/certs/apisix.key'); - set_env_from_file('TEST2_CERT', 't/certs/test2.crt'); - set_env_from_file('TEST2_KEY', 't/certs/test2.key'); -} use t::APISIX 'no_plan'; + add_block_preprocessor(sub { my ($block) = @_; @@ -38,10 +24,28 @@ add_block_preprocessor(sub { my $extra_yaml_config = <<_EOC_; plugins: - opentelemetry +plugin_attr: + opentelemetry: + batch_span_processor: + max_export_batch_size: 1 + inactive_timeout: 0.5 _EOC_ $block->set_value("extra_yaml_config", $extra_yaml_config); } + + if (!$block->extra_init_by_lua) { + my $extra_init_by_lua = <<_EOC_; +-- mock exporter http client +local client = require("opentelemetry.trace.exporter.http_client") +client.do_request = function() + ngx.log(ngx.INFO, "opentelemetry export span") +end +_EOC_ + + $block->set_value("extra_init_by_lua", $extra_init_by_lua); + } + if (!$block->request) { $block->set_value("request", "GET /t"); } @@ -49,8 +53,14 @@ _EOC_ if (!defined $block->response_body) { $block->set_value("response_body", "passed\n"); } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]"); + } + $block; }); + repeat_each(1); no_long_string(); no_root_location(); @@ -60,28 +70,31 @@ run_tests; __DATA__ -=== TEST 1: add plugin metadata +=== TEST 1: add plugin --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/plugin_metadata/opentelemetry', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "batch_span_processor": { - "max_export_batch_size": 1, - "inactive_timeout": 0.5 - }, - "collector": { - "address": "127.0.0.1:4318", - "request_timeout": 3, - "request_headers": { - "foo": "bar" + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + } } }, - "trace_id_source": "x-request-id" + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/opentracing" }]] ) + if code >= 300 then ngx.status = code end @@ -91,7 +104,20 @@ __DATA__ -=== TEST 2: add plugin +=== TEST 2: trigger opentelemetry +--- request +GET /opentracing +--- response_body +opentracing +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span + + + +=== TEST 3: use default always_off sampler --- config location /t { content_by_lua_block { @@ -101,9 +127,6 @@ __DATA__ [[{ "plugins": { "opentelemetry": { - "sampler": { - "name": "always_on" - } } }, "upstream": { @@ -122,29 +145,21 @@ __DATA__ ngx.say(body) } } ---- request -GET /t -=== TEST 3: trigger opentelemetry +=== TEST 4: not trigger opentelemetry --- request GET /opentracing ---- wait: 2 --- response_body opentracing +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out -=== TEST 4: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*opentelemetry-lua.*/ - - - -=== TEST 5: use trace_id_ratio sampler, fraction = 1.0 +=== TEST 5: use trace_id_ratio sampler, default fraction = 0 --- config location /t { content_by_lua_block { @@ -155,10 +170,7 @@ qr/.*opentelemetry-lua.*/ "plugins": { "opentelemetry": { "sampler": { - "name": "trace_id_ratio", - "options": { - "fraction": 1.0 - } + "name": "trace_id_ratio" } } }, @@ -178,29 +190,21 @@ qr/.*opentelemetry-lua.*/ ngx.say(body) } } ---- request -GET /t -=== TEST 6: trigger opentelemetry +=== TEST 6: not trigger opentelemetry --- request GET /opentracing ---- wait: 2 --- response_body opentracing +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out -=== TEST 7: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*opentelemetry-lua.*/ - - - -=== TEST 8: use parent_base sampler, root sampler = trace_id_ratio with default fraction = 0 +=== TEST 7: use trace_id_ratio sampler, fraction = 1.0 --- config location /t { content_by_lua_block { @@ -211,11 +215,9 @@ qr/.*opentelemetry-lua.*/ "plugins": { "opentelemetry": { "sampler": { - "name": "parent_base", + "name": "trace_id_ratio", "options": { - "root": { - "name": "trace_id_ratio" - } + "fraction": 1.0 } } } @@ -236,31 +238,23 @@ qr/.*opentelemetry-lua.*/ ngx.say(body) } } ---- request -GET /t -=== TEST 9: trigger opentelemetry, trace_flag = 1 +=== TEST 8: trigger opentelemetry --- request GET /opentracing ---- more_headers -traceparent: 00-00000000000000000000000000000001-0000000000000001-01 ---- wait: 2 --- response_body opentracing +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span -=== TEST 10: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*"traceId":"00000000000000000000000000000001",.*/ - - - -=== TEST 11: use parent_base sampler, root sampler = trace_id_ratio with fraction = 1 +=== TEST 9: use parent_base sampler, default root sampler = always_off --- config location /t { content_by_lua_block { @@ -271,15 +265,7 @@ qr/.*"traceId":"00000000000000000000000000000001",.*/ "plugins": { "opentelemetry": { "sampler": { - "name": "parent_base", - "options": { - "root": { - "name": "trace_id_ratio", - "options": { - "fraction": 1.0 - } - } - } + "name": "parent_base" } } }, @@ -299,71 +285,47 @@ qr/.*"traceId":"00000000000000000000000000000001",.*/ ngx.say(body) } } ---- request -GET /t -=== TEST 12: trigger opentelemetry, trace_flag = 1 +=== TEST 10: not trigger opentelemetry --- request GET /opentracing ---- more_headers -traceparent: 00-00000000000000000000000000000001-0000000000000001-01 ---- wait: 2 --- response_body opentracing +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out -=== TEST 13: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*"traceId":"00000000000000000000000000000001",.*/ - - - -=== TEST 14: set additional_attributes +=== TEST 11: use parent_base sampler, root sampler = always_on --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/services/1', - ngx.HTTP_PUT, - [[{ - "name": "service_name", - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - } - }]] - ) - if code >= 300 then - ngx.status = code - ngx.say(body) - return - end local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "name": "route_name", "plugins": { "opentelemetry": { "sampler": { - "name": "always_on" - }, - "additional_attributes": [ - "http_user_agent", - "arg_foo", - "cookie_token", - "remote_addr" - ] + "name": "parent_base", + "options": { + "root": { + "name": "always_on" + } + } + } } }, - "uri": "/opentracing", - "service_id": "1" + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/opentracing" }]] ) @@ -373,33 +335,23 @@ qr/.*"traceId":"00000000000000000000000000000001",.*/ ngx.say(body) } } ---- request -GET /t -=== TEST 15: trigger opentelemetry +=== TEST 12: trigger opentelemetry --- request -GET /opentracing?foo=bar&a=b ---- more_headers -X-Request-Id: 01010101010101010101010101010101 -User-Agent: test_nginx -Cookie: token=auth_token; ---- wait: 2 +GET /opentracing --- response_body opentracing +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span -=== TEST 16: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*\/opentracing\?foo=bar.*/ - - - -=== TEST 17: create route for /specific_status +=== TEST 13: use parent_base sampler, root sampler = trace_id_ratio with default fraction = 0 --- config location /t { content_by_lua_block { @@ -407,21 +359,25 @@ qr/.*\/opentracing\?foo=bar.*/ local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "name": "route_name", "plugins": { "opentelemetry": { "sampler": { - "name": "always_on" + "name": "parent_base", + "options": { + "root": { + "name": "trace_id_ratio" + } + } } } }, - "uri": "/specific_status", "upstream": { "nodes": { "127.0.0.1:1980": 1 }, "type": "roundrobin" - } + }, + "uri": "/opentracing" }]] ) @@ -431,276 +387,343 @@ qr/.*\/opentracing\?foo=bar.*/ ngx.say(body) } } ---- request -GET /t -=== TEST 18: test response empty body +=== TEST 14: not trigger opentelemetry --- request -HEAD /specific_status +GET /opentracing --- response_body ---- wait: 2 - - - -=== TEST 19: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*\/specific_status.*/ +opentracing +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out -=== TEST 20: test create_router span when SSL router is created ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - - -- First, let's trigger SSL router creation by adding an SSL certificate - local code, body = t('/apisix/admin/ssls/1', - ngx.HTTP_PUT, - [[{ - "cert": "$env://TEST_CERT", - "key": "$env://TEST_KEY", - "snis": ["test.com"] - }]] - ) - - if code >= 300 then - ngx.status = code - ngx.say(body) - return - end - - ngx.say("SSL certificate added") - } - } +=== TEST 15: trigger opentelemetry, trace_flag = 1 --- request -GET /t +GET /opentracing +--- more_headers +traceparent: 00-00000000000000000000000000000001-0000000000000001-01 --- response_body -SSL certificate added +opentracing --- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span -=== TEST 21: verify create_router span in logs after SSL setup ---- exec -grep -c '"name":"create_router"' ci/pod/otelcol-contrib/data-otlp.json || echo "0" ---- response_body eval -qr/[1-9]\d*/ - - - -=== TEST 22: test sni_radixtree_match span with SSL request +=== TEST 16: use parent_base sampler, root sampler = trace_id_ratio with fraction = 1 --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - - -- Create a route that uses the SSL certificate local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ "plugins": { "opentelemetry": { "sampler": { - "name": "always_on" + "name": "parent_base", + "options": { + "root": { + "name": "trace_id_ratio", + "options": { + "fraction": 1.0 + } + } + } } } }, - "uri": "/hello", - "hosts": ["test.com"], "upstream": { "nodes": { "127.0.0.1:1980": 1 }, "type": "roundrobin" - } + }, + "uri": "/opentracing" }]] - ) - + ) + if code >= 300 then ngx.status = code - ngx.say(body) - return end - - ngx.say("Route created") + ngx.say(body) } } ---- request -GET /t ---- response_body -Route created -=== TEST 23: trigger SSL match with SNI ---- exec -curl -k -H "Host: test.com" https://127.0.0.1:1994/hello --resolve "test.com:1994:127.0.0.1" || echo "request_completed" ---- wait: 2 +=== TEST 17: trigger opentelemetry +--- request +GET /opentracing +--- response_body +opentracing +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span -=== TEST 24: verify sni_radixtree_match span in logs ---- exec -grep -c '"name":"sni_radixtree_match"' ci/pod/otelcol-contrib/data-otlp.json || echo "0" ---- response_body eval -qr/[1-9]\d*/ +=== TEST 18: not trigger opentelemetry, trace_flag = 0 +--- request +GET /opentracing +--- more_headers +traceparent: 00-00000000000000000000000000000001-0000000000000001-00 +--- response_body +opentracing +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out -=== TEST 25: test multiple SSL certificates trigger multiple create_router spans +=== TEST 19: set additional_attributes --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - - -- Add another SSL certificate to trigger router recreation - local code, body = t('/apisix/admin/ssls/2', + local code, body = t('/apisix/admin/services/1', ngx.HTTP_PUT, [[{ - "cert": "$env://TEST_CERT", - "key": "$env://TEST_KEY", - "snis": ["test2.com"] + "name": "service_name", + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } }]] ) - if code >= 300 then ngx.status = code ngx.say(body) return end - - ngx.say("Second SSL certificate added") + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "name": "route_name", + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + }, + "additional_attributes": [ + "http_user_agent", + "arg_foo", + "cookie_token", + "remote_addr" + ] + } + }, + "uri": "/opentracing", + "service_id": "1" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) } } ---- request -GET /t ---- response_body -Second SSL certificate added ---- wait: 1 - -=== TEST 26: verify create_router span count increased after adding second SSL ---- exec -grep -o '"name":"create_router"' ci/pod/otelcol-contrib/data-otlp.json | wc -l ---- response_body eval -qr/[2-9]\d*/ - - -=== TEST 27: test SSL router error span status ---- config - location /t { - content_by_lua_block { - -- This test verifies that when SSL router creation fails, - -- the span status is set to ERROR - -- We'll simulate this by causing a router creation failure - - local ssl = require("apisix.ssl") - local orig_func = ssl.get_latest_certificates - - -- Temporarily replace the function to simulate failure - ssl.get_latest_certificates = function() - return nil, "simulated error" +=== TEST 20: trigger opentelemetry, test trace_id_source=x-request-id, custom resource, additional_attributes +--- extra_yaml_config +plugins: + - opentelemetry +plugin_attr: + opentelemetry: + trace_id_source: x-request-id + resource: + service.name: test + test_key: test_val + batch_span_processor: + max_export_batch_size: 1 + inactive_timeout: 0.5 +--- extra_init_by_lua + local core = require("apisix.core") + local otlp = require("opentelemetry.trace.exporter.otlp") + local span_kind = require("opentelemetry.trace.span_kind") + otlp.export_spans = function(self, spans) + if (#spans ~= 1) then + ngx.log(ngx.ERR, "unexpected spans length: ", #spans) + return + end + + local span = spans[1] + if span:context().trace_id ~= "01010101010101010101010101010101" then + ngx.log(ngx.ERR, "unexpected trace id: ", span:context().trace_id) + return + end + + local current_span_kind = span:plain().kind + if current_span_kind ~= span_kind.server then + ngx.log(ngx.ERR, "expected span.kind to be server but got ", current_span_kind) + return + end + + if span.name ~= "/opentracing?foo=bar&a=b" then + ngx.log(ngx.ERR, "expect span name: /opentracing?foo=bar&a=b, but got ", span.name) + return + end + + local expected_resource_attrs = { + test_key = "test_val", + } + expected_resource_attrs["service.name"] = "test" + expected_resource_attrs["telemetry.sdk.language"] = "lua" + expected_resource_attrs["telemetry.sdk.name"] = "opentelemetry-lua" + expected_resource_attrs["telemetry.sdk.version"] = "0.1.1" + expected_resource_attrs["hostname"] = core.utils.gethostname() + local actual_resource_attrs = span.tracer.provider.resource:attributes() + if #actual_resource_attrs ~= 6 then + ngx.log(ngx.ERR, "expect len(actual_resource) = 6, but got ", #actual_resource_attrs) + return + end + for _, attr in ipairs(actual_resource_attrs) do + local expected_val = expected_resource_attrs[attr.key] + if not expected_val then + ngx.log(ngx.ERR, "unexpected resource attr key: ", attr.key) + return end - - local radixtree_sni = require("apisix.ssl.router.radixtree_sni") - local api_ctx = {} - - -- This should trigger an error path in match_and_set - local ok, err = radixtree_sni.match_and_set(api_ctx, false, "test.com") - - -- Restore original function - ssl.get_latest_certificates = orig_func - - if not ok then - ngx.say("Error simulated successfully: ", err) - else - ngx.say("Unexpected success") + if attr.value.string_value ~= expected_val then + ngx.log(ngx.ERR, "unexpected resource attr val: ", attr.value.string_value) + return end + end + + local expected_attributes = { + service = "service_name", + route = "route_name", + http_user_agent = "test_nginx", + arg_foo = "bar", + cookie_token = "auth_token", + remote_addr = "127.0.0.1", } - } ---- request -GET /t ---- response_body_like -Error simulated successfully:.* - - + if #span.attributes ~= 6 then + ngx.log(ngx.ERR, "expect len(span.attributes) = 6, but got ", #span.attributes) + return + end + for _, attr in ipairs(span.attributes) do + local expected_val = expected_attributes[attr.key] + if not expected_val then + ngx.log(ngx.ERR, "unexpected attr key: ", attr.key) + return + end + if attr.value.string_value ~= expected_val then + ngx.log(ngx.ERR, "unexpected attr val: ", attr.value.string_value) + return + end + end -=== TEST 28: verify error status in create_router span ---- exec -tail -n 5 ci/pod/otelcol-contrib/data-otlp.json | grep -A 10 -B 10 '"name":"create_router"' | grep -c '"status":"STATUS_ERROR"' || echo "0" ---- response_body eval -qr/[0-9]+/ + ngx.log(ngx.INFO, "opentelemetry export span") + end +--- request +GET /opentracing?foo=bar&a=b +--- more_headers +X-Request-Id: 01010101010101010101010101010101 +User-Agent: test_nginx +Cookie: token=auth_token; +--- response_body +opentracing +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span -=== TEST 29: test SSL match failure span status +=== TEST 21: create route for /specific_status --- config location /t { content_by_lua_block { - local radixtree_sni = require("apisix.ssl.router.radixtree_sni") - local api_ctx = {} - - -- Try to match a non-existent SNI - local ok, err = radixtree_sni.match_and_set(api_ctx, false, "nonexistent.com") - - if not ok then - ngx.say("SNI match failed as expected: ", err) - else - ngx.say("Unexpected match success") + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_PUT, + [[{ + "name": "route_name", + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + } + } + }, + "uri": "/specific_status", + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code end + ngx.say(body) } } ---- request -GET /t ---- response_body_like -SNI match failed as expected:.* -=== TEST 30: verify error status in sni_radixtree_match span for failed match ---- exec -tail -n 5 ci/pod/otelcol-contrib/data-otlp.json | grep -A 10 -B 10 '"name":"sni_radixtree_match"' | grep -c '"status":"STATUS_ERROR"' || echo "0" ---- response_body eval -qr/[0-9]+/ - - - -=== TEST 31: test SSL router span attributes ---- exec -tail -n 10 ci/pod/otelcol-contrib/data-otlp.json | grep -A 20 '"name":"sni_radixtree_match"' | grep -q '"key":"span.kind"' && echo "span_kind_found" || echo "span_kind_not_found" ---- response_body -span_kind_found - +=== TEST 22: 500 status, test span.status +--- extra_init_by_lua + local otlp = require("opentelemetry.trace.exporter.otlp") + otlp.export_spans = function(self, spans) + if (#spans ~= 1) then + ngx.log(ngx.ERR, "unexpected spans length: ", #spans) + return + end + local span = spans[1] + if span.status.code ~= 2 then + ngx.log(ngx.ERR, "unexpected status.code: ", span.status.code) + end + if span.status.message ~= "upstream response status: 500" then + ngx.log(ngx.ERR, "unexpected status.message: ", span.status.message) + end -=== TEST 32: test internal span kind for SSL router spans ---- exec -tail -n 10 ci/pod/otelcol-contrib/data-otlp.json | grep -A 20 '"name":"create_router"' | grep -q '"stringValue":"SPAN_KIND_INTERNAL"' && echo "internal_kind_found" || echo "internal_kind_not_found" + ngx.log(ngx.INFO, "opentelemetry export span") + end +--- request +GET /specific_status +--- more_headers +X-Test-Upstream-Status: 500 +--- error_code: 500 --- response_body -internal_kind_found +upstream status: 500 +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span -=== TEST 33: test multiple SNI matches create multiple spans ---- exec -curl -k -H "Host: test.com" https://127.0.0.1:1994/hello --resolve "test.com:1994:127.0.0.1" > /dev/null 2>&1 -curl -k -H "Host: test2.com" https://127.0.0.1:1994/hello --resolve "test2.com:1994:127.0.0.1" > /dev/null 2>&1 -echo "requests_sent" ---- wait: 2 +=== TEST 23: test response empty body +--- extra_init_by_lua + local otlp = require("opentelemetry.trace.exporter.otlp") + otlp.export_spans = function(self, spans) + ngx.log(ngx.INFO, "opentelemetry export span") + end +--- request +HEAD /specific_status --- response_body -requests_sent - - - -=== TEST 34: verify multiple sni_radixtree_match spans ---- exec -grep -o '"name":"sni_radixtree_match"' ci/pod/otelcol-contrib/data-otlp.json | wc -l ---- response_body eval -qr/[2-9]\d*/ +--- wait: 1 +--- grep_error_log eval +qr/opentelemetry export span/ +--- grep_error_log_out +opentelemetry export span diff --git a/t/plugin/opentelemetry2.t b/t/plugin/opentelemetry2.t index 6129f44b2ae6..2495d8ef2adf 100644 --- a/t/plugin/opentelemetry2.t +++ b/t/plugin/opentelemetry2.t @@ -26,14 +26,46 @@ plugins: - example-plugin - key-auth - opentelemetry +plugin_attr: + opentelemetry: + batch_span_processor: + max_export_batch_size: 1 + inactive_timeout: 0.5 _EOC_ $block->set_value("extra_yaml_config", $extra_yaml_config); } + + if (!$block->extra_init_by_lua) { + my $extra_init_by_lua = <<_EOC_; +-- mock exporter http client +local client = require("opentelemetry.trace.exporter.http_client") +client.do_request = function() + ngx.log(ngx.INFO, "opentelemetry export span") +end +local ctx_new = require("opentelemetry.context").new +require("opentelemetry.context").new = function (...) + local ctx = ctx_new(...) + local current = ctx.current + ctx.current = function (...) + ngx.log(ngx.INFO, "opentelemetry context current") + return current(...) + end + return ctx +end +_EOC_ + + $block->set_value("extra_init_by_lua", $extra_init_by_lua); + } + if (!$block->request) { $block->set_value("request", "GET /t"); } + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]"); + } + $block; }); @@ -41,31 +73,7 @@ run_tests; __DATA__ -=== TEST 1: add plugin metadata ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/plugin_metadata/opentelemetry', - ngx.HTTP_PUT, - [[{ - "batch_span_processor": { - "max_export_batch_size": 1, - "inactive_timeout": 0.5 - }, - "trace_id_source": "x-request-id" - }]] - ) - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } - - - -=== TEST 2: trace request rejected by auth +=== TEST 1: trace request rejected by auth --- config location /t { content_by_lua_block { @@ -121,16 +129,101 @@ passed -=== TEST 3: trigger opentelemetry +=== TEST 2: trigger opentelemetry --- request GET /hello --- error_code: 401 ---- wait: 2 +--- wait: 1 +--- grep_error_log eval +qr/(opentelemetry export span|opentelemetry context current|plugin body_filter phase)/ +--- grep_error_log_out +plugin body_filter phase +plugin body_filter phase +opentelemetry context current +opentelemetry context current +opentelemetry export span -=== TEST 4: check log ---- exec -tail -n 1 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*\/hello.*/ +=== TEST 3: set additional_attributes with match +--- 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, + [[{ + "name": "route_name", + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + }, + "additional_header_prefix_attributes": [ + "x-my-header-*" + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/attributes" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 4: opentelemetry expands headers +--- extra_init_by_lua + local otlp = require("opentelemetry.trace.exporter.otlp") + otlp.export_spans = function(self, spans) + if (#spans ~= 1) then + ngx.log(ngx.ERR, "unexpected spans length: ", #spans) + return + end + + local attributes_names = {} + local attributes = {} + local span = spans[1] + for _, attribute in ipairs(span.attributes) do + if attribute.key == "hostname" then + -- remove any randomness + goto skip + end + table.insert(attributes_names, attribute.key) + attributes[attribute.key] = attribute.value.string_value or "" + ::skip:: + end + table.sort(attributes_names) + for _, attribute in ipairs(attributes_names) do + ngx.log(ngx.INFO, "attribute " .. attribute .. ": \"" .. attributes[attribute] .. "\"") + end + + ngx.log(ngx.INFO, "opentelemetry export span") + end +--- request +GET /attributes +--- more_headers +x-my-header-name: william +x-my-header-nick: bill +--- wait: 1 +--- error_code: 404 +--- grep_error_log eval +qr/attribute .+?:.[^,]*/ +--- grep_error_log_out +attribute route: "route_name" +attribute service: "" +attribute x-my-header-name: "william" +attribute x-my-header-nick: "bill" From ce4277ca88da72d7d0896d5867b2ab657e60db07 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 19:55:16 +0530 Subject: [PATCH 11/52] revert --- t/plugin/opentelemetry.t | 460 +++++++------------------------------- t/plugin/opentelemetry2.t | 157 +++---------- t/plugin/opentelemetry3.t | 2 - 3 files changed, 116 insertions(+), 503 deletions(-) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index 759b248c6a80..68232d749588 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -1,3 +1,4 @@ + # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with @@ -16,7 +17,6 @@ # use t::APISIX 'no_plan'; - add_block_preprocessor(sub { my ($block) = @_; @@ -24,28 +24,10 @@ add_block_preprocessor(sub { my $extra_yaml_config = <<_EOC_; plugins: - opentelemetry -plugin_attr: - opentelemetry: - batch_span_processor: - max_export_batch_size: 1 - inactive_timeout: 0.5 _EOC_ $block->set_value("extra_yaml_config", $extra_yaml_config); } - - if (!$block->extra_init_by_lua) { - my $extra_init_by_lua = <<_EOC_; --- mock exporter http client -local client = require("opentelemetry.trace.exporter.http_client") -client.do_request = function() - ngx.log(ngx.INFO, "opentelemetry export span") -end -_EOC_ - - $block->set_value("extra_init_by_lua", $extra_init_by_lua); - } - if (!$block->request) { $block->set_value("request", "GET /t"); } @@ -53,14 +35,8 @@ _EOC_ if (!defined $block->response_body) { $block->set_value("response_body", "passed\n"); } - - if (!$block->no_error_log && !$block->error_log) { - $block->set_value("no_error_log", "[error]"); - } - $block; }); - repeat_each(1); no_long_string(); no_root_location(); @@ -70,75 +46,28 @@ run_tests; __DATA__ -=== TEST 1: add plugin +=== TEST 1: add plugin metadata --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', + local code, body = t('/apisix/admin/plugin_metadata/opentelemetry', ngx.HTTP_PUT, [[{ - "plugins": { - "opentelemetry": { - "sampler": { - "name": "always_on" - } - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" + "batch_span_processor": { + "max_export_batch_size": 1, + "inactive_timeout": 0.5 }, - "uri": "/opentracing" - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } - - - -=== TEST 2: trigger opentelemetry ---- request -GET /opentracing ---- response_body -opentracing ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span - - - -=== TEST 3: use default always_off sampler ---- 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": { + "collector": { + "address": "127.0.0.1:4318", + "request_timeout": 3, + "request_headers": { + "foo": "bar" } }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uri": "/opentracing" + "trace_id_source": "x-request-id" }]] ) - if code >= 300 then ngx.status = code end @@ -148,18 +77,7 @@ opentelemetry export span -=== TEST 4: not trigger opentelemetry ---- request -GET /opentracing ---- response_body -opentracing ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out - - - -=== TEST 5: use trace_id_ratio sampler, default fraction = 0 +=== TEST 2: add plugin --- config location /t { content_by_lua_block { @@ -170,7 +88,7 @@ qr/opentelemetry export span/ "plugins": { "opentelemetry": { "sampler": { - "name": "trace_id_ratio" + "name": "always_on" } } }, @@ -190,21 +108,29 @@ qr/opentelemetry export span/ ngx.say(body) } } +--- request +GET /t -=== TEST 6: not trigger opentelemetry +=== TEST 3: trigger opentelemetry --- request GET /opentracing +--- wait: 2 --- response_body opentracing ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -=== TEST 7: use trace_id_ratio sampler, fraction = 1.0 +=== TEST 4: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*opentelemetry-lua.*/ + + + +=== TEST 5: use trace_id_ratio sampler, fraction = 1.0 --- config location /t { content_by_lua_block { @@ -238,120 +164,29 @@ qr/opentelemetry export span/ ngx.say(body) } } - - - -=== TEST 8: trigger opentelemetry --- request -GET /opentracing ---- response_body -opentracing ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span +GET /t -=== TEST 9: use parent_base sampler, default root sampler = always_off ---- 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": "parent_base" - } - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uri": "/opentracing" - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } - - - -=== TEST 10: not trigger opentelemetry +=== TEST 6: trigger opentelemetry --- request GET /opentracing +--- wait: 2 --- response_body opentracing ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -=== TEST 11: use parent_base sampler, root sampler = always_on ---- 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": "parent_base", - "options": { - "root": { - "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) - } - } +=== TEST 7: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*opentelemetry-lua.*/ -=== TEST 12: trigger opentelemetry ---- request -GET /opentracing ---- response_body -opentracing ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span - - - -=== TEST 13: use parent_base sampler, root sampler = trace_id_ratio with default fraction = 0 +=== TEST 8: use parent_base sampler, root sampler = trace_id_ratio with default fraction = 0 --- config location /t { content_by_lua_block { @@ -387,36 +222,31 @@ opentelemetry export span ngx.say(body) } } - - - -=== TEST 14: not trigger opentelemetry --- request -GET /opentracing ---- response_body -opentracing ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out +GET /t -=== TEST 15: trigger opentelemetry, trace_flag = 1 +=== TEST 9: trigger opentelemetry, trace_flag = 1 --- request GET /opentracing --- more_headers traceparent: 00-00000000000000000000000000000001-0000000000000001-01 +--- wait: 2 --- response_body opentracing ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span -=== TEST 16: use parent_base sampler, root sampler = trace_id_ratio with fraction = 1 +=== TEST 10: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*"traceId":"00000000000000000000000000000001",.*/ + + + +=== TEST 11: use parent_base sampler, root sampler = trace_id_ratio with fraction = 1 --- config location /t { content_by_lua_block { @@ -455,36 +285,31 @@ opentelemetry export span ngx.say(body) } } +--- request +GET /t -=== TEST 17: trigger opentelemetry +=== TEST 12: trigger opentelemetry, trace_flag = 1 --- request GET /opentracing +--- more_headers +traceparent: 00-00000000000000000000000000000001-0000000000000001-01 +--- wait: 2 --- response_body opentracing ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span -=== TEST 18: not trigger opentelemetry, trace_flag = 0 ---- request -GET /opentracing ---- more_headers -traceparent: 00-00000000000000000000000000000001-0000000000000001-00 ---- response_body -opentracing ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out +=== TEST 13: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*"traceId":"00000000000000000000000000000001",.*/ -=== TEST 19: set additional_attributes +=== TEST 14: set additional_attributes --- config location /t { content_by_lua_block { @@ -534,122 +359,38 @@ qr/opentelemetry export span/ ngx.say(body) } } +--- request +GET /t -=== TEST 20: trigger opentelemetry, test trace_id_source=x-request-id, custom resource, additional_attributes ---- extra_yaml_config -plugins: - - opentelemetry -plugin_attr: - opentelemetry: - trace_id_source: x-request-id - resource: - service.name: test - test_key: test_val - batch_span_processor: - max_export_batch_size: 1 - inactive_timeout: 0.5 ---- extra_init_by_lua - local core = require("apisix.core") - local otlp = require("opentelemetry.trace.exporter.otlp") - local span_kind = require("opentelemetry.trace.span_kind") - otlp.export_spans = function(self, spans) - if (#spans ~= 1) then - ngx.log(ngx.ERR, "unexpected spans length: ", #spans) - return - end - - local span = spans[1] - if span:context().trace_id ~= "01010101010101010101010101010101" then - ngx.log(ngx.ERR, "unexpected trace id: ", span:context().trace_id) - return - end - - local current_span_kind = span:plain().kind - if current_span_kind ~= span_kind.server then - ngx.log(ngx.ERR, "expected span.kind to be server but got ", current_span_kind) - return - end - - if span.name ~= "/opentracing?foo=bar&a=b" then - ngx.log(ngx.ERR, "expect span name: /opentracing?foo=bar&a=b, but got ", span.name) - return - end - - local expected_resource_attrs = { - test_key = "test_val", - } - expected_resource_attrs["service.name"] = "test" - expected_resource_attrs["telemetry.sdk.language"] = "lua" - expected_resource_attrs["telemetry.sdk.name"] = "opentelemetry-lua" - expected_resource_attrs["telemetry.sdk.version"] = "0.1.1" - expected_resource_attrs["hostname"] = core.utils.gethostname() - local actual_resource_attrs = span.tracer.provider.resource:attributes() - if #actual_resource_attrs ~= 6 then - ngx.log(ngx.ERR, "expect len(actual_resource) = 6, but got ", #actual_resource_attrs) - return - end - for _, attr in ipairs(actual_resource_attrs) do - local expected_val = expected_resource_attrs[attr.key] - if not expected_val then - ngx.log(ngx.ERR, "unexpected resource attr key: ", attr.key) - return - end - if attr.value.string_value ~= expected_val then - ngx.log(ngx.ERR, "unexpected resource attr val: ", attr.value.string_value) - return - end - end - - local expected_attributes = { - service = "service_name", - route = "route_name", - http_user_agent = "test_nginx", - arg_foo = "bar", - cookie_token = "auth_token", - remote_addr = "127.0.0.1", - } - if #span.attributes ~= 6 then - ngx.log(ngx.ERR, "expect len(span.attributes) = 6, but got ", #span.attributes) - return - end - for _, attr in ipairs(span.attributes) do - local expected_val = expected_attributes[attr.key] - if not expected_val then - ngx.log(ngx.ERR, "unexpected attr key: ", attr.key) - return - end - if attr.value.string_value ~= expected_val then - ngx.log(ngx.ERR, "unexpected attr val: ", attr.value.string_value) - return - end - end - - ngx.log(ngx.INFO, "opentelemetry export span") - end +=== TEST 15: trigger opentelemetry --- request GET /opentracing?foo=bar&a=b --- more_headers X-Request-Id: 01010101010101010101010101010101 User-Agent: test_nginx Cookie: token=auth_token; +--- wait: 2 --- response_body opentracing ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span -=== TEST 21: create route for /specific_status +=== TEST 16: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*\/opentracing\?foo=bar.*/ + + + +=== TEST 17: create route for /specific_status --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/2', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ "name": "route_name", @@ -676,54 +417,21 @@ opentelemetry export span ngx.say(body) } } +--- request +GET /t -=== TEST 22: 500 status, test span.status ---- extra_init_by_lua - local otlp = require("opentelemetry.trace.exporter.otlp") - otlp.export_spans = function(self, spans) - if (#spans ~= 1) then - ngx.log(ngx.ERR, "unexpected spans length: ", #spans) - return - end - - local span = spans[1] - if span.status.code ~= 2 then - ngx.log(ngx.ERR, "unexpected status.code: ", span.status.code) - end - if span.status.message ~= "upstream response status: 500" then - ngx.log(ngx.ERR, "unexpected status.message: ", span.status.message) - end - - ngx.log(ngx.INFO, "opentelemetry export span") - end +=== TEST 18: test response empty body --- request -GET /specific_status ---- more_headers -X-Test-Upstream-Status: 500 ---- error_code: 500 +HEAD /specific_status --- response_body -upstream status: 500 ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span +--- wait: 2 -=== TEST 23: test response empty body ---- extra_init_by_lua - local otlp = require("opentelemetry.trace.exporter.otlp") - otlp.export_spans = function(self, spans) - ngx.log(ngx.INFO, "opentelemetry export span") - end ---- request -HEAD /specific_status ---- response_body ---- wait: 1 ---- grep_error_log eval -qr/opentelemetry export span/ ---- grep_error_log_out -opentelemetry export span +=== TEST 19: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*\/specific_status.*/ diff --git a/t/plugin/opentelemetry2.t b/t/plugin/opentelemetry2.t index 2495d8ef2adf..6129f44b2ae6 100644 --- a/t/plugin/opentelemetry2.t +++ b/t/plugin/opentelemetry2.t @@ -26,46 +26,14 @@ plugins: - example-plugin - key-auth - opentelemetry -plugin_attr: - opentelemetry: - batch_span_processor: - max_export_batch_size: 1 - inactive_timeout: 0.5 _EOC_ $block->set_value("extra_yaml_config", $extra_yaml_config); } - - if (!$block->extra_init_by_lua) { - my $extra_init_by_lua = <<_EOC_; --- mock exporter http client -local client = require("opentelemetry.trace.exporter.http_client") -client.do_request = function() - ngx.log(ngx.INFO, "opentelemetry export span") -end -local ctx_new = require("opentelemetry.context").new -require("opentelemetry.context").new = function (...) - local ctx = ctx_new(...) - local current = ctx.current - ctx.current = function (...) - ngx.log(ngx.INFO, "opentelemetry context current") - return current(...) - end - return ctx -end -_EOC_ - - $block->set_value("extra_init_by_lua", $extra_init_by_lua); - } - if (!$block->request) { $block->set_value("request", "GET /t"); } - if (!$block->no_error_log && !$block->error_log) { - $block->set_value("no_error_log", "[error]"); - } - $block; }); @@ -73,7 +41,31 @@ run_tests; __DATA__ -=== TEST 1: trace request rejected by auth +=== TEST 1: add plugin metadata +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/opentelemetry', + ngx.HTTP_PUT, + [[{ + "batch_span_processor": { + "max_export_batch_size": 1, + "inactive_timeout": 0.5 + }, + "trace_id_source": "x-request-id" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } + + + +=== TEST 2: trace request rejected by auth --- config location /t { content_by_lua_block { @@ -129,101 +121,16 @@ passed -=== TEST 2: trigger opentelemetry +=== TEST 3: trigger opentelemetry --- request GET /hello --- error_code: 401 ---- wait: 1 ---- grep_error_log eval -qr/(opentelemetry export span|opentelemetry context current|plugin body_filter phase)/ ---- grep_error_log_out -plugin body_filter phase -plugin body_filter phase -opentelemetry context current -opentelemetry context current -opentelemetry export span +--- wait: 2 -=== TEST 3: set additional_attributes with match ---- 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, - [[{ - "name": "route_name", - "plugins": { - "opentelemetry": { - "sampler": { - "name": "always_on" - }, - "additional_header_prefix_attributes": [ - "x-my-header-*" - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uri": "/attributes" - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 4: opentelemetry expands headers ---- extra_init_by_lua - local otlp = require("opentelemetry.trace.exporter.otlp") - otlp.export_spans = function(self, spans) - if (#spans ~= 1) then - ngx.log(ngx.ERR, "unexpected spans length: ", #spans) - return - end - - local attributes_names = {} - local attributes = {} - local span = spans[1] - for _, attribute in ipairs(span.attributes) do - if attribute.key == "hostname" then - -- remove any randomness - goto skip - end - table.insert(attributes_names, attribute.key) - attributes[attribute.key] = attribute.value.string_value or "" - ::skip:: - end - table.sort(attributes_names) - for _, attribute in ipairs(attributes_names) do - ngx.log(ngx.INFO, "attribute " .. attribute .. ": \"" .. attributes[attribute] .. "\"") - end - - ngx.log(ngx.INFO, "opentelemetry export span") - end ---- request -GET /attributes ---- more_headers -x-my-header-name: william -x-my-header-nick: bill ---- wait: 1 ---- error_code: 404 ---- grep_error_log eval -qr/attribute .+?:.[^,]*/ ---- grep_error_log_out -attribute route: "route_name" -attribute service: "" -attribute x-my-header-name: "william" -attribute x-my-header-nick: "bill" +=== TEST 4: check log +--- exec +tail -n 1 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*\/hello.*/ diff --git a/t/plugin/opentelemetry3.t b/t/plugin/opentelemetry3.t index 9a18de31e1c5..ff7ea14e56e5 100644 --- a/t/plugin/opentelemetry3.t +++ b/t/plugin/opentelemetry3.t @@ -161,8 +161,6 @@ hello world qr/opentelemetry export span/ --- grep_error_log_out opentelemetry export span -opentelemetry export span -opentelemetry export span --- error_log eval qr/request log: \{.*"opentelemetry_context_traceparent":"00-\w{32}-\w{16}-01".*\}/ From 1d6c4e250ddff72fd486a264e445e924eb1cc448 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 19:55:43 +0530 Subject: [PATCH 12/52] revert --- t/plugin/opentelemetry.t | 1 - 1 file changed, 1 deletion(-) diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t index 68232d749588..daf91e39e8bd 100644 --- a/t/plugin/opentelemetry.t +++ b/t/plugin/opentelemetry.t @@ -1,4 +1,3 @@ - # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with From 1f0cedb3766731fbd75c5a33b384d87fe81adb77 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 23 Oct 2025 21:29:01 +0530 Subject: [PATCH 13/52] f --- t/plugin/opentelemetry3.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/plugin/opentelemetry3.t b/t/plugin/opentelemetry3.t index ff7ea14e56e5..9a18de31e1c5 100644 --- a/t/plugin/opentelemetry3.t +++ b/t/plugin/opentelemetry3.t @@ -161,6 +161,8 @@ hello world qr/opentelemetry export span/ --- grep_error_log_out opentelemetry export span +opentelemetry export span +opentelemetry export span --- error_log eval qr/request log: \{.*"opentelemetry_context_traceparent":"00-\w{32}-\w{16}-01".*\}/ From e6c31c01750764b37b28e948d7170ebe26e7ff6d Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 11:12:42 +0530 Subject: [PATCH 14/52] add test --- t/plugin/opentelemetry4.t | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t index 4a94aa88c4d5..40d032914550 100644 --- a/t/plugin/opentelemetry4.t +++ b/t/plugin/opentelemetry4.t @@ -190,3 +190,71 @@ qr/.*create_router.*/ tail ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*sni_radixtree_match.*/ + + + +=== TEST 7: route with one upstream node +--- 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": { + "test1.com:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/opentracing" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 8: hit route +--- init_by_lua_block + require "resty.core" + apisix = require("apisix") + core = require("apisix.core") + apisix.http_init() + + local utils = require("apisix.core.utils") + utils.dns_parse = function (domain) -- mock: DNS parser + if domain == "test1.com" then + return {address = "127.0.0.2"} + end + + error("unknown domain: " .. domain) + end +--- request +GET /opentracing +--- response_body +opentracing + + + +=== TEST 9: check resolve_dns span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*resolve_dns.*/ From 234f9f7ef191298e2e738f6d43a5e07548a851ef Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 11:44:21 +0530 Subject: [PATCH 15/52] add plugin phase test --- t/plugin/opentelemetry4.t | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t index 40d032914550..94c198c827f3 100644 --- a/t/plugin/opentelemetry4.t +++ b/t/plugin/opentelemetry4.t @@ -258,3 +258,11 @@ opentracing tail ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*resolve_dns.*/ + + + +=== TEST 10: check apisix.phase.delayed_body_filter.opentelemetry span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*apisix.phase.delayed_body_filter.opentelemetry.*/ From c3f9ae9f260cf5c6f60e713085ebacd125382a5b Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 11:46:40 +0530 Subject: [PATCH 16/52] fix test --- t/plugin/opentelemetry4.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t index 94c198c827f3..e1d8a58e58d5 100644 --- a/t/plugin/opentelemetry4.t +++ b/t/plugin/opentelemetry4.t @@ -248,6 +248,7 @@ passed end --- request GET /opentracing +--- wait: 2 --- response_body opentracing From 03f39061df5bd5ba694ed6d66961905e8ea0fad7 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 12:15:00 +0530 Subject: [PATCH 17/52] add test --- t/plugin/opentelemetry4.t | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t index e1d8a58e58d5..07d874acf1ec 100644 --- a/t/plugin/opentelemetry4.t +++ b/t/plugin/opentelemetry4.t @@ -262,7 +262,31 @@ qr/.*resolve_dns.*/ -=== TEST 10: check apisix.phase.delayed_body_filter.opentelemetry span +=== TEST 10: check apisix.phase.access span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*apisix.phase.access.*/ + + + +=== TEST 11: check apisix.phase.rewrite span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*apisix.phase.rewrite.*/ + + + +=== TEST 12: check apisix.phase.header_filter span +--- exec +tail ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*apisix.phase.header_filter.*/ + + + +=== TEST 13: check apisix.phase.delayed_body_filter.opentelemetry span --- exec tail ci/pod/otelcol-contrib/data-otlp.json --- response_body eval From 12d25134d2bbb5604f2d2d1c4d1789c8ae13c5b4 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 12:24:53 +0530 Subject: [PATCH 18/52] f --- t/plugin/opentelemetry4.t | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t index 07d874acf1ec..9c491f5f724a 100644 --- a/t/plugin/opentelemetry4.t +++ b/t/plugin/opentelemetry4.t @@ -179,7 +179,7 @@ opentracing === TEST 5: check create router span --- exec -tail ci/pod/otelcol-contrib/data-otlp.json +tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*create_router.*/ @@ -187,7 +187,7 @@ qr/.*create_router.*/ === TEST 6: check sni_radixtree_match span --- exec -tail ci/pod/otelcol-contrib/data-otlp.json +tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*sni_radixtree_match.*/ @@ -256,7 +256,7 @@ opentracing === TEST 9: check resolve_dns span --- exec -tail ci/pod/otelcol-contrib/data-otlp.json +tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*resolve_dns.*/ @@ -270,23 +270,15 @@ qr/.*apisix.phase.access.*/ -=== TEST 11: check apisix.phase.rewrite span ---- exec -tail ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*apisix.phase.rewrite.*/ - - - -=== TEST 12: check apisix.phase.header_filter span +=== TEST 11: check apisix.phase.header_filter span --- exec -tail ci/pod/otelcol-contrib/data-otlp.json +tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*apisix.phase.header_filter.*/ -=== TEST 13: check apisix.phase.delayed_body_filter.opentelemetry span +=== TEST 12: check apisix.phase.delayed_body_filter.opentelemetry span --- exec tail ci/pod/otelcol-contrib/data-otlp.json --- response_body eval From f6e92b8c40eec19b2c8699a467f61097b49bfa28 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 12:26:05 +0530 Subject: [PATCH 19/52] f --- apisix/init.lua | 5 +++-- apisix/plugin.lua | 7 +++++-- apisix/utils/upstream.lua | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index c242ea6a436f..cffffc991761 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -674,7 +674,7 @@ end function _M.http_access_phase() - tracer.new_span("http_access_phase", tracer.kind.server) + tracer.new_span("apisix.phase.access", tracer.kind.server) -- from HTTP/3 to HTTP/1.1 we need to convert :authority pesudo-header -- to Host header, so we set upstream_host variable here. if ngx.req.http_version() == 3 then @@ -800,7 +800,6 @@ function _M.http_access_phase() else local plugins = plugin.filter(api_ctx, route) api_ctx.plugins = plugins - plugin.run_plugin("rewrite", plugins, api_ctx) if api_ctx.consumer then local changed @@ -920,8 +919,10 @@ end function _M.http_body_filter_phase() + tracer.new_span("apisix.phase.body_filter", tracer.kind.server) common_phase("body_filter") common_phase("delayed_body_filter") + tracer.finish_current_span() end diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 789eb528d546..003ceda2c5a4 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -38,6 +38,7 @@ local tostring = tostring local error = error local getmetatable = getmetatable local setmetatable = setmetatable +local tracer = require("apisix.utils.tracer") -- make linter happy to avoid error: getting the Lua global "load" -- luacheck: globals load, ignore lua_load local lua_load = load @@ -1221,7 +1222,7 @@ function _M.run_plugin(phase, plugins, api_ctx) end return api_ctx, plugin_run end - + tracer.new_span("apisix.phase." .. phase) for i = 1, #plugins, 2 do local phase_func = plugins[i][phase] local conf = plugins[i + 1] @@ -1229,11 +1230,13 @@ function _M.run_plugin(phase, plugins, api_ctx) plugin_run = true run_meta_pre_function(conf, api_ctx, plugins[i]["name"]) api_ctx._plugin_name = plugins[i]["name"] + tracer.new_span("apisix.phase." .. phase .. "." .. api_ctx._plugin_name) phase_func(conf, api_ctx) + tracer.finish_current_span() api_ctx._plugin_name = nil end end - + tracer.finish_current_span() return api_ctx, plugin_run end diff --git a/apisix/utils/upstream.lua b/apisix/utils/upstream.lua index 49e8a18a1075..a55eaae63e49 100644 --- a/apisix/utils/upstream.lua +++ b/apisix/utils/upstream.lua @@ -20,7 +20,7 @@ local ngx_now = ngx.now local ipairs = ipairs local type = type local tostring = tostring - +local tracer = require("apisix.utils.tracer") local _M = {} @@ -80,6 +80,7 @@ _M.compare_upstream_node = compare_upstream_node local function parse_domain_for_nodes(nodes) + tracer.new_span("resolve_dns", tracer.kind.internal) local new_nodes = core.table.new(#nodes, 0) for _, node in ipairs(nodes) do local host = node.host @@ -100,6 +101,7 @@ local function parse_domain_for_nodes(nodes) core.table.insert(new_nodes, node) end end + tracer.finish_current_span() return new_nodes end _M.parse_domain_for_nodes = parse_domain_for_nodes From 0577ab27d4f1dceba6550d1faeaa965a5a67c4d8 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 12:30:56 +0530 Subject: [PATCH 20/52] update docs --- docs/en/latest/plugins/opentelemetry.md | 147 +++++++++++++++++++--- docs/zh/latest/plugins/opentelemetry.md | 159 ++++++++++++++++++++---- 2 files changed, 268 insertions(+), 38 deletions(-) diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index 061c26212dd5..865ceeff7335 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -153,37 +153,152 @@ In OpenTelemetry collector's log, you should see information similar to the foll ```text 2024-02-18T17:14:03.825Z info ResourceSpans #0 -Resource SchemaURL: -Resource attributes: - -> telemetry.sdk.language: Str(lua) - -> telemetry.sdk.name: Str(opentelemetry-lua) - -> telemetry.sdk.version: Str(0.1.1) - -> hostname: Str(e34673e24631) - -> service.name: Str(APISIX) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope opentelemetry-lua Span #0 - Trace ID : fbd0a38d4ea4a128ff1a688197bc58b0 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 5a3835b61110d942 + Name : http_router_match + Kind : Internal + Start time : 2025-10-24 06:58:04.430430976 +0000 UTC + End time : 2025-10-24 06:58:04.431542016 +0000 UTC + Status code : Unset + Status message : +Span #1 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 4ab25e2b92f394e1 + Name : resolve_dns + Kind : Internal + Start time : 2025-10-24 06:58:04.432521984 +0000 UTC + End time : 2025-10-24 06:58:04.44903296 +0000 UTC + Status code : Unset + Status message : +Span #2 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 3620c0f05dd2be4f + Name : apisix.phase.header_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.960481024 +0000 UTC + End time : 2025-10-24 06:58:06.960510976 +0000 UTC + Status code : Unset + Status message : +Span #3 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 4c5f3476f62a7e8a + ID : a9bfad7bb6986e41 + Name : apisix.phase.body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.960579072 +0000 UTC + End time : 2025-10-24 06:58:06.96059008 +0000 UTC + Status code : Unset + Status message : +Span #4 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : b2994675df6baa83 + ID : 26705f9c47584a5b + Name : apisix.phase.delayed_body_filter.opentelemetry + Kind : Internal + Start time : 2025-10-24 06:58:06.960613888 +0000 UTC + End time : 2025-10-24 06:58:06.960687104 +0000 UTC + Status code : Unset + Status message : +Span #5 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 4c5f3476f62a7e8a + ID : b2994675df6baa83 + Name : apisix.phase.delayed_body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.96059904 +0000 UTC + End time : 2025-10-24 06:58:06.960692992 +0000 UTC + Status code : Unset + Status message : +Span #6 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 4c5f3476f62a7e8a + Name : apisix.phase.body_filter + Kind : Server + Start time : 2025-10-24 06:58:06.96056704 +0000 UTC + End time : 2025-10-24 06:58:06.960698112 +0000 UTC + Status code : Unset + Status message : +Span #7 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 2024d73d32cbd81b + ID : 223c64fb691a24e8 + Name : apisix.phase.body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.961624064 +0000 UTC + End time : 2025-10-24 06:58:06.961635072 +0000 UTC + Status code : Unset + Status message : +Span #8 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : fd193dd24c618f60 + ID : 8729ad6e0d94a23b + Name : apisix.phase.delayed_body_filter.opentelemetry + Kind : Internal + Start time : 2025-10-24 06:58:06.961648896 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #9 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 2024d73d32cbd81b + ID : fd193dd24c618f60 + Name : apisix.phase.delayed_body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.961641984 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #10 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 2024d73d32cbd81b + Name : apisix.phase.body_filter + Kind : Server + Start time : 2025-10-24 06:58:06.960980992 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #11 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : cfb0b4603dc2e385 + ID : 905f850f13e32bfb + Name : apisix.phase.access + Kind : Server + Start time : 2025-10-24 06:58:04.427932928 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #12 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a Parent ID : - ID : af3dc7642104748a - Name : GET /anything + ID : cfb0b4603dc2e385 + Name : GET /headers Kind : Server - Start time : 2024-02-18 17:14:03.763244032 +0000 UTC - End time : 2024-02-18 17:14:03.920229888 +0000 UTC + Start time : 2025-10-24 06:58:04.432427008 +0000 UTC + End time : 2025-10-24 06:58:06.962299904 +0000 UTC Status code : Unset Status message : Attributes: -> net.host.name: Str(127.0.0.1) -> http.method: Str(GET) -> http.scheme: Str(http) - -> http.target: Str(/anything) - -> http.user_agent: Str(curl/7.64.1) + -> http.target: Str(/headers) + -> http.user_agent: Str(curl/8.16.0) -> apisix.route_id: Str(otel-tracing-route) -> apisix.route_name: Empty() - -> http.route: Str(/anything) + -> http.route: Str(/headers) -> http.status_code: Int(200) -{"kind": "exporter", "data_type": "traces", "name": "debug"} + {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +2025-10-24T06:58:13.893Z info Metrics {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 25, "data points": 26} +2025-10-24T06:58:13.893Z info ResourceMetrics #0 ``` To visualize these traces, you can export your telemetry to backend Services, such as Zipkin and Prometheus. See [exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter) for more details. diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index f22d90c932b3..a55943560b0f 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -152,37 +152,152 @@ curl "http://127.0.0.1:9080/anything" ```text 2024-02-18T17:14:03.825Z info ResourceSpans #0 -Resource SchemaURL: -Resource attributes: - -> telemetry.sdk.language: Str(lua) - -> telemetry.sdk.name: Str(opentelemetry-lua) - -> telemetry.sdk.version: Str(0.1.1) - -> hostname: Str(e34673e24631) - -> service.name: Str(APISIX) ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope opentelemetry-lua Span #0 - Trace ID : fbd0a38d4ea4a128ff1a688197bc58b0 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 5a3835b61110d942 + Name : http_router_match + Kind : Internal + Start time : 2025-10-24 06:58:04.430430976 +0000 UTC + End time : 2025-10-24 06:58:04.431542016 +0000 UTC + Status code : Unset + Status message : +Span #1 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 4ab25e2b92f394e1 + Name : resolve_dns + Kind : Internal + Start time : 2025-10-24 06:58:04.432521984 +0000 UTC + End time : 2025-10-24 06:58:04.44903296 +0000 UTC + Status code : Unset + Status message : +Span #2 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 3620c0f05dd2be4f + Name : apisix.phase.header_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.960481024 +0000 UTC + End time : 2025-10-24 06:58:06.960510976 +0000 UTC + Status code : Unset + Status message : +Span #3 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 4c5f3476f62a7e8a + ID : a9bfad7bb6986e41 + Name : apisix.phase.body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.960579072 +0000 UTC + End time : 2025-10-24 06:58:06.96059008 +0000 UTC + Status code : Unset + Status message : +Span #4 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : b2994675df6baa83 + ID : 26705f9c47584a5b + Name : apisix.phase.delayed_body_filter.opentelemetry + Kind : Internal + Start time : 2025-10-24 06:58:06.960613888 +0000 UTC + End time : 2025-10-24 06:58:06.960687104 +0000 UTC + Status code : Unset + Status message : +Span #5 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 4c5f3476f62a7e8a + ID : b2994675df6baa83 + Name : apisix.phase.delayed_body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.96059904 +0000 UTC + End time : 2025-10-24 06:58:06.960692992 +0000 UTC + Status code : Unset + Status message : +Span #6 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 4c5f3476f62a7e8a + Name : apisix.phase.body_filter + Kind : Server + Start time : 2025-10-24 06:58:06.96056704 +0000 UTC + End time : 2025-10-24 06:58:06.960698112 +0000 UTC + Status code : Unset + Status message : +Span #7 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 2024d73d32cbd81b + ID : 223c64fb691a24e8 + Name : apisix.phase.body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.961624064 +0000 UTC + End time : 2025-10-24 06:58:06.961635072 +0000 UTC + Status code : Unset + Status message : +Span #8 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : fd193dd24c618f60 + ID : 8729ad6e0d94a23b + Name : apisix.phase.delayed_body_filter.opentelemetry + Kind : Internal + Start time : 2025-10-24 06:58:06.961648896 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #9 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 2024d73d32cbd81b + ID : fd193dd24c618f60 + Name : apisix.phase.delayed_body_filter + Kind : Internal + Start time : 2025-10-24 06:58:06.961641984 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #10 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : 905f850f13e32bfb + ID : 2024d73d32cbd81b + Name : apisix.phase.body_filter + Kind : Server + Start time : 2025-10-24 06:58:06.960980992 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #11 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a + Parent ID : cfb0b4603dc2e385 + ID : 905f850f13e32bfb + Name : apisix.phase.access + Kind : Server + Start time : 2025-10-24 06:58:04.427932928 +0000 UTC + End time : 1970-01-01 00:00:00 +0000 UTC + Status code : Unset + Status message : +Span #12 + Trace ID : 95a1644afaaf65e1f0193b1f193b990a Parent ID : - ID : af3dc7642104748a - Name : GET /anything + ID : cfb0b4603dc2e385 + Name : GET /headers Kind : Server - Start time : 2024-02-18 17:14:03.763244032 +0000 UTC - End time : 2024-02-18 17:14:03.920229888 +0000 UTC + Start time : 2025-10-24 06:58:04.432427008 +0000 UTC + End time : 2025-10-24 06:58:06.962299904 +0000 UTC Status code : Unset Status message : Attributes: - -> net.host.name: Str(127.0.0.1) - -> http.method: Str(GET) - -> http.scheme: Str(http) - -> http.target: Str(/anything) - -> http.user_agent: Str(curl/7.64.1) - -> apisix.route_id: Str(otel-tracing-route) - -> apisix.route_name: Empty() - -> http.route: Str(/anything) - -> http.status_code: Int(200) -{"kind": "exporter", "data_type": "traces", "name": "debug"} + -> net.host.name: Str(127.0.0.1) + -> http.method: Str(GET) + -> http.scheme: Str(http) + -> http.target: Str(/headers) + -> http.user_agent: Str(curl/8.16.0) + -> apisix.route_id: Str(otel-tracing-route) + -> apisix.route_name: Empty() + -> http.route: Str(/headers) + -> http.status_code: Int(200) + {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +2025-10-24T06:58:13.893Z info Metrics {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 25, "data points": 26} +2025-10-24T06:58:13.893Z info ResourceMetrics #0 ``` 要可视化这些追踪,你可以将 traces 导出到后端服务,例如 Zipkin 和 Prometheus。有关更多详细信息,请参阅[exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter)。 From b1aaba22e155b0a884f4fd166fc35be0ae2102f2 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 12:41:24 +0530 Subject: [PATCH 21/52] fix lint --- docs/en/latest/plugins/opentelemetry.md | 2 +- docs/zh/latest/plugins/opentelemetry.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index 865ceeff7335..6891a377e273 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -296,7 +296,7 @@ Attributes: -> apisix.route_name: Empty() -> http.route: Str(/headers) -> http.status_code: Int(200) - {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +{"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} 2025-10-24T06:58:13.893Z info Metrics {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 25, "data points": 26} 2025-10-24T06:58:13.893Z info ResourceMetrics #0 ``` diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index a55943560b0f..18f0566bd03d 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -295,7 +295,7 @@ Attributes: -> apisix.route_name: Empty() -> http.route: Str(/headers) -> http.status_code: Int(200) - {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +{"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} 2025-10-24T06:58:13.893Z info Metrics {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 25, "data points": 26} 2025-10-24T06:58:13.893Z info ResourceMetrics #0 ``` From c3f37eba7ca5cd2990b40a0b337300834d8a4293 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 13:32:11 +0530 Subject: [PATCH 22/52] fix otel3 --- t/plugin/opentelemetry3.t | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/plugin/opentelemetry3.t b/t/plugin/opentelemetry3.t index 9a18de31e1c5..228f0f6d6763 100644 --- a/t/plugin/opentelemetry3.t +++ b/t/plugin/opentelemetry3.t @@ -163,6 +163,17 @@ qr/opentelemetry export span/ opentelemetry export span opentelemetry export span opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span +opentelemetry export span --- error_log eval qr/request log: \{.*"opentelemetry_context_traceparent":"00-\w{32}-\w{16}-01".*\}/ From cab6620864b4ebff786e3325e758dbf82466572b Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 18:32:00 +0530 Subject: [PATCH 23/52] fix tests --- t/plugin/opentelemetry4.t | 106 +++++++++++--------------------------- 1 file changed, 30 insertions(+), 76 deletions(-) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry4.t index 9c491f5f724a..93ffe40bdd82 100644 --- a/t/plugin/opentelemetry4.t +++ b/t/plugin/opentelemetry4.t @@ -60,7 +60,15 @@ run_tests; __DATA__ -=== TEST 1: add plugin metadata +=== TEST 1: check sni_radixtree_match span +--- exec +echo '' > ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr// + + + +=== TEST 2: add plugin metadata --- config location /t { content_by_lua_block { @@ -91,7 +99,7 @@ __DATA__ -=== TEST 2: set route +=== TEST 3: set route --- config location /t { content_by_lua_block { @@ -108,7 +116,7 @@ __DATA__ }, "upstream": { "nodes": { - "127.0.0.1:1980": 1 + "test1.com:1980": 1 }, "type": "roundrobin" }, @@ -127,7 +135,7 @@ GET /t -=== TEST 3: set ssl with two certs and keys in env +=== TEST 4: set ssl with two certs and keys in env --- config location /t { content_by_lua_block { @@ -168,70 +176,7 @@ passed -=== TEST 4: trigger SSL match with SNI ---- exec -curl -s -k --resolve "test.com:1994:127.0.0.1" https://test.com:1994/opentracing ---- wait: 5 ---- response_body -opentracing - - - -=== TEST 5: check create router span ---- exec -tail -n 12 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*create_router.*/ - - - -=== TEST 6: check sni_radixtree_match span ---- exec -tail -n 12 ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*sni_radixtree_match.*/ - - - -=== TEST 7: route with one upstream node ---- 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": { - "test1.com:1980": 1 - }, - "type": "roundrobin" - }, - "uri": "/opentracing" - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed - - - -=== TEST 8: hit route +=== TEST 5: trigger SSL match with SNI --- init_by_lua_block require "resty.core" apisix = require("apisix") @@ -246,23 +191,32 @@ passed error("unknown domain: " .. domain) end ---- request -GET /opentracing ---- wait: 2 +--- exec +curl -k --resolve "test.com:1994:127.0.0.1" https://test.com:1994/opentracing +--- wait: 5 --- response_body opentracing -=== TEST 9: check resolve_dns span +=== TEST 6: check sni_radixtree_match span --- exec -tail -n 12 ci/pod/otelcol-contrib/data-otlp.json +sed -i '$d' ci/pod/otelcol-contrib/data-otlp.json +tail -n 13 ci/pod/otelcol-contrib/data-otlp.json +--- response_body eval +qr/.*sni_radixtree_match.*/ + + + +=== TEST 7: check resolve_dns span +--- exec +tail -n 13 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*resolve_dns.*/ -=== TEST 10: check apisix.phase.access span +=== TEST 8: check apisix.phase.access span --- exec tail ci/pod/otelcol-contrib/data-otlp.json --- response_body eval @@ -270,7 +224,7 @@ qr/.*apisix.phase.access.*/ -=== TEST 11: check apisix.phase.header_filter span +=== TEST 9: check apisix.phase.header_filter span --- exec tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval @@ -278,7 +232,7 @@ qr/.*apisix.phase.header_filter.*/ -=== TEST 12: check apisix.phase.delayed_body_filter.opentelemetry span +=== TEST 10: check apisix.phase.delayed_body_filter.opentelemetry span --- exec tail ci/pod/otelcol-contrib/data-otlp.json --- response_body eval From 9d195e5c5ac718ba758e24f06f7af5f419b18d65 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Fri, 24 Oct 2025 18:59:58 +0530 Subject: [PATCH 24/52] remove todo --- apisix/plugins/opentelemetry.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index cbf9c80772ee..e5850fdf6a4b 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -411,8 +411,8 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) if root_span_ctx.span and not root_span_ctx:span():is_recording() then return end - -- TODO: we should create another tracer object with always_on sampler in here, - -- because the root span already decided to sample, all child spans should be sampled too. + local conf = core.table.deepcopy(conf) + conf.sampler.name = "always_on" local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil, create_tracer_obj, conf, plugin_info) if not tracer then From b5056303160e343ad47fb11d6aeee23cdb8ff893 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Sun, 26 Oct 2025 00:42:43 +0530 Subject: [PATCH 25/52] rename --- t/plugin/{opentelemetry4.t => opentelemetry6.t} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename t/plugin/{opentelemetry4.t => opentelemetry6.t} (100%) diff --git a/t/plugin/opentelemetry4.t b/t/plugin/opentelemetry6.t similarity index 100% rename from t/plugin/opentelemetry4.t rename to t/plugin/opentelemetry6.t From 9fc46b0e4ffc4651c2acc0872a86ce8b35811d76 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Sun, 26 Oct 2025 11:24:25 +0530 Subject: [PATCH 26/52] Update opentelemetry6.t --- t/plugin/opentelemetry6.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/plugin/opentelemetry6.t b/t/plugin/opentelemetry6.t index 93ffe40bdd82..78a56c54aaec 100644 --- a/t/plugin/opentelemetry6.t +++ b/t/plugin/opentelemetry6.t @@ -60,7 +60,7 @@ run_tests; __DATA__ -=== TEST 1: check sni_radixtree_match span +=== TEST 1: empty file --- exec echo '' > ci/pod/otelcol-contrib/data-otlp.json --- response_body eval From c5be5f942ca7c9b01a6c632b90c295c07bc073ba Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 11:56:29 +0530 Subject: [PATCH 27/52] apply suggestions --- apisix/core/response.lua | 2 +- apisix/plugin.lua | 2 +- apisix/utils/tracer.lua | 12 ++++++++++++ t/plugin/opentelemetry6.t | 4 ++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/apisix/core/response.lua b/apisix/core/response.lua index 27be28c572bc..d6199bbb2b28 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -90,7 +90,7 @@ function resp_exit(code, ...) if code then if code >= 400 then - tracer.finish_current_span(tracer.status.ERROR, message or ("response code " .. code)) + tracer.finish_all_spans(tracer.status.ERROR, message or ("response code " .. code)) end return ngx_exit(code) end diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 003ceda2c5a4..be2e12cd526e 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -1222,7 +1222,7 @@ function _M.run_plugin(phase, plugins, api_ctx) end return api_ctx, plugin_run end - tracer.new_span("apisix.phase." .. phase) + tracer.new_span("apisix.plugins.phase." .. phase) for i = 1, #plugins, 2 do local phase_func = plugins[i][phase] local conf = plugins[i + 1] diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua index a316ed036783..a4f1c36e322a 100644 --- a/apisix/utils/tracer.lua +++ b/apisix/utils/tracer.lua @@ -61,5 +61,17 @@ function _M.finish_current_span(code, message) sp:finish() end +function _M.finish_all_spans(code, message) + if not ngx.ctx._apisix_spans then + return + end + for _, sp in pairs(ngx.ctx._apisix_spans) do + if not sp:is_finished() then + sp:set_status(code, message) + end + sp:finish() + end +end + return _M diff --git a/t/plugin/opentelemetry6.t b/t/plugin/opentelemetry6.t index 78a56c54aaec..f05bd85c1069 100644 --- a/t/plugin/opentelemetry6.t +++ b/t/plugin/opentelemetry6.t @@ -224,11 +224,11 @@ qr/.*apisix.phase.access.*/ -=== TEST 9: check apisix.phase.header_filter span +=== TEST 9: check apisix.plugins.phase.header_filter span --- exec tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval -qr/.*apisix.phase.header_filter.*/ +qr/.*apisix.plugins.phase.header_filter.*/ From 05eda81a363619c29df78aec93f89f14ef5ce3b0 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 12:03:59 +0530 Subject: [PATCH 28/52] fix lint --- apisix/utils/tracer.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua index a4f1c36e322a..4217b37ecbfd 100644 --- a/apisix/utils/tracer.lua +++ b/apisix/utils/tracer.lua @@ -20,6 +20,7 @@ local span = require("apisix.utils.span") local span_kind = require("opentelemetry.trace.span_kind") local span_status = require("opentelemetry.trace.span_status") local table = table +local pairs = pairs local _M = { kind = span_kind, From 956935a13473d8be334ac0af5dc626ba9115455e Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 12:38:29 +0530 Subject: [PATCH 29/52] fix --- apisix/utils/tracer.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua index 4217b37ecbfd..94639f39546e 100644 --- a/apisix/utils/tracer.lua +++ b/apisix/utils/tracer.lua @@ -67,7 +67,7 @@ function _M.finish_all_spans(code, message) return end for _, sp in pairs(ngx.ctx._apisix_spans) do - if not sp:is_finished() then + if code then sp:set_status(code, message) end sp:finish() From d2bd7192769e61d89d1bfb2c634dfb8ebde3f943 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 13:12:48 +0530 Subject: [PATCH 30/52] f --- apisix/plugins/opentelemetry.lua | 30 +++--------------------------- t/plugin/opentelemetry6.t | 8 -------- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index e5850fdf6a4b..ff5f55007d65 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -427,44 +427,20 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) end -function _M.delayed_body_filter(conf, api_ctx) - if api_ctx.otel_context_token and ngx.arg[2] then - local ctx = context:current() - ctx:detach(api_ctx.otel_context_token) - api_ctx.otel_context_token = nil - - -- get span from current context - local span = ctx:span() - local upstream_status = core.response.get_upstream_status(api_ctx) - if upstream_status and upstream_status >= 500 then - span:set_status(span_status.ERROR, - "upstream response status: " .. upstream_status) - end - - span:set_attributes(attr.int("http.status_code", upstream_status)) - - inject_core_spans(ctx, api_ctx, conf) - - span:finish() - end -end - - --- body_filter maybe not called because of empty http body response --- so we need to check if the span has finished in log phase function _M.log(conf, api_ctx) if api_ctx.otel_context_token then -- ctx:detach() is not necessary, because of ctx is stored in ngx.ctx local upstream_status = core.response.get_upstream_status(api_ctx) -- get span from current context - local span = context:current():span() + local ctx = context:current() + local span = ctx:span() if upstream_status and upstream_status >= 500 then span:set_status(span_status.ERROR, "upstream response status: " .. upstream_status) end - inject_core_spans(span, api_ctx, conf) + inject_core_spans(ctx, api_ctx, conf) span:finish() end diff --git a/t/plugin/opentelemetry6.t b/t/plugin/opentelemetry6.t index f05bd85c1069..0b01f623a0f9 100644 --- a/t/plugin/opentelemetry6.t +++ b/t/plugin/opentelemetry6.t @@ -229,11 +229,3 @@ qr/.*apisix.phase.access.*/ tail -n 12 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*apisix.plugins.phase.header_filter.*/ - - - -=== TEST 10: check apisix.phase.delayed_body_filter.opentelemetry span ---- exec -tail ci/pod/otelcol-contrib/data-otlp.json ---- response_body eval -qr/.*apisix.phase.delayed_body_filter.opentelemetry.*/ From 119f9d3ac7188b65734523dd768a746e4c4cd43a Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 14:21:20 +0530 Subject: [PATCH 31/52] apply suggestions --- apisix/plugins/opentelemetry.lua | 18 ++++++++++++--- apisix/utils/span.lua | 38 +++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index ff5f55007d65..f111424f44f0 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -411,10 +411,16 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) if root_span_ctx.span and not root_span_ctx:span():is_recording() then return end - local conf = core.table.deepcopy(conf) - conf.sampler.name = "always_on" + local inject_conf = { + sampler = { + name = "always_on", + options = conf.sampler.options + }, + additional_attributes = conf.additional_attributes, + additional_header_prefix_attributes = conf.additional_header_prefix_attributes + } local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil, - create_tracer_obj, conf, plugin_info) + create_tracer_obj, inject_conf, plugin_info) if not tracer then core.log.error("failed to fetch tracer object: ", err) return @@ -443,6 +449,12 @@ function _M.log(conf, api_ctx) inject_core_spans(ctx, api_ctx, conf) span:finish() + if ngx.ctx._apisix_spans then + for _, sp in ipairs(ngx.ctx._apisix_spans) do + sp:release() + end + ngx.ctx._apisix_spans = nil + end end end diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index 45f10220cce6..e404d3fed208 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -14,11 +14,13 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- +local tablepool = require("tablepool") local util = require("opentelemetry.util") local span_status = require("opentelemetry.trace.span_status") local setmetatable = setmetatable local table = table local ipairs = ipairs +local pool_name = "opentelemetry_span" local _M = {} @@ -29,14 +31,14 @@ local mt = { function _M.new(name, kind) - local self = { - name = name, - start_time = util.time_nano(), - end_time = 0, - kind = kind, - attributes = {}, - children = {}, - } + local self = tablepool.fetch(pool_name, 0, 8) + self.name = name + self.start_time = util.time_nano() + self.end_time = 0 + self.kind = kind + self.attributes = self.attributes or {} + self.children = self.children or {} + self.status = nil return setmetatable(self, mt) end @@ -71,5 +73,25 @@ function _M.finish(self) self.end_time = util.time_nano() end +function _M.release(self) + self.name = nil + self.start_time = nil + self.end_time = nil + self.kind = nil + self.status = nil + if self.attributes then + for i = #self.attributes, 1, -1 do + self.attributes[i] = nil + end + end + + if self.children then + for i = #self.children, 1, -1 do + self.children[i] = nil + end + end + + tablepool.release(pool_name, self) +end return _M From 91e37be38bb779e42d57dfedd3e1d7470d69f4b8 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 17:45:58 +0530 Subject: [PATCH 32/52] fix test --- apisix/plugins/opentelemetry.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index f111424f44f0..0931ac0ac97e 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -447,7 +447,7 @@ function _M.log(conf, api_ctx) end inject_core_spans(ctx, api_ctx, conf) - + span:set_attributes(attr.int("http.status_code", upstream_status)) span:finish() if ngx.ctx._apisix_spans then for _, sp in ipairs(ngx.ctx._apisix_spans) do From fe16d9e719e2364a11265f4760d45de79a7719d2 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Mon, 27 Oct 2025 20:57:59 +0530 Subject: [PATCH 33/52] fix tests --- t/router/radixtree-sni2.t | 3 +++ t/stream-node/sanity.t | 3 +++ t/stream-node/tls.t | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/t/router/radixtree-sni2.t b/t/router/radixtree-sni2.t index 79d23df8e232..bbc36a733d3d 100644 --- a/t/router/radixtree-sni2.t +++ b/t/router/radixtree-sni2.t @@ -611,6 +611,9 @@ ssl handshake: true qr/(fetch|release) table \w+/ --- grep_error_log_out fetch table api_ctx +fetch table opentelemetry_span +fetch table opentelemetry_span +fetch table opentelemetry_span release table api_ctx diff --git a/t/stream-node/sanity.t b/t/stream-node/sanity.t index 556c4bc9983a..d9119046996a 100644 --- a/t/stream-node/sanity.t +++ b/t/stream-node/sanity.t @@ -394,6 +394,9 @@ hello world qr/(fetch|release) (ctx var|table \w+)/ --- grep_error_log_out fetch table api_ctx +fetch table opentelemetry_span +fetch table opentelemetry_span +fetch table opentelemetry_span fetch ctx var fetch table ctx_var fetch table plugins diff --git a/t/stream-node/tls.t b/t/stream-node/tls.t index 13bdcba0f1f5..2c0003ef5823 100644 --- a/t/stream-node/tls.t +++ b/t/stream-node/tls.t @@ -126,8 +126,12 @@ hello world qr/(fetch|release) table \w+/ --- grep_error_log_out fetch table api_ctx +fetch table opentelemetry_span +fetch table opentelemetry_span +fetch table opentelemetry_span release table api_ctx fetch table api_ctx +fetch table opentelemetry_span fetch table ctx_var fetch table plugins release table ctx_var From 14b4101020174d7f08b343cfab0c783ecb9d004d Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Tue, 28 Oct 2025 01:15:18 +0530 Subject: [PATCH 34/52] f --- t/stream-node/sanity.t | 2 -- 1 file changed, 2 deletions(-) diff --git a/t/stream-node/sanity.t b/t/stream-node/sanity.t index d9119046996a..c0fc0bd5b5b2 100644 --- a/t/stream-node/sanity.t +++ b/t/stream-node/sanity.t @@ -395,8 +395,6 @@ qr/(fetch|release) (ctx var|table \w+)/ --- grep_error_log_out fetch table api_ctx fetch table opentelemetry_span -fetch table opentelemetry_span -fetch table opentelemetry_span fetch ctx var fetch table ctx_var fetch table plugins From 079484d00473a62bb23c9487f8d5a871e099ed94 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Tue, 28 Oct 2025 12:15:09 +0530 Subject: [PATCH 35/52] apply suggestions --- apisix/utils/span.lua | 39 ++++++++++++++------------------------- apisix/utils/stack.lua | 5 ++--- apisix/utils/tracer.lua | 6 ++++-- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index e404d3fed208..c27234160dbe 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -19,7 +19,6 @@ local util = require("opentelemetry.util") local span_status = require("opentelemetry.trace.span_status") local setmetatable = setmetatable local table = table -local ipairs = ipairs local pool_name = "opentelemetry_span" local _M = {} @@ -50,20 +49,27 @@ end function _M.set_status(self, code, message) code = span_status.validate(code) - local status = { - code = code, - message = "" - } + local status = self.status + if not status then + status = { + code = code, + message = "" + } + self.status = status + else + status.code = code + end + if code == span_status.ERROR then status.message = message end - - self.status = status end function _M.set_attributes(self, ...) - for _, attr in ipairs({ ... }) do + local count = select('#', ...) + for i = 1, count do + local attr = select(i, ...) table.insert(self.attributes, attr) end end @@ -74,23 +80,6 @@ function _M.finish(self) end function _M.release(self) - self.name = nil - self.start_time = nil - self.end_time = nil - self.kind = nil - self.status = nil - if self.attributes then - for i = #self.attributes, 1, -1 do - self.attributes[i] = nil - end - end - - if self.children then - for i = #self.children, 1, -1 do - self.children[i] = nil - end - end - tablepool.release(pool_name, self) end diff --git a/apisix/utils/stack.lua b/apisix/utils/stack.lua index 030065ed7988..ad639d55337b 100644 --- a/apisix/utils/stack.lua +++ b/apisix/utils/stack.lua @@ -17,6 +17,7 @@ local _M = {} local mt = { __index = _M } local setmetatable = setmetatable +local table = require("apisix.core.table") function _M.new() local self = { @@ -65,9 +66,7 @@ end function _M.clear(self) - for i = 1, self._n do - self._data[i] = nil - end + table.clear(self._data) self._n = 0 end diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua index 94639f39546e..86e3ae0cec05 100644 --- a/apisix/utils/tracer.lua +++ b/apisix/utils/tracer.lua @@ -63,10 +63,12 @@ function _M.finish_current_span(code, message) end function _M.finish_all_spans(code, message) - if not ngx.ctx._apisix_spans then + local apisix_spans = ngx.ctx._apisix_spans + if not apisix_spans then return end - for _, sp in pairs(ngx.ctx._apisix_spans) do + + for _, sp in pairs(apisix_spans) do if code then sp:set_status(code, message) end From c310ade51995c8d354536ca18a40b877164da582 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Tue, 28 Oct 2025 12:34:59 +0530 Subject: [PATCH 36/52] fix lint --- apisix/utils/span.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index c27234160dbe..126c25116473 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -19,6 +19,7 @@ local util = require("opentelemetry.util") local span_status = require("opentelemetry.trace.span_status") local setmetatable = setmetatable local table = table +local select = select local pool_name = "opentelemetry_span" local _M = {} From 8ed32c517937612f9b04aa2708aa97bdf3797fa9 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 09:20:17 +0800 Subject: [PATCH 37/52] perf spans --- apisix/core/response.lua | 3 +- apisix/init.lua | 43 +- apisix/plugin.lua | 13 +- apisix/plugins/opentelemetry.lua | 37 +- apisix/ssl/router/radixtree_sni.lua | 9 +- apisix/tracer.lua | 120 +++++ apisix/utils/span.lua | 32 +- apisix/utils/stack.lua | 2 +- apisix/utils/upstream.lua | 8 +- conf/config-default.yaml | 764 ++++++++++++++++++++++++++++ t/plugin/opentelemetry6.t | 19 +- 11 files changed, 1006 insertions(+), 44 deletions(-) create mode 100644 apisix/tracer.lua create mode 100755 conf/config-default.yaml diff --git a/apisix/core/response.lua b/apisix/core/response.lua index d6199bbb2b28..25c9ba6b7a9f 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -31,6 +31,7 @@ if ngx.config.subsystem == "http" then ngx_add_header = ngx_resp.add_header end +local tracer = require("apisix.tracer") local error = error local select = select local type = type @@ -90,7 +91,7 @@ function resp_exit(code, ...) if code then if code >= 400 then - tracer.finish_all_spans(tracer.status.ERROR, message or ("response code " .. code)) + tracer.finish(ngx.ctx, tracer.status.ERROR, "response code " .. code) end return ngx_exit(code) end diff --git a/apisix/init.lua b/apisix/init.lua index 9fd5b2da90be..01510c687a23 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -46,8 +46,13 @@ local ctxdump = require("resty.ctxdump") local debug = require("apisix.debug") local pubsub_kafka = require("apisix.pubsub.kafka") local resource = require("apisix.resource") +<<<<<<< HEAD local trusted_addresses_util = require("apisix.utils.trusted-addresses") local tracer = require("apisix.utils.tracer") +======= +local tracer = require("apisix.tracer") + +>>>>>>> fece8f294 (perf spans) local ngx = ngx local get_method = ngx.req.get_method local ngx_exit = ngx.exit @@ -203,6 +208,9 @@ function _M.ssl_client_hello_phase() local ngx_ctx = ngx.ctx local api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx + api_ctx.ngx_ctx = ngx_ctx + + tracer.start(ngx_ctx, "ssl_client_hello_phase", tracer.kind.server) local span = tracer.new_span("ssl_client_hello_phase", tracer.kind.server) @@ -218,23 +226,35 @@ function _M.ssl_client_hello_phase() core.log.error("failed to fetch ssl config: ", err) end core.log.error("failed to match any SSL certificate by SNI: ", sni) +<<<<<<< HEAD span:set_status(tracer.status.ERROR, "failed match SNI") tracer.finish_current_span() +======= + tracer.finish(ngx_ctx, tracer.status.ERROR, "failed match SNI") +>>>>>>> fece8f294 (perf spans) ngx_exit(-1) end ok, err = apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols) if not ok then core.log.error("failed to set ssl protocols: ", err) +<<<<<<< HEAD span:set_status(tracer.status.ERROR, "failed set protocols") tracer.finish_current_span() +======= + tracer.finish(ngx_ctx, tracer.status.ERROR, "failed set protocols") +>>>>>>> fece8f294 (perf spans) ngx_exit(-1) end -- in stream subsystem, ngx.ssl.server_name() return hostname of ssl session in preread phase, -- so that we can't get real SNI without recording it in ngx.ctx during client_hello phase ngx.ctx.client_hello_sni = sni +<<<<<<< HEAD tracer.finish_current_span() +======= + tracer.finish(ngx_ctx) +>>>>>>> fece8f294 (perf spans) end @@ -488,7 +508,6 @@ local function common_phase(phase_name) end - function _M.handle_upstream(api_ctx, route, enable_websocket) -- some plugins(ai-proxy...) request upstream by http client directly if api_ctx.bypass_nginx_upstream then @@ -686,9 +705,12 @@ function _M.http_access_phase() -- always fetch table from the table pool, we don't need a reused api_ctx local api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx + api_ctx.ngx_ctx = ngx_ctx core.ctx.set_vars_meta(api_ctx) + tracer.start(ngx_ctx, "apisix.phase.access", tracer.kind.server) + if not verify_https_client(api_ctx) then return core.response.exit(400) end @@ -726,12 +748,20 @@ function _M.http_access_phase() handle_x_forwarded_headers(api_ctx) +<<<<<<< HEAD local router_match_span = tracer.new_span("http_router_match", tracer.kind.internal) +======= + tracer.start(ngx_ctx, "http_router_match", tracer.kind.internal) +>>>>>>> fece8f294 (perf spans) router.router_http.match(api_ctx) local route = api_ctx.matched_route if not route then +<<<<<<< HEAD tracer.new_span("run_global_rules", tracer.kind.internal) +======= + tracer.finish(ngx.ctx, tracer.status.ERROR, "no matched route") +>>>>>>> fece8f294 (perf spans) -- run global rule when there is no matching route local global_rules, conf_version = apisix_global_rules.global_rules() plugin.run_global_rules(api_ctx, global_rules, conf_version, nil) @@ -744,6 +774,7 @@ function _M.http_access_phase() return core.response.exit(404, {error_msg = "404 Route Not Found"}) end + tracer.finish(ngx_ctx) tracer.finish_current_span() @@ -837,6 +868,7 @@ function _M.http_access_phase() end plugin.run_plugin("access", plugins, api_ctx) end + tracer.finish(ngx_ctx) _M.handle_upstream(api_ctx, route, enable_websocket) @@ -895,6 +927,8 @@ end function _M.http_header_filter_phase() + local ngx_ctx = ngx.ctx + tracer.start(ngx_ctx, "apisix.phase.header_filter", tracer.kind.server) core.response.set_header("Server", ver_header) local up_status = get_var("upstream_status") @@ -917,6 +951,9 @@ function _M.http_header_filter_phase() end core.response.set_header("Apisix-Plugins", core.table.concat(deduplicate, ", ")) end + tracer.finish(ngx_ctx) + + tracer.start(ngx_ctx, "apisix.phase.body_filter", tracer.kind.server) end @@ -1050,6 +1087,7 @@ function _M.http_log_phase() if not api_ctx then return end + tracer.finish_all(api_ctx.ngx_ctx) if not api_ctx.var.apisix_upstream_response_time or api_ctx.var.apisix_upstream_response_time == "" then @@ -1075,6 +1113,9 @@ function _M.http_log_phase() core.tablepool.release("matched_route_record", api_ctx.curr_req_matched) end + tracer.release(api_ctx.ngx_ctx) + api_ctx.ngx_ctx = nil + core.tablepool.release("api_ctx", api_ctx) end diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 92ee496efa7b..380d86efda6f 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -38,7 +38,7 @@ local tostring = tostring local error = error local getmetatable = getmetatable local setmetatable = setmetatable -local tracer = require("apisix.utils.tracer") +local tracer = require("apisix.tracer") -- make linter happy to avoid error: getting the Lua global "load" -- luacheck: globals load, ignore lua_load local lua_load = load @@ -1221,7 +1221,7 @@ function _M.run_plugin(phase, plugins, api_ctx) end return api_ctx, plugin_run end - tracer.new_span("apisix.plugins.phase." .. phase) + for i = 1, #plugins, 2 do local phase_func = plugins[i][phase] local conf = plugins[i + 1] @@ -1229,13 +1229,14 @@ function _M.run_plugin(phase, plugins, api_ctx) plugin_run = true run_meta_pre_function(conf, api_ctx, plugins[i]["name"]) api_ctx._plugin_name = plugins[i]["name"] - tracer.new_span("apisix.phase." .. phase .. "." .. api_ctx._plugin_name) + tracer.start(api_ctx.ngx_ctx, "apisix.phase." .. phase + .. ".plugins." .. api_ctx._plugin_name) phase_func(conf, api_ctx) - tracer.finish_current_span() + tracer.finish(api_ctx.ngx_ctx) api_ctx._plugin_name = nil end end - tracer.finish_current_span() + return api_ctx, plugin_run end @@ -1304,6 +1305,7 @@ end function _M.run_global_rules(api_ctx, global_rules, conf_version, phase_name) if global_rules and #global_rules > 0 then + tracer.start(api_ctx.ngx_ctx, "run_global_rules", tracer.kind.internal) local orig_conf_type = api_ctx.conf_type local orig_conf_version = api_ctx.conf_version local orig_conf_id = api_ctx.conf_id @@ -1338,6 +1340,7 @@ function _M.run_global_rules(api_ctx, global_rules, conf_version, phase_name) api_ctx.conf_type = orig_conf_type api_ctx.conf_version = orig_conf_version api_ctx.conf_id = orig_conf_id + tracer.finish(api_ctx.ngx_ctx) end end diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 0931ac0ac97e..96cb168f045f 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -48,6 +48,7 @@ local pairs = pairs local ipairs = ipairs local unpack = unpack local string_format = string.format +local update_time = ngx.update_time local lrucache = core.lrucache.new({ type = 'plugin', count = 128, ttl = 24 * 60 * 60, @@ -376,8 +377,8 @@ function _M.rewrite(conf, api_ctx) ngx_var.opentelemetry_span_id = span_context.span_id end - if not ctx:span():is_recording() then - ngx.ctx._apisix_skip_tracing = true + if not ctx:span():is_recording() and ngx.ctx.tracing then + ngx.ctx.tracing.skip = true end api_ctx.otel_context_token = ctx:attach() @@ -387,7 +388,12 @@ function _M.rewrite(conf, api_ctx) end -local function create_child_span(tracer, parent_span_ctx, span) +local function create_child_span(tracer, parent_span_ctx, spans, span_idx) + local span = spans[span_idx] + if not span or span.finished then + return + end + span.finished = true local new_span_ctx, new_span = tracer:start(parent_span_ctx, span.name, { kind = span.kind, @@ -395,8 +401,8 @@ local function create_child_span(tracer, parent_span_ctx, span) }) new_span.start_time = span.start_time - for _, child in ipairs(span.children or {}) do - create_child_span(tracer, new_span_ctx, child) + for _, idx in ipairs(span.child_ids or {}) do + create_child_span(tracer, new_span_ctx, spans, idx) end if span.status then new_span:set_status(span.status.code, span.status.message) @@ -406,9 +412,16 @@ end local function inject_core_spans(root_span_ctx, api_ctx, conf) + local tracing = api_ctx.ngx_ctx.tracing + if not tracing then + return + end + + local span = root_span_ctx:span() + local metadata = plugin.plugin_metadata(plugin_name) local plugin_info = metadata.value - if root_span_ctx.span and not root_span_ctx:span():is_recording() then + if span and not span:is_recording() then return end local inject_conf = { @@ -425,10 +438,13 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) core.log.error("failed to fetch tracer object: ", err) return end - for _, sp in ipairs(ngx.ctx._apisix_spans or {}) do - if root_span_ctx.span_context then - create_child_span(tracer, root_span_ctx, sp) - end + + if #tracing.spans == 0 then + return + end + span.start_time = tracing.spans[1].start_time + for i, _ in ipairs(tracing.spans or {}) do + create_child_span(tracer, root_span_ctx, tracing.spans, i) end end @@ -448,6 +464,7 @@ function _M.log(conf, api_ctx) inject_core_spans(ctx, api_ctx, conf) span:set_attributes(attr.int("http.status_code", upstream_status)) + update_time() span:finish() if ngx.ctx._apisix_spans then for _, sp in ipairs(ngx.ctx._apisix_spans) do diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index 327a1a854c09..a1fd29736de4 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -21,7 +21,7 @@ local apisix_ssl = require("apisix.ssl") local secret = require("apisix.secret") local ngx_ssl = require("ngx.ssl") local config_util = require("apisix.core.config_util") -local tracer = require("apisix.utils.tracer") +local tracer = require("apisix.tracer") local ngx = ngx local ipairs = ipairs local type = type @@ -174,6 +174,7 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) core.log.debug("sni: ", sni) + tracer.start(api_ctx.ngx_ctx, "sni_radixtree_match", tracer.kind.internal) local sni_rev = sni:reverse() local span = tracer.new_span("sni_radixtree_match", tracer.kind.internal) local ok = radixtree_router:dispatch(sni_rev, nil, api_ctx) @@ -183,12 +184,10 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) -- with it sometimes core.log.error("failed to find any SSL certificate by SNI: ", sni) end - span:set_status(tracer.status.ERROR, "failed match SNI") - tracer.finish_current_span() + tracer.finish(api_ctx.ngx_ctx, tracer.status.ERROR, "failed match SNI") return false end - tracer.finish_current_span() - + tracer.finish(api_ctx.ngx_ctx) if api_ctx.matched_sni == "*" then -- wildcard matches everything, no need for further validation diff --git a/apisix/tracer.lua b/apisix/tracer.lua new file mode 100644 index 000000000000..459244f2195b --- /dev/null +++ b/apisix/tracer.lua @@ -0,0 +1,120 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local table = require("apisix.core.table") +local tablepool = require("tablepool") +local stack = require("apisix.utils.stack") +local span = require("apisix.utils.span") +local span_kind = require("opentelemetry.trace.span_kind") +local span_status = require("opentelemetry.trace.span_status") +local local_conf = require("apisix.core.config_local").local_conf() +local ipairs = ipairs +local ngx = ngx + +local enable_tracing = false +if ngx.config.subsystem == "http" and type(local_conf.apisix.tracing) == "boolean" then + enable_tracing = local_conf.apisix.tracing +end + +local _M = { + kind = span_kind, + status = span_status, + span_state = {}, +} + +function _M.start(ctx, name, kind) + if not enable_tracing then + return + end + + local tracing = ctx and ctx.tracing + if not tracing then + tracing = tablepool.fetch("tracing", 0, 8) + tracing.context = stack.new() + tracing.spans = tablepool.fetch("tracing_spans", 20, 0) + ctx.tracing = tracing + end + if tracing.skip then + return + end + + local spans = tracing.spans + local context = tracing.context + + table.insert(spans, span.new(name, kind)) + local idx = #spans + + if not context:is_empty() then + local parent_idx = context:peek() + local parent = spans[parent_idx] + parent:append_child(idx) + end + context:push(idx) +end + + +local function finish_span(tracing, code, message) + local sp_idx = tracing.context:pop() + if not sp_idx then + return + end + local sp = tracing.spans[sp_idx] + if code then + sp:set_status(code, message) + end + sp:finish() +end + + +function _M.finish(ctx, code, message) + local tracing = ctx and ctx.tracing + if not tracing then + return + end + finish_span(tracing, code, message) +end + + +function _M.finish_all(ctx, code, message) + local tracing = ctx and ctx.tracing + if not tracing then + return + end + while not tracing.context:is_empty() do + finish_span(tracing, code, message) + end +end + + +function _M.release(ctx) + local tracing = ctx and ctx.tracing + if not tracing then + return + end + + while not tracing.context:is_empty() do + finish_span(tracing) + end + + for _, sp in ipairs(tracing.spans) do + sp:release() + end + tablepool.release("tracing_spans", tracing.spans) + tablepool.release("tracing", tracing) +end + + +return _M diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index 126c25116473..1b3f2ef675ac 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -21,6 +21,7 @@ local setmetatable = setmetatable local table = table local select = select local pool_name = "opentelemetry_span" +local update_time = ngx.update_time local _M = {} @@ -29,22 +30,34 @@ local mt = { __index = _M } +local function get_time() + update_time() + return util.time_nano() +end + function _M.new(name, kind) - local self = tablepool.fetch(pool_name, 0, 8) + local self = tablepool.fetch(pool_name, 0, 16) + self.start_time = get_time() self.name = name - self.start_time = util.time_nano() - self.end_time = 0 self.kind = kind - self.attributes = self.attributes or {} - self.children = self.children or {} + self.end_time = 0 self.status = nil + self.dead = false return setmetatable(self, mt) end -function _M.append_child(self, span) - table.insert(self.children, span) +function _M.append_child(self, child_id) + if not self.child_ids then + self.child_ids = {} + end + table.insert(self.child_ids, child_id) +end + + +function _M.release(self) + tablepool.release(pool_name, self) end @@ -77,11 +90,8 @@ end function _M.finish(self) - self.end_time = util.time_nano() + self.end_time = get_time() end -function _M.release(self) - tablepool.release(pool_name, self) -end return _M diff --git a/apisix/utils/stack.lua b/apisix/utils/stack.lua index ad639d55337b..af4e6a066831 100644 --- a/apisix/utils/stack.lua +++ b/apisix/utils/stack.lua @@ -19,6 +19,7 @@ local mt = { __index = _M } local setmetatable = setmetatable local table = require("apisix.core.table") + function _M.new() local self = { _data = {}, @@ -72,4 +73,3 @@ end return _M - diff --git a/apisix/utils/upstream.lua b/apisix/utils/upstream.lua index a55eaae63e49..498f74779940 100644 --- a/apisix/utils/upstream.lua +++ b/apisix/utils/upstream.lua @@ -20,7 +20,9 @@ local ngx_now = ngx.now local ipairs = ipairs local type = type local tostring = tostring -local tracer = require("apisix.utils.tracer") +local resource = require("apisix.resource") +local tracer = require("apisix.tracer") +local ngx = ngx local _M = {} @@ -80,7 +82,7 @@ _M.compare_upstream_node = compare_upstream_node local function parse_domain_for_nodes(nodes) - tracer.new_span("resolve_dns", tracer.kind.internal) + tracer.start(ngx.ctx, "resolve_dns", tracer.kind.internal) local new_nodes = core.table.new(#nodes, 0) for _, node in ipairs(nodes) do local host = node.host @@ -101,7 +103,7 @@ local function parse_domain_for_nodes(nodes) core.table.insert(new_nodes, node) end end - tracer.finish_current_span() + tracer.finish(ngx.ctx) return new_nodes end _M.parse_domain_for_nodes = parse_domain_for_nodes diff --git a/conf/config-default.yaml b/conf/config-default.yaml new file mode 100755 index 000000000000..05538f23ea03 --- /dev/null +++ b/conf/config-default.yaml @@ -0,0 +1,764 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# PLEASE DO NOT UPDATE THIS FILE! +# If you want to set the specified configuration value, you can set the new +# value in the conf/config.yaml file. +# + +apisix: + # node_listen: 9080 # APISIX listening port + node_listen: # This style support multiple ports + - 9080 + # - port: 9081 + # enable_http2: true # If not set, the default value is `false`. + # - ip: 127.0.0.2 # Specific IP, If not set, the default value is `0.0.0.0`. + # port: 9082 + # enable_http2: true + # - port: 9082 + # backlog: 1024 # sets the backlog parameter in the listen() call that limits + # the maximum length for the queue of pending connections. + # By default, backlog is set to -1 on FreeBSD, DragonFly BSD, and macOS, + # and to 511 on other platforms. + enable_admin: true + enable_dev_mode: false # Sets nginx worker_processes to 1 if set to true + enable_reuseport: true # Enable nginx SO_REUSEPORT switch if set to true. + show_upstream_status_in_response_header: false # when true all upstream status write to `X-APISIX-Upstream-Status` otherwise only 5xx code + enable_ipv6: true + + #proxy_protocol: # Proxy Protocol configuration + # listen_http_port: 9181 # The port with proxy protocol for http, it differs from node_listen and admin_listen. + # This port can only receive http request with proxy protocol, but node_listen & admin_listen + # can only receive http request. If you enable proxy protocol, you must use this port to + # receive http request with proxy protocol + # listen_https_port: 9182 # The port with proxy protocol for https + # enable_tcp_pp: true # Enable the proxy protocol for tcp proxy, it works for stream_proxy.tcp option + # enable_tcp_pp_to_upstream: true # Enables the proxy protocol to the upstream server + enable_server_tokens: true # Whether the APISIX version number should be shown in Server header. + # It's enabled by default. + + # configurations to load third party code and/or override the builtin one. + extra_lua_path: "" # extend lua_package_path to load third party code + extra_lua_cpath: "" # extend lua_package_cpath to load third party code + lua_module_hook: "agent.hook" # the hook module which will be used to inject third party code into APISIX + + proxy_cache: # Proxy Caching configuration + cache_ttl: 10s # The default caching time in disk if the upstream does not specify the cache time + zones: # The parameters of a cache + - name: disk_cache_one # The name of the cache, administrator can specify + # which cache to use by name in the admin api (disk|memory) + memory_size: 50m # The size of shared memory, it's used to store the cache index for + # disk strategy, store cache content for memory strategy (disk|memory) + disk_size: 1G # The size of disk, it's used to store the cache data (disk) + disk_path: /tmp/disk_cache_one # The path to store the cache data (disk) + cache_levels: "1:2" # The hierarchy levels of a cache (disk) + #- name: disk_cache_two + # memory_size: 50m + # disk_size: 1G + # disk_path: "/tmp/disk_cache_two" + # cache_levels: "1:2" + - name: memory_cache + memory_size: 50m + + delete_uri_tail_slash: false # delete the '/' at the end of the URI + # The URI normalization in servlet is a little different from the RFC's. + # See https://github.com/jakartaee/servlet/blob/master/spec/src/main/asciidoc/servlet-spec-body.adoc#352-uri-path-canonicalization, + # which is used under Tomcat. + # Turn this option on if you want to be compatible with servlet when matching URI path. + normalize_uri_like_servlet: false + router: + http: radixtree_uri # radixtree_uri: match route by uri(base on radixtree) + # radixtree_host_uri: match route by host + uri(base on radixtree) + # radixtree_uri_with_parameter: like radixtree_uri but match uri with parameters, + # see https://github.com/api7/lua-resty-radixtree/#parameters-in-path for + # more details. + ssl: radixtree_sni # radixtree_sni: match route by SNI(base on radixtree) + #stream_proxy: # TCP/UDP proxy + # only: true # use stream proxy only, don't enable HTTP stuff + # tcp: # TCP proxy port list + # - addr: 9100 + # tls: true + # - addr: "127.0.0.1:9101" + # udp: # UDP proxy port list + # - 9200 + # - "127.0.0.1:9201" + #dns_resolver: # If not set, read from `/etc/resolv.conf` + # - 1.1.1.1 + # - 8.8.8.8 + #dns_resolver_valid: 30 # if given, override the TTL of the valid records. The unit is second. + resolver_timeout: 5 # resolver timeout + enable_resolv_search_opt: true # enable search option in resolv.conf + ssl: + enable: true + listen: # APISIX listening port in https. + - port: 9443 + enable_http2: true + # - ip: 127.0.0.3 # Specific IP, If not set, the default value is `0.0.0.0`. + # port: 9445 + # enable_http2: true + # - port: 9446 + # backlog: 1024 # sets the backlog parameter in the listen() call that limits + # the maximum length for the queue of pending connections. + # By default, backlog is set to -1 on FreeBSD, DragonFly BSD, and macOS, + # and to 511 on other platforms. + # ssl_trusted_combined_path: /usr/local/apisix/conf/cert/.ssl_trusted_combined.pem # All the trusted certificates will be combined into a single file + ssl_trusted_certificate: system # Specifies comma separated list of trusted CA. Value can be either "system"(for using system available ca certs) or + # a file path with trusted CA certificates in the PEM format + # used to verify the certificate when APISIX needs to do SSL/TLS handshaking + # with external services (e.g. etcd) + ssl_protocols: TLSv1.2 TLSv1.3 + ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl_session_tickets: false # disable ssl_session_tickets by default for 'ssl_session_tickets' would make Perfect Forward Secrecy useless. + # ref: https://github.com/mozilla/server-side-tls/issues/135 + + key_encrypt_salt: # If not set, will save origin ssl key into etcd. + - edd1c9f0985e76a2 # If set this, the key_encrypt_salt should be an array whose elements are string, and the size is also 16, and it will encrypt ssl key with AES-128-CBC + # !!! So do not change it after saving your ssl, it can't decrypt the ssl keys have be saved if you change !! + # Only use the first key to encrypt, and decrypt in the order of the array. + + #fallback_sni: "my.default.domain" # If set this, when the client doesn't send SNI during handshake, the fallback SNI will be used instead + enable_control: true + #control: + # ip: 127.0.0.1 + # port: 9090 + disable_sync_configuration_during_start: false # safe exit. Remove this once the feature is stable + + # This time will be used to distinguish whether the worker is started first time or restarted due to a crash, unit: second. + worker_startup_time_threshold: 60 + + data_encryption: # add `encrypt_fields = { $field },` in plugin schema to enable encryption + enable: false # if not set, the default value is `false`. + keyring: + - qeddd145sfvddff3 # If not set, will save origin value into etcd. + # If set this, the keyring should be an array whose elements are string, and the size is also 16, and it will encrypt fields with AES-128-CBC + # !!! So do not change it after encryption, it can't decrypt the fields have be saved if you change !! + # Only use the first key to encrypt, and decrypt in the order of the array. + + # status: # When enabled, APISIX will provide `/status` and `/status/ready` endpoints + # ip: 127.0.0.1 # /status endpoint will return 200 status code if APISIX has successfully started and running correctly + # port: 7085 # /status/ready endpoint will return 503 status code if none of the configured etcd (dp_manager) are available + + # disable_upstream_healthcheck: false # A global switch for healthcheck. Defaults to false. + # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. + + # fine tune the parameters of LRU cache for some features like secret + lru: + secret: + ttl: 300 # seconds + count: 512 + neg_ttl: 60 + neg_count: 512 + + # enable more detailed tracing information + tracing: false + +nginx_config: # config for render the template to generate nginx.conf + #user: root # specifies the execution user of the worker process. + # the "user" directive makes sense only if the master process runs with super-user privileges. + # if you're not root user,the default is current user. + error_log: logs/error.log + error_log_level: warn # warn,error + worker_processes: auto # if you want use multiple cores in container, you can inject the number of cpu as environment variable "APISIX_WORKER_PROCESSES" + enable_cpu_affinity: false # disable CPU affinity by default, if APISIX is deployed on a physical machine, it can be enabled and work well. + worker_rlimit_nofile: 20480 # the number of files a worker process can open, should be larger than worker_connections + worker_shutdown_timeout: 240s # timeout for a graceful shutdown of worker processes + + max_pending_timers: 16384 # increase it if you see "too many pending timers" error + max_running_timers: 4096 # increase it if you see "lua_max_running_timers are not enough" error + + event: + worker_connections: 10620 + #envs: # allow to get a list of environment variables + # - TEST_ENV + + meta: + lua_shared_dict: + prometheus-metrics: 15m + + stream: + enable_access_log: false # enable access log or not, default false + access_log: logs/access_stream.log + access_log_format: "$remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time" + # create your custom log format by visiting http://nginx.org/en/docs/varindex.html + access_log_format_escape: default # allows setting json or default characters escaping in variables + lua_shared_dict: + etcd-cluster-health-check-stream: 10m + lrucache-lock-stream: 10m + plugin-limit-conn-stream: 10m + worker-events-stream: 10m + tars-stream: 1m + config-stream: 5m + + # As user can add arbitrary configurations in the snippet, + # it is user's responsibility to check the configurations + # don't conflict with APISIX. + main_configuration_snippet: | + # Add custom Nginx main configuration to nginx.conf. + # The configuration should be well indented! + http_configuration_snippet: | + # Add custom Nginx http configuration to nginx.conf. + # The configuration should be well indented! + http_server_configuration_snippet: | + # Add custom Nginx http server configuration to nginx.conf. + # The configuration should be well indented! + http_server_location_configuration_snippet: | + # Add custom Nginx http server location configuration to nginx.conf. + # The configuration should be well indented! + http_admin_configuration_snippet: | + # Add custom Nginx admin server configuration to nginx.conf. + # The configuration should be well indented! + http_end_configuration_snippet: | + # Add custom Nginx http end configuration to nginx.conf. + # The configuration should be well indented! + stream_configuration_snippet: | + # Add custom Nginx stream configuration to nginx.conf. + # The configuration should be well indented! + + http: + enable_access_log: true # enable access log or not, default true + access_log: logs/access.log + # available variables: + # request_type: traditional_http / ai_chat / ai_stream + # llm_time_to_first_token: duration from the start send request to ai server to the first token received + # llm_prompt_tokens: number of tokens in the prompt + # llm_completion_tokens: number of tokens in the chat completion + access_log_format: "$remote_addr - $remote_user [$time_local] $http_host \"$request_line\" $status $body_bytes_sent $request_time \"$http_referer\" \"$http_user_agent\" $upstream_addr $upstream_status $upstream_response_time \"$upstream_scheme://$upstream_host$upstream_uri\" \"$apisix_request_id\"" + access_log_format_escape: default # allows setting json or default characters escaping in variables + keepalive_timeout: 60s # timeout during which a keep-alive client connection will stay open on the server side. + client_header_timeout: 60s # timeout for reading client request header, then 408 (Request Time-out) error is returned to the client + client_body_timeout: 60s # timeout for reading client request body, then 408 (Request Time-out) error is returned to the client + client_max_body_size: 0 # The maximum allowed size of the client request body. + # If exceeded, the 413 (Request Entity Too Large) error is returned to the client. + # Note that unlike Nginx, we don't limit the body size by default. + + send_timeout: 10s # timeout for transmitting a response to the client.then the connection is closed + underscores_in_headers: "on" # default enables the use of underscores in client request header fields + real_ip_header: X-Real-IP # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header + real_ip_recursive: "off" # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive + real_ip_from: # http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from + - 127.0.0.1 + - "unix:" + custom_lua_shared_dict: # add custom shared cache to nginx.conf + # ipc_shared_dict: 100m # custom shared cache, format: `cache-key: cache-size` + config: 5m + kubernetes: 20m + nacos: 20m + + # Enables or disables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) + # when establishing a connection with the proxied HTTPS server. + proxy_ssl_server_name: true + upstream: + keepalive: 320 # Sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. + # When this number is exceeded, the least recently used connections are closed. + keepalive_requests: 1000 # Sets the maximum number of requests that can be served through one keepalive connection. + # After the maximum number of requests is made, the connection is closed. + keepalive_timeout: 60s # Sets a timeout during which an idle keepalive connection to an upstream server will stay open. + charset: utf-8 # Adds the specified charset to the "Content-Type" response header field, see + # http://nginx.org/en/docs/http/ngx_http_charset_module.html#charset + variables_hash_max_size: 2048 # Sets the maximum size of the variables hash table. + + lua_shared_dict: + internal-status: 10m + plugin-limit-req: 10m + plugin-limit-count: 10m + plugin-limit-conn: 10m + plugin-limit-conn-redis-cluster-slot-lock: 1m + plugin-graphql-limit-count: 10m + plugin-graphql-limit-count-reset-header: 10m + plugin-ai-rate-limiting: 10m + plugin-ai-rate-limiting-reset-header: 10m + upstream-healthcheck: 10m + worker-events: 10m + lrucache-lock: 10m + balancer-ewma: 10m + balancer-ewma-locks: 10m + balancer-ewma-last-touched-at: 10m + plugin-limit-req-redis-cluster-slot-lock: 1m + plugin-limit-count-redis-cluster-slot-lock: 1m + plugin-limit-count-advanced: 10m + plugin-limit-count-advanced-redis-cluster-slot-lock: 1m + tracing_buffer: 10m + plugin-api-breaker: 10m + etcd-cluster-health-check: 10m + discovery: 1m + jwks: 1m + introspection: 10m + access-tokens: 1m + ext-plugin: 1m + tars: 1m + cas-auth: 10m + status_report: 1m + +#discovery: # service discovery center +# dns: +# servers: +# - "127.0.0.1:8600" # use the real address of your dns server +# order: # order in which to try different dns record types when resolving +# - last # "last" will try the last previously successful type for a hostname. +# - SRV +# - A +# - AAAA +# - CNAME +# eureka: +# host: # it's possible to define multiple eureka hosts addresses of the same eureka cluster. +# - "http://127.0.0.1:8761" +# prefix: /eureka/ +# fetch_interval: 30 # default 30s +# weight: 100 # default weight for node +# timeout: +# connect: 2000 # default 2000ms +# send: 2000 # default 2000ms +# read: 5000 # default 5000ms +# nacos: +# host: +# - "http://${username}:${password}@${host1}:${port1}" +# prefix: "/nacos/v1/" +# fetch_interval: 30 # default 30 sec +# weight: 100 # default 100 +# timeout: +# connect: 2000 # default 2000 ms +# send: 2000 # default 2000 ms +# read: 5000 # default 5000 ms +# consul_kv: +# servers: +# - "http://127.0.0.1:8500" +# - "http://127.0.0.1:8600" +# prefix: "upstreams" +# skip_keys: # if you need to skip special keys +# - "upstreams/unused_api/" +# timeout: +# connect: 2000 # default 2000 ms +# read: 2000 # default 2000 ms +# wait: 60 # default 60 sec +# weight: 1 # default 1 +# fetch_interval: 3 # default 3 sec, only take effect for keepalive: false way +# keepalive: true # default true, use the long pull way to query consul servers +# sort_type: "origin" # default origin +# default_server: # you can define default server when missing hit +# host: "127.0.0.1" +# port: 20999 +# metadata: +# fail_timeout: 1 # default 1 ms +# weight: 1 # default 1 +# max_fails: 1 # default 1 +# dump: # if you need, when registered nodes updated can dump into file +# path: "logs/consul_kv.dump" +# expire: 2592000 # unit sec, here is 30 day +# consul: +# servers: # make sure service name is unique in these consul servers +# - "http://127.0.0.1:8500" # `http://127.0.0.1:8500` and `http://127.0.0.1:8600` are different clusters +# - "http://127.0.0.1:8600" +# skip_services: # if you need to skip special services +# - "service_a" # `consul` service is default skip service +# timeout: +# connect: 2000 # default 2000 ms +# read: 2000 # default 2000 ms +# wait: 60 # default 60 sec +# weight: 1 # default 1 +# fetch_interval: 3 # default 3 sec, only take effect for keepalive: false way +# keepalive: true # default true, use the long pull way to query consul servers +# default_service: # you can define default server when missing hit +# host: "127.0.0.1" +# port: 20999 +# metadata: +# fail_timeout: 1 # default 1 ms +# weight: 1 # default 1 +# max_fails: 1 # default 1 +# dump: # if you need, when registered nodes updated can dump into file +# path: "logs/consul.dump" +# expire: 2592000 # unit sec, here is 30 day +# load_on_init: true # default true, load the consul dump file on init +# kubernetes: +# ### kubernetes service discovery both support single-cluster and multi-cluster mode +# ### applicable to the case where the service is distributed in a single or multiple kubernetes clusters. +# +# ### single-cluster mode ### +# service: +# schema: https #apiserver schema, options [http, https], default https +# host: ${KUBERNETES_SERVICE_HOST} #apiserver host, options [ipv4, ipv6, domain, environment variable], default ${KUBERNETES_SERVICE_HOST} +# port: ${KUBERNETES_SERVICE_PORT} #apiserver port, options [port number, environment variable], default ${KUBERNETES_SERVICE_PORT} +# client: +# # serviceaccount token or path of serviceaccount token_file +# token_file: ${KUBERNETES_CLIENT_TOKEN_FILE} +# # token: |- +# # eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEif +# # 6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEifeyJhbGciOiJSUzI1NiIsImtpZCI +# # kubernetes discovery plugin support use namespace_selector +# # you can use one of [equal, not_equal, match, not_match] filter namespace +# namespace_selector: +# # only save endpoints with namespace equal default +# equal: default +# # only save endpoints with namespace not equal default +# #not_equal: default +# # only save endpoints with namespace match one of [default, ^my-[a-z]+$] +# #match: +# #- default +# #- ^my-[a-z]+$ +# # only save endpoints with namespace not match one of [default, ^my-[a-z]+$ ] +# #not_match: +# #- default +# #- ^my-[a-z]+$ +# # kubernetes discovery plugin support use label_selector +# # for the expression of label_selector, please refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/labels +# label_selector: |- +# first="a",second="b" +# # reserved lua shared memory size,1m memory can store about 1000 pieces of endpoint +# shared_size: 1m #default 1m +# ### single-cluster mode ### +# +# ### multi-cluster mode ### +# - id: release # a custom name refer to the cluster, pattern ^[a-z0-9]{1,8} +# service: +# schema: https #apiserver schema, options [http, https], default https +# host: ${KUBERNETES_SERVICE_HOST} #apiserver host, options [ipv4, ipv6, domain, environment variable] +# port: ${KUBERNETES_SERVICE_PORT} #apiserver port, options [port number, environment variable] +# client: +# # serviceaccount token or path of serviceaccount token_file +# token_file: ${KUBERNETES_CLIENT_TOKEN_FILE} +# # token: |- +# # eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEif +# # 6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEifeyJhbGciOiJSUzI1NiIsImtpZCI +# # kubernetes discovery plugin support use namespace_selector +# # you can use one of [equal, not_equal, match, not_match] filter namespace +# namespace_selector: +# # only save endpoints with namespace equal default +# equal: default +# # only save endpoints with namespace not equal default +# #not_equal: default +# # only save endpoints with namespace match one of [default, ^my-[a-z]+$] +# #match: +# #- default +# #- ^my-[a-z]+$ +# # only save endpoints with namespace not match one of [default, ^my-[a-z]+$ ] +# #not_match: +# #- default +# #- ^my-[a-z]+$ +# # kubernetes discovery plugin support use label_selector +# # for the expression of label_selector, please refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/labels +# label_selector: |- +# first="a",second="b" +# # reserved lua shared memory size,1m memory can store about 1000 pieces of endpoint +# shared_size: 1m #default 1m +# ### multi-cluster mode ### + +graphql: + max_size: 1048576 # the maximum size limitation of graphql in bytes, default 1MiB + +#ext-plugin: + #cmd: ["ls", "-l"] + +plugins: # plugin list (sorted by priority) +# - exit-transformer # priority: 9999999 + - real-ip # priority: 23000 + # - toolset # priority: 22901 + - ai # priority: 22900 + - client-control # priority: 22000 + - proxy-buffering # priority: 21991 + - proxy-control # priority: 21990 + - request-id # priority: 12015 + - zipkin # priority: 12011 + #- skywalking # priority: 12010 + #- opentelemetry # priority: 12009 + - ext-plugin-pre-req # priority: 12000 + - fault-injection # priority: 11000 + - mocking # priority: 10900 + - serverless-pre-function # priority: 10000 + #- batch-requests # priority: 4010 + - cors # priority: 4000 + - ip-restriction # priority: 3000 + - ua-restriction # priority: 2999 + - referer-restriction # priority: 2990 + - csrf # priority: 2980 + - uri-blocker # priority: 2900 + - request-validation # priority: 2800 + - portal-auth # priority: 2750 + - chaitin-waf # priority: 2700 + - multi-auth # priority: 2600 + - openid-connect # priority: 2599 + - saml-auth # priority: 2598 + - cas-auth # priority: 2597 + - authz-casbin # priority: 2560 + - authz-casdoor # priority: 2559 + - wolf-rbac # priority: 2555 + - ldap-auth # priority: 2540 + - hmac-auth # priority: 2530 + - basic-auth # priority: 2520 + - jwt-auth # priority: 2510 + - key-auth # priority: 2500 + - acl # priority: 2410 + - consumer-restriction # priority: 2400 + - attach-consumer-label # priority: 2399 + - forward-auth # priority: 2002 + - opa # priority: 2001 + - authz-keycloak # priority: 2000 + - data-mask # priority: 1500 + #- error-log-logger # priority: 1091 + - proxy-cache # priority: 1085 + - body-transformer # priority: 1080 + - ai-request-rewrite # priority: 1073 + - ai-prompt-template # priority: 1071 + - ai-prompt-decorator # priority: 1070 + - ai-prompt-guard # priority: 1072 + - ai-rag # priority: 1060 + - ai-aws-content-moderation # priority: 1050 + - ai-proxy-multi # priority: 1041 + - ai-proxy # priority: 1040 + - ai-rate-limiting # priority: 1030 + - ai-aliyun-content-moderation # priority: 1029 + - proxy-mirror # priority: 1010 + - graphql-proxy-cache # priority: 1009 + - proxy-rewrite # priority: 1008 + - workflow # priority: 1006 + - api-breaker # priority: 1005 + - graphql-limit-count # priority: 1004 + - limit-conn # priority: 1003 + - limit-count # priority: 1002 + - limit-count-advanced # priority: 1001 + - limit-req # priority: 999 + #- node-status # priority: 998 + - traffic-label # priority: 995 + - gzip # priority: 994 + - server-info # priority: 990 + - api7-traffic-split # priority: 966 + - traffic-split # priority: 966 + - redirect # priority: 900 + - response-rewrite # priority: 899 + - soap # priority: 554 + - openapi-to-mcp # priority: 540 + - oas-validator # priority: 510 + - degraphql # priority: 509 + - kafka-proxy # priority: 508 + #- dubbo-proxy # priority: 507 + - grpc-transcode # priority: 506 + - grpc-web # priority: 505 + - public-api # priority: 501 + - prometheus # priority: 500 + - datadog # priority: 495 + - lago # priority: 415 + - error-page # priority: 450 + - loki-logger # priority: 414 + - elasticsearch-logger # priority: 413 + - echo # priority: 412 + - loggly # priority: 411 + - http-logger # priority: 410 + - splunk-hec-logging # priority: 409 + - skywalking-logger # priority: 408 + - google-cloud-logging # priority: 407 + - sls-logger # priority: 406 + - tcp-logger # priority: 405 + - kafka-logger # priority: 403 + - rocketmq-logger # priority: 402 + - syslog # priority: 401 + - udp-logger # priority: 400 + - file-logger # priority: 399 + - clickhouse-logger # priority: 398 + - tencent-cloud-cls # priority: 397 + #- log-rotate # priority: 100 + # <- recommend to use priority (0, 100) for your custom plugins + - example-plugin # priority: 0 + #- gm # priority: -43 + - aws-lambda # priority: -1899 + - azure-functions # priority: -1900 + - openwhisk # priority: -1901 + - openfunction # priority: -1902 + - serverless-post-function # priority: -2000 + - ext-plugin-post-req # priority: -3000 + - ext-plugin-post-resp # priority: -4000 + +stream_plugins: # sorted by priority + - ip-restriction # priority: 3000 + - limit-conn # priority: 1003 + - mqtt-proxy # priority: 1000 + - traffic-split # priority: 966 + #- prometheus # priority: 500 + - syslog # priority: 401 + # <- recommend to use priority (0, 100) for your custom plugins + +#wasm: + #plugins: + #- name: wasm_log + #priority: 7999 + #file: t/wasm/log/main.go.wasm + +#xrpc: + #protocols: + #- name: pingpong + +plugin_attr: + log-rotate: + enable: false + timeout: 10000 # maximum wait time for a log rotation(unit: millisecond) interval: 3600 # rotate interval (unit: second) + max_kept: 168 # max number of log files will be kept + max_size: -1 # max size bytes of log files to be rotated, size check would be skipped with a value less than 0 + enable_compression: false # enable log file compression(gzip) or not, default false + skywalking: + service_name: APISIX + service_instance_name: APISIX Instance Name + endpoint_addr: http://127.0.0.1:12800 + soap: + endpoint: http://127.0.0.1:5000 + timeout: 3000 + prometheus: + export_uri: /apisix/prometheus/metrics + metric_prefix: apisix_ + enable_export_server: true + export_addr: + ip: 127.0.0.1 + port: 9091 + fetch_metric_timeout: 5 + allow_degradation: false + degradation_pause_steps: [ 60 ] + #metrics: + # http_status: + # # extra labels from nginx variables + # extra_labels: + # # the label name doesn't need to be the same as variable name + # # below labels are only examples, you could add any valid variables as you need + # - upstream_addr: $upstream_addr + # - upstream_status: $upstream_status + # expire: 0 # The expiration time after which metrics are removed. unit: second. + # # 0 means the metrics will not expire + # http_latency: + # extra_labels: + # - upstream_addr: $upstream_addr + # expire: 0 # The expiration time after which metrics are removed. unit: second. + # # 0 means the metrics will not expire + # bandwidth: + # extra_labels: + # - upstream_addr: $upstream_addr + # expire: 0 # The expiration time after which metrics are removed. unit: second. + # # 0 means the metrics will not expire + # default_buckets: + # - 10 + # - 50 + # - 100 + # - 200 + # - 500 + server-info: + report_ttl: 60 # live time for server info in etcd (unit: second) + dubbo-proxy: + upstream_multiplex_count: 32 + proxy-mirror: + timeout: # proxy timeout in mirrored sub-request + connect: 60s + read: 60s + send: 60s +# redirect: +# https_port: 8443 # the default port for use by HTTP redirects to HTTPS + inspect: + delay: 3 # in seconds + hooks_file: "/usr/local/apisix/plugin_inspect_hooks.lua" + zipkin: # Plugin: zipkin + set_ngx_var: false # export zipkin variables to nginx variables + +deployment: + role: traditional + role_traditional: + config_provider: etcd + admin: + # Default token when use API to call for Admin API. + # *NOTE*: Highly recommended to modify this value to protect APISIX's Admin API. + # Disabling this configuration item means that the Admin API does not + # require any authentication. + admin_key: + - + name: admin + key: edd1c9f034335f136f87ad84b625c8f1 + role: admin # admin: manage all configuration data + # viewer: only can view configuration data + - + name: viewer + key: 4054f7cf07e344346cd3f287985e76a2 + role: viewer + + enable_admin_cors: true # Admin API support CORS response headers. + allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow + - 127.0.0.0/24 # If we don't set any IP list, then any IP access is allowed by default. + #- "::/64" + admin_listen: # use a separate port + ip: 0.0.0.0 # Specific IP, if not set, the default value is `0.0.0.0`. + port: 9180 # Specific port, which must be different from node_listen's port. + + #https_admin: true # enable HTTPS when use a separate port for Admin API. + # Admin API will use conf/apisix_admin_api.crt and conf/apisix_admin_api.key as certificate. + + admin_api_mtls: # Depends on `admin_listen` and `https_admin`. + admin_ssl_cert: "" # Path of your self-signed server side cert. + admin_ssl_cert_key: "" # Path of your self-signed server side key. + admin_ssl_ca_cert: "" # Path of your self-signed ca cert.The CA is used to sign all admin api callers' certificates. + + admin_api_version: v3 # The version of admin api, latest version is v3. + + # fallback_cp: + # mode: "write" # "write" specifies that the dp instance will run on backup mode and not accept/proxy any requests. defaults to "write", which specifies "fallback" mode + # interval: 1 # interval in seconds for backup mode + # aws_s3: # configuration for AWS S3 to store configuration/resources either to push (in backup mode) or pull (in fallback mode) + # access_key: "just-access" + # secret_key: "super-secret" + # resource_bucket: "resource_bucket" + # config_bucket: "config_bucket" + # region: "ap-south-1" + # endpoint: "http://localhost:6969" + # azure_blob: # configuration for Azure Blob Storage to store configuration/resources either to push (in backup mode) or pull (in fallback mode) + # account_name: devstoreaccount1 + # account_key: "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" + # resource_container: yaml + # config_container: config + # endpoint: "http://localhost:10000/devstoreaccount1" + + etcd: + host: # it's possible to define multiple etcd hosts addresses of the same etcd cluster. + - "http://127.0.0.1:2379" # multiple etcd address, if your etcd cluster enables TLS, please use https scheme, + # e.g. https://127.0.0.1:2379. + prefix: /apisix # configuration prefix in etcd + timeout: 30 # The timeout when connect/read/write to etcd. + watch_timeout: 50 # The timeout when watch etcd + #resync_delay: 5 # when sync failed and a rest is needed, resync after the configured seconds plus 50% random jitter + #health_check_timeout: 10 # etcd retry the unhealthy nodes after the configured seconds + startup_retry: 2 # the number of retry to etcd during the startup, default to 2 + #user: root # root username for etcd + #password: 5tHkHhYkjr6cQY # root password for etcd + tls: + # To enable etcd client certificate you need to build APISIX-Base, see + # https://apisix.apache.org/docs/apisix/FAQ#how-do-i-build-the-apisix-base-environment + #cert: /path/to/cert # path of certificate used by the etcd client + #key: /path/to/key # path of key used by the etcd client + + verify: true # whether to verify the etcd endpoint certificate when setup a TLS connection to etcd, + # the default value is true, e.g. the certificate will be verified strictly. + #sni: # the SNI for etcd TLS requests. If missed, the host part of the URL will be used. + +api7ee: + telemetry: + enable: true # enable telemetry data report to the control plane + interval: 15 # interval in seconds to send telemetry data to the control plane + max_metrics_size: 33554432 # max size in bytes(32M) of the metrics data sent to the control plane, if the size exceeds, the data will be truncated + metrics_batch_size: 4194304 # Max batch size before compression (bytes, 4 MiB). + compression_level: -1 # gzip compression level. -1 uses library default (usually 6). + # Range 0-9; 1 fastest, 9 highest compression. Gzip is enabled by default. + healthcheck_report_interval: 120 # healthcheck data report interval in seconds + http_timeout: 30s # http timeout used while sending healthcheck and telemetry data to the control plane + consumer_proxy: + enable: false # if enable is true, the consumer resources will be proxied by the control plane. + cache_success_count: 512 # cache the count of successful results queried from the control plane. + cache_success_ttl: 60 # (unit:sec), cache the ttl of successful results queried from the control plane. + cache_failure_count: 512 # cache the count of failed results queried from the control plane. + cache_failure_ttl: 60 # (unit:sec), cache the ttl of failed results queried from the control plane. + developer_proxy: # proxy portal-auth plugin + cache_success_count: 256 # cache the count of successful results queried from the control plane. + cache_success_ttl: 15 # (unit:sec), cache the ttl of successful results queried from the control plane. + cache_failure_count: 256 # cache the count of failed results queried from the control plane. + cache_failure_ttl: 15 # (unit:sec), cache the ttl of failed results queried from the control plane. diff --git a/t/plugin/opentelemetry6.t b/t/plugin/opentelemetry6.t index 0b01f623a0f9..003e191f5268 100644 --- a/t/plugin/opentelemetry6.t +++ b/t/plugin/opentelemetry6.t @@ -36,6 +36,8 @@ add_block_preprocessor(sub { if (!$block->extra_yaml_config) { my $extra_yaml_config = <<_EOC_; +apisix: + tracing: true plugins: - opentelemetry _EOC_ @@ -200,32 +202,35 @@ opentracing === TEST 6: check sni_radixtree_match span +--- max_size: 1048576 --- exec -sed -i '$d' ci/pod/otelcol-contrib/data-otlp.json -tail -n 13 ci/pod/otelcol-contrib/data-otlp.json +tail -n 18 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*sni_radixtree_match.*/ === TEST 7: check resolve_dns span +--- max_size: 1048576 --- exec -tail -n 13 ci/pod/otelcol-contrib/data-otlp.json +tail -n 18 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*resolve_dns.*/ === TEST 8: check apisix.phase.access span +--- max_size: 1048576 --- exec -tail ci/pod/otelcol-contrib/data-otlp.json +tail -n 18 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval qr/.*apisix.phase.access.*/ -=== TEST 9: check apisix.plugins.phase.header_filter span +=== TEST 9: check apisix.phase.header_filter span +--- max_size: 1048576 --- exec -tail -n 12 ci/pod/otelcol-contrib/data-otlp.json +tail -n 18 ci/pod/otelcol-contrib/data-otlp.json --- response_body eval -qr/.*apisix.plugins.phase.header_filter.*/ +qr/.*apisix.phase.header_filter.*/ From 25002810c403ffe16415df6e566ad3c2e9f77372 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 10:07:20 +0800 Subject: [PATCH 38/52] f --- apisix/cli/config.lua | 3 ++- apisix/core/response.lua | 3 +-- apisix/init.lua | 35 ----------------------------------- apisix/secret.lua | 9 ++++----- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/apisix/cli/config.lua b/apisix/cli/config.lua index 25e6783c23f8..13359fa814cd 100644 --- a/apisix/cli/config.lua +++ b/apisix/cli/config.lua @@ -84,7 +84,8 @@ local _M = { neg_ttl = 60, neg_count = 512 } - } + }, + tracing = false }, nginx_config = { error_log = "logs/error.log", diff --git a/apisix/core/response.lua b/apisix/core/response.lua index 25c9ba6b7a9f..f182abf9c3c0 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -19,7 +19,7 @@ -- -- @module core.response -local tracer = require("apisix.utils.tracer") +local tracer = require("apisix.tracer") local encode_json = require("cjson.safe").encode local ngx = ngx local arg = ngx.arg @@ -31,7 +31,6 @@ if ngx.config.subsystem == "http" then ngx_add_header = ngx_resp.add_header end -local tracer = require("apisix.tracer") local error = error local select = select local type = type diff --git a/apisix/init.lua b/apisix/init.lua index f283d7b3a793..ed3d1a67f200 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -46,17 +46,10 @@ local ctxdump = require("resty.ctxdump") local debug = require("apisix.debug") local pubsub_kafka = require("apisix.pubsub.kafka") local resource = require("apisix.resource") -<<<<<<< HEAD local trusted_addresses_util = require("apisix.utils.trusted-addresses") -<<<<<<< HEAD -local tracer = require("apisix.utils.tracer") -======= local tracer = require("apisix.tracer") ->>>>>>> fece8f294 (perf spans) -======= local discovery = require("apisix.discovery.init").discovery ->>>>>>> master local ngx = ngx local get_method = ngx.req.get_method local ngx_exit = ngx.exit @@ -229,35 +222,21 @@ function _M.ssl_client_hello_phase() core.log.error("failed to fetch ssl config: ", err) end core.log.error("failed to match any SSL certificate by SNI: ", sni) -<<<<<<< HEAD - span:set_status(tracer.status.ERROR, "failed match SNI") - tracer.finish_current_span() -======= tracer.finish(ngx_ctx, tracer.status.ERROR, "failed match SNI") ->>>>>>> fece8f294 (perf spans) ngx_exit(-1) end ok, err = apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols) if not ok then core.log.error("failed to set ssl protocols: ", err) -<<<<<<< HEAD - span:set_status(tracer.status.ERROR, "failed set protocols") - tracer.finish_current_span() -======= tracer.finish(ngx_ctx, tracer.status.ERROR, "failed set protocols") ->>>>>>> fece8f294 (perf spans) ngx_exit(-1) end -- in stream subsystem, ngx.ssl.server_name() return hostname of ssl session in preread phase, -- so that we can't get real SNI without recording it in ngx.ctx during client_hello phase ngx.ctx.client_hello_sni = sni -<<<<<<< HEAD - tracer.finish_current_span() -======= tracer.finish(ngx_ctx) ->>>>>>> fece8f294 (perf spans) end @@ -751,36 +730,22 @@ function _M.http_access_phase() handle_x_forwarded_headers(api_ctx) -<<<<<<< HEAD - local router_match_span = tracer.new_span("http_router_match", tracer.kind.internal) -======= tracer.start(ngx_ctx, "http_router_match", tracer.kind.internal) ->>>>>>> fece8f294 (perf spans) router.router_http.match(api_ctx) local route = api_ctx.matched_route if not route then -<<<<<<< HEAD - tracer.new_span("run_global_rules", tracer.kind.internal) -======= tracer.finish(ngx.ctx, tracer.status.ERROR, "no matched route") ->>>>>>> fece8f294 (perf spans) -- run global rule when there is no matching route local global_rules, conf_version = apisix_global_rules.global_rules() plugin.run_global_rules(api_ctx, global_rules, conf_version, nil) - tracer.finish_current_span() - core.log.info("not find any matched route") - router_match_span:set_status(tracer.status.ERROR, "no matched route") - tracer.finish_current_span() return core.response.exit(404, {error_msg = "404 Route Not Found"}) end tracer.finish(ngx_ctx) - tracer.finish_current_span() - core.log.info("matched route: ", core.json.delay_encode(api_ctx.matched_route, true)) diff --git a/apisix/secret.lua b/apisix/secret.lua index ba9ee565324d..24a5e611aec3 100644 --- a/apisix/secret.lua +++ b/apisix/secret.lua @@ -18,7 +18,7 @@ local require = require local core = require("apisix.core") local string = require("apisix.core.string") -local tracer = require("apisix.utils.tracer") +local tracer = require("apisix.tracer") local local_conf = require("apisix.core.config_local").local_conf() @@ -149,7 +149,7 @@ local function fetch_by_uri_secret(secret_uri) return nil, "no secret conf, secret_uri: " .. secret_uri end - local span = tracer.new_span("fetch_secret", tracer.kind.client) + tracer.start(ngx.ctx, "fetch_secret", tracer.kind.client) local ok, sm = pcall(require, "apisix.secret." .. opts.manager) if not ok then return nil, "no secret manager: " .. opts.manager @@ -157,12 +157,11 @@ local function fetch_by_uri_secret(secret_uri) local value, err = sm.get(conf, opts.key) if err then - span:set_status(tracer.status.ERROR, err) - tracer.finish_current_span() + tracer.finish(ngx.ctx, tracer.status.ERROR, err) return nil, err end - tracer.finish_current_span() + tracer.finish(ngx.ctx) return value end From baee5802afe219e81956708ead7e9ca4af3aafb0 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 10:07:43 +0800 Subject: [PATCH 39/52] remove --- apisix/utils/tracer.lua | 80 ---- conf/config-default.yaml | 764 --------------------------------------- 2 files changed, 844 deletions(-) delete mode 100644 apisix/utils/tracer.lua delete mode 100755 conf/config-default.yaml diff --git a/apisix/utils/tracer.lua b/apisix/utils/tracer.lua deleted file mode 100644 index 86e3ae0cec05..000000000000 --- a/apisix/utils/tracer.lua +++ /dev/null @@ -1,80 +0,0 @@ --- --- Licensed to the Apache Software Foundation (ASF) under one or more --- contributor license agreements. See the NOTICE file distributed with --- this work for additional information regarding copyright ownership. --- The ASF licenses this file to You under the Apache License, Version 2.0 --- (the "License"); you may not use this file except in compliance with --- the License. You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- -local ngx = ngx -local stack = require("apisix.utils.stack") -local span = require("apisix.utils.span") -local span_kind = require("opentelemetry.trace.span_kind") -local span_status = require("opentelemetry.trace.span_status") -local table = table -local pairs = pairs - -local _M = { - kind = span_kind, - status = span_status, -} - - -function _M.new_span(name, kind) - local ctx = ngx.ctx - if not ctx._apisix_spans then - ctx._apisix_spans = {} - end - if not ctx._apisix_span_stack then - ctx._apisix_span_stack = stack.new() - end - local sp = span.new(name, kind) - if ctx._apisix_skip_tracing then - return sp - end - if ctx._apisix_span_stack:is_empty() then - table.insert(ctx._apisix_spans, sp) - else - local parent_span = ctx._apisix_span_stack:peek() - parent_span:append_child(sp) - end - ctx._apisix_span_stack:push(sp) - return sp -end - - -function _M.finish_current_span(code, message) - if not ngx.ctx._apisix_span_stack then - return - end - local sp = ngx.ctx._apisix_span_stack:pop() - if code then - sp:set_status(code, message) - end - sp:finish() -end - -function _M.finish_all_spans(code, message) - local apisix_spans = ngx.ctx._apisix_spans - if not apisix_spans then - return - end - - for _, sp in pairs(apisix_spans) do - if code then - sp:set_status(code, message) - end - sp:finish() - end -end - - -return _M diff --git a/conf/config-default.yaml b/conf/config-default.yaml deleted file mode 100755 index 05538f23ea03..000000000000 --- a/conf/config-default.yaml +++ /dev/null @@ -1,764 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# PLEASE DO NOT UPDATE THIS FILE! -# If you want to set the specified configuration value, you can set the new -# value in the conf/config.yaml file. -# - -apisix: - # node_listen: 9080 # APISIX listening port - node_listen: # This style support multiple ports - - 9080 - # - port: 9081 - # enable_http2: true # If not set, the default value is `false`. - # - ip: 127.0.0.2 # Specific IP, If not set, the default value is `0.0.0.0`. - # port: 9082 - # enable_http2: true - # - port: 9082 - # backlog: 1024 # sets the backlog parameter in the listen() call that limits - # the maximum length for the queue of pending connections. - # By default, backlog is set to -1 on FreeBSD, DragonFly BSD, and macOS, - # and to 511 on other platforms. - enable_admin: true - enable_dev_mode: false # Sets nginx worker_processes to 1 if set to true - enable_reuseport: true # Enable nginx SO_REUSEPORT switch if set to true. - show_upstream_status_in_response_header: false # when true all upstream status write to `X-APISIX-Upstream-Status` otherwise only 5xx code - enable_ipv6: true - - #proxy_protocol: # Proxy Protocol configuration - # listen_http_port: 9181 # The port with proxy protocol for http, it differs from node_listen and admin_listen. - # This port can only receive http request with proxy protocol, but node_listen & admin_listen - # can only receive http request. If you enable proxy protocol, you must use this port to - # receive http request with proxy protocol - # listen_https_port: 9182 # The port with proxy protocol for https - # enable_tcp_pp: true # Enable the proxy protocol for tcp proxy, it works for stream_proxy.tcp option - # enable_tcp_pp_to_upstream: true # Enables the proxy protocol to the upstream server - enable_server_tokens: true # Whether the APISIX version number should be shown in Server header. - # It's enabled by default. - - # configurations to load third party code and/or override the builtin one. - extra_lua_path: "" # extend lua_package_path to load third party code - extra_lua_cpath: "" # extend lua_package_cpath to load third party code - lua_module_hook: "agent.hook" # the hook module which will be used to inject third party code into APISIX - - proxy_cache: # Proxy Caching configuration - cache_ttl: 10s # The default caching time in disk if the upstream does not specify the cache time - zones: # The parameters of a cache - - name: disk_cache_one # The name of the cache, administrator can specify - # which cache to use by name in the admin api (disk|memory) - memory_size: 50m # The size of shared memory, it's used to store the cache index for - # disk strategy, store cache content for memory strategy (disk|memory) - disk_size: 1G # The size of disk, it's used to store the cache data (disk) - disk_path: /tmp/disk_cache_one # The path to store the cache data (disk) - cache_levels: "1:2" # The hierarchy levels of a cache (disk) - #- name: disk_cache_two - # memory_size: 50m - # disk_size: 1G - # disk_path: "/tmp/disk_cache_two" - # cache_levels: "1:2" - - name: memory_cache - memory_size: 50m - - delete_uri_tail_slash: false # delete the '/' at the end of the URI - # The URI normalization in servlet is a little different from the RFC's. - # See https://github.com/jakartaee/servlet/blob/master/spec/src/main/asciidoc/servlet-spec-body.adoc#352-uri-path-canonicalization, - # which is used under Tomcat. - # Turn this option on if you want to be compatible with servlet when matching URI path. - normalize_uri_like_servlet: false - router: - http: radixtree_uri # radixtree_uri: match route by uri(base on radixtree) - # radixtree_host_uri: match route by host + uri(base on radixtree) - # radixtree_uri_with_parameter: like radixtree_uri but match uri with parameters, - # see https://github.com/api7/lua-resty-radixtree/#parameters-in-path for - # more details. - ssl: radixtree_sni # radixtree_sni: match route by SNI(base on radixtree) - #stream_proxy: # TCP/UDP proxy - # only: true # use stream proxy only, don't enable HTTP stuff - # tcp: # TCP proxy port list - # - addr: 9100 - # tls: true - # - addr: "127.0.0.1:9101" - # udp: # UDP proxy port list - # - 9200 - # - "127.0.0.1:9201" - #dns_resolver: # If not set, read from `/etc/resolv.conf` - # - 1.1.1.1 - # - 8.8.8.8 - #dns_resolver_valid: 30 # if given, override the TTL of the valid records. The unit is second. - resolver_timeout: 5 # resolver timeout - enable_resolv_search_opt: true # enable search option in resolv.conf - ssl: - enable: true - listen: # APISIX listening port in https. - - port: 9443 - enable_http2: true - # - ip: 127.0.0.3 # Specific IP, If not set, the default value is `0.0.0.0`. - # port: 9445 - # enable_http2: true - # - port: 9446 - # backlog: 1024 # sets the backlog parameter in the listen() call that limits - # the maximum length for the queue of pending connections. - # By default, backlog is set to -1 on FreeBSD, DragonFly BSD, and macOS, - # and to 511 on other platforms. - # ssl_trusted_combined_path: /usr/local/apisix/conf/cert/.ssl_trusted_combined.pem # All the trusted certificates will be combined into a single file - ssl_trusted_certificate: system # Specifies comma separated list of trusted CA. Value can be either "system"(for using system available ca certs) or - # a file path with trusted CA certificates in the PEM format - # used to verify the certificate when APISIX needs to do SSL/TLS handshaking - # with external services (e.g. etcd) - ssl_protocols: TLSv1.2 TLSv1.3 - ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 - ssl_session_tickets: false # disable ssl_session_tickets by default for 'ssl_session_tickets' would make Perfect Forward Secrecy useless. - # ref: https://github.com/mozilla/server-side-tls/issues/135 - - key_encrypt_salt: # If not set, will save origin ssl key into etcd. - - edd1c9f0985e76a2 # If set this, the key_encrypt_salt should be an array whose elements are string, and the size is also 16, and it will encrypt ssl key with AES-128-CBC - # !!! So do not change it after saving your ssl, it can't decrypt the ssl keys have be saved if you change !! - # Only use the first key to encrypt, and decrypt in the order of the array. - - #fallback_sni: "my.default.domain" # If set this, when the client doesn't send SNI during handshake, the fallback SNI will be used instead - enable_control: true - #control: - # ip: 127.0.0.1 - # port: 9090 - disable_sync_configuration_during_start: false # safe exit. Remove this once the feature is stable - - # This time will be used to distinguish whether the worker is started first time or restarted due to a crash, unit: second. - worker_startup_time_threshold: 60 - - data_encryption: # add `encrypt_fields = { $field },` in plugin schema to enable encryption - enable: false # if not set, the default value is `false`. - keyring: - - qeddd145sfvddff3 # If not set, will save origin value into etcd. - # If set this, the keyring should be an array whose elements are string, and the size is also 16, and it will encrypt fields with AES-128-CBC - # !!! So do not change it after encryption, it can't decrypt the fields have be saved if you change !! - # Only use the first key to encrypt, and decrypt in the order of the array. - - # status: # When enabled, APISIX will provide `/status` and `/status/ready` endpoints - # ip: 127.0.0.1 # /status endpoint will return 200 status code if APISIX has successfully started and running correctly - # port: 7085 # /status/ready endpoint will return 503 status code if none of the configured etcd (dp_manager) are available - - # disable_upstream_healthcheck: false # A global switch for healthcheck. Defaults to false. - # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. - - # fine tune the parameters of LRU cache for some features like secret - lru: - secret: - ttl: 300 # seconds - count: 512 - neg_ttl: 60 - neg_count: 512 - - # enable more detailed tracing information - tracing: false - -nginx_config: # config for render the template to generate nginx.conf - #user: root # specifies the execution user of the worker process. - # the "user" directive makes sense only if the master process runs with super-user privileges. - # if you're not root user,the default is current user. - error_log: logs/error.log - error_log_level: warn # warn,error - worker_processes: auto # if you want use multiple cores in container, you can inject the number of cpu as environment variable "APISIX_WORKER_PROCESSES" - enable_cpu_affinity: false # disable CPU affinity by default, if APISIX is deployed on a physical machine, it can be enabled and work well. - worker_rlimit_nofile: 20480 # the number of files a worker process can open, should be larger than worker_connections - worker_shutdown_timeout: 240s # timeout for a graceful shutdown of worker processes - - max_pending_timers: 16384 # increase it if you see "too many pending timers" error - max_running_timers: 4096 # increase it if you see "lua_max_running_timers are not enough" error - - event: - worker_connections: 10620 - #envs: # allow to get a list of environment variables - # - TEST_ENV - - meta: - lua_shared_dict: - prometheus-metrics: 15m - - stream: - enable_access_log: false # enable access log or not, default false - access_log: logs/access_stream.log - access_log_format: "$remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time" - # create your custom log format by visiting http://nginx.org/en/docs/varindex.html - access_log_format_escape: default # allows setting json or default characters escaping in variables - lua_shared_dict: - etcd-cluster-health-check-stream: 10m - lrucache-lock-stream: 10m - plugin-limit-conn-stream: 10m - worker-events-stream: 10m - tars-stream: 1m - config-stream: 5m - - # As user can add arbitrary configurations in the snippet, - # it is user's responsibility to check the configurations - # don't conflict with APISIX. - main_configuration_snippet: | - # Add custom Nginx main configuration to nginx.conf. - # The configuration should be well indented! - http_configuration_snippet: | - # Add custom Nginx http configuration to nginx.conf. - # The configuration should be well indented! - http_server_configuration_snippet: | - # Add custom Nginx http server configuration to nginx.conf. - # The configuration should be well indented! - http_server_location_configuration_snippet: | - # Add custom Nginx http server location configuration to nginx.conf. - # The configuration should be well indented! - http_admin_configuration_snippet: | - # Add custom Nginx admin server configuration to nginx.conf. - # The configuration should be well indented! - http_end_configuration_snippet: | - # Add custom Nginx http end configuration to nginx.conf. - # The configuration should be well indented! - stream_configuration_snippet: | - # Add custom Nginx stream configuration to nginx.conf. - # The configuration should be well indented! - - http: - enable_access_log: true # enable access log or not, default true - access_log: logs/access.log - # available variables: - # request_type: traditional_http / ai_chat / ai_stream - # llm_time_to_first_token: duration from the start send request to ai server to the first token received - # llm_prompt_tokens: number of tokens in the prompt - # llm_completion_tokens: number of tokens in the chat completion - access_log_format: "$remote_addr - $remote_user [$time_local] $http_host \"$request_line\" $status $body_bytes_sent $request_time \"$http_referer\" \"$http_user_agent\" $upstream_addr $upstream_status $upstream_response_time \"$upstream_scheme://$upstream_host$upstream_uri\" \"$apisix_request_id\"" - access_log_format_escape: default # allows setting json or default characters escaping in variables - keepalive_timeout: 60s # timeout during which a keep-alive client connection will stay open on the server side. - client_header_timeout: 60s # timeout for reading client request header, then 408 (Request Time-out) error is returned to the client - client_body_timeout: 60s # timeout for reading client request body, then 408 (Request Time-out) error is returned to the client - client_max_body_size: 0 # The maximum allowed size of the client request body. - # If exceeded, the 413 (Request Entity Too Large) error is returned to the client. - # Note that unlike Nginx, we don't limit the body size by default. - - send_timeout: 10s # timeout for transmitting a response to the client.then the connection is closed - underscores_in_headers: "on" # default enables the use of underscores in client request header fields - real_ip_header: X-Real-IP # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header - real_ip_recursive: "off" # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive - real_ip_from: # http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from - - 127.0.0.1 - - "unix:" - custom_lua_shared_dict: # add custom shared cache to nginx.conf - # ipc_shared_dict: 100m # custom shared cache, format: `cache-key: cache-size` - config: 5m - kubernetes: 20m - nacos: 20m - - # Enables or disables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) - # when establishing a connection with the proxied HTTPS server. - proxy_ssl_server_name: true - upstream: - keepalive: 320 # Sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. - # When this number is exceeded, the least recently used connections are closed. - keepalive_requests: 1000 # Sets the maximum number of requests that can be served through one keepalive connection. - # After the maximum number of requests is made, the connection is closed. - keepalive_timeout: 60s # Sets a timeout during which an idle keepalive connection to an upstream server will stay open. - charset: utf-8 # Adds the specified charset to the "Content-Type" response header field, see - # http://nginx.org/en/docs/http/ngx_http_charset_module.html#charset - variables_hash_max_size: 2048 # Sets the maximum size of the variables hash table. - - lua_shared_dict: - internal-status: 10m - plugin-limit-req: 10m - plugin-limit-count: 10m - plugin-limit-conn: 10m - plugin-limit-conn-redis-cluster-slot-lock: 1m - plugin-graphql-limit-count: 10m - plugin-graphql-limit-count-reset-header: 10m - plugin-ai-rate-limiting: 10m - plugin-ai-rate-limiting-reset-header: 10m - upstream-healthcheck: 10m - worker-events: 10m - lrucache-lock: 10m - balancer-ewma: 10m - balancer-ewma-locks: 10m - balancer-ewma-last-touched-at: 10m - plugin-limit-req-redis-cluster-slot-lock: 1m - plugin-limit-count-redis-cluster-slot-lock: 1m - plugin-limit-count-advanced: 10m - plugin-limit-count-advanced-redis-cluster-slot-lock: 1m - tracing_buffer: 10m - plugin-api-breaker: 10m - etcd-cluster-health-check: 10m - discovery: 1m - jwks: 1m - introspection: 10m - access-tokens: 1m - ext-plugin: 1m - tars: 1m - cas-auth: 10m - status_report: 1m - -#discovery: # service discovery center -# dns: -# servers: -# - "127.0.0.1:8600" # use the real address of your dns server -# order: # order in which to try different dns record types when resolving -# - last # "last" will try the last previously successful type for a hostname. -# - SRV -# - A -# - AAAA -# - CNAME -# eureka: -# host: # it's possible to define multiple eureka hosts addresses of the same eureka cluster. -# - "http://127.0.0.1:8761" -# prefix: /eureka/ -# fetch_interval: 30 # default 30s -# weight: 100 # default weight for node -# timeout: -# connect: 2000 # default 2000ms -# send: 2000 # default 2000ms -# read: 5000 # default 5000ms -# nacos: -# host: -# - "http://${username}:${password}@${host1}:${port1}" -# prefix: "/nacos/v1/" -# fetch_interval: 30 # default 30 sec -# weight: 100 # default 100 -# timeout: -# connect: 2000 # default 2000 ms -# send: 2000 # default 2000 ms -# read: 5000 # default 5000 ms -# consul_kv: -# servers: -# - "http://127.0.0.1:8500" -# - "http://127.0.0.1:8600" -# prefix: "upstreams" -# skip_keys: # if you need to skip special keys -# - "upstreams/unused_api/" -# timeout: -# connect: 2000 # default 2000 ms -# read: 2000 # default 2000 ms -# wait: 60 # default 60 sec -# weight: 1 # default 1 -# fetch_interval: 3 # default 3 sec, only take effect for keepalive: false way -# keepalive: true # default true, use the long pull way to query consul servers -# sort_type: "origin" # default origin -# default_server: # you can define default server when missing hit -# host: "127.0.0.1" -# port: 20999 -# metadata: -# fail_timeout: 1 # default 1 ms -# weight: 1 # default 1 -# max_fails: 1 # default 1 -# dump: # if you need, when registered nodes updated can dump into file -# path: "logs/consul_kv.dump" -# expire: 2592000 # unit sec, here is 30 day -# consul: -# servers: # make sure service name is unique in these consul servers -# - "http://127.0.0.1:8500" # `http://127.0.0.1:8500` and `http://127.0.0.1:8600` are different clusters -# - "http://127.0.0.1:8600" -# skip_services: # if you need to skip special services -# - "service_a" # `consul` service is default skip service -# timeout: -# connect: 2000 # default 2000 ms -# read: 2000 # default 2000 ms -# wait: 60 # default 60 sec -# weight: 1 # default 1 -# fetch_interval: 3 # default 3 sec, only take effect for keepalive: false way -# keepalive: true # default true, use the long pull way to query consul servers -# default_service: # you can define default server when missing hit -# host: "127.0.0.1" -# port: 20999 -# metadata: -# fail_timeout: 1 # default 1 ms -# weight: 1 # default 1 -# max_fails: 1 # default 1 -# dump: # if you need, when registered nodes updated can dump into file -# path: "logs/consul.dump" -# expire: 2592000 # unit sec, here is 30 day -# load_on_init: true # default true, load the consul dump file on init -# kubernetes: -# ### kubernetes service discovery both support single-cluster and multi-cluster mode -# ### applicable to the case where the service is distributed in a single or multiple kubernetes clusters. -# -# ### single-cluster mode ### -# service: -# schema: https #apiserver schema, options [http, https], default https -# host: ${KUBERNETES_SERVICE_HOST} #apiserver host, options [ipv4, ipv6, domain, environment variable], default ${KUBERNETES_SERVICE_HOST} -# port: ${KUBERNETES_SERVICE_PORT} #apiserver port, options [port number, environment variable], default ${KUBERNETES_SERVICE_PORT} -# client: -# # serviceaccount token or path of serviceaccount token_file -# token_file: ${KUBERNETES_CLIENT_TOKEN_FILE} -# # token: |- -# # eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEif -# # 6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEifeyJhbGciOiJSUzI1NiIsImtpZCI -# # kubernetes discovery plugin support use namespace_selector -# # you can use one of [equal, not_equal, match, not_match] filter namespace -# namespace_selector: -# # only save endpoints with namespace equal default -# equal: default -# # only save endpoints with namespace not equal default -# #not_equal: default -# # only save endpoints with namespace match one of [default, ^my-[a-z]+$] -# #match: -# #- default -# #- ^my-[a-z]+$ -# # only save endpoints with namespace not match one of [default, ^my-[a-z]+$ ] -# #not_match: -# #- default -# #- ^my-[a-z]+$ -# # kubernetes discovery plugin support use label_selector -# # for the expression of label_selector, please refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/labels -# label_selector: |- -# first="a",second="b" -# # reserved lua shared memory size,1m memory can store about 1000 pieces of endpoint -# shared_size: 1m #default 1m -# ### single-cluster mode ### -# -# ### multi-cluster mode ### -# - id: release # a custom name refer to the cluster, pattern ^[a-z0-9]{1,8} -# service: -# schema: https #apiserver schema, options [http, https], default https -# host: ${KUBERNETES_SERVICE_HOST} #apiserver host, options [ipv4, ipv6, domain, environment variable] -# port: ${KUBERNETES_SERVICE_PORT} #apiserver port, options [port number, environment variable] -# client: -# # serviceaccount token or path of serviceaccount token_file -# token_file: ${KUBERNETES_CLIENT_TOKEN_FILE} -# # token: |- -# # eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEif -# # 6Ikx5ME1DNWdnbmhQNkZCNlZYMXBsT3pYU3BBS2swYzBPSkN3ZnBESGpkUEEifeyJhbGciOiJSUzI1NiIsImtpZCI -# # kubernetes discovery plugin support use namespace_selector -# # you can use one of [equal, not_equal, match, not_match] filter namespace -# namespace_selector: -# # only save endpoints with namespace equal default -# equal: default -# # only save endpoints with namespace not equal default -# #not_equal: default -# # only save endpoints with namespace match one of [default, ^my-[a-z]+$] -# #match: -# #- default -# #- ^my-[a-z]+$ -# # only save endpoints with namespace not match one of [default, ^my-[a-z]+$ ] -# #not_match: -# #- default -# #- ^my-[a-z]+$ -# # kubernetes discovery plugin support use label_selector -# # for the expression of label_selector, please refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/labels -# label_selector: |- -# first="a",second="b" -# # reserved lua shared memory size,1m memory can store about 1000 pieces of endpoint -# shared_size: 1m #default 1m -# ### multi-cluster mode ### - -graphql: - max_size: 1048576 # the maximum size limitation of graphql in bytes, default 1MiB - -#ext-plugin: - #cmd: ["ls", "-l"] - -plugins: # plugin list (sorted by priority) -# - exit-transformer # priority: 9999999 - - real-ip # priority: 23000 - # - toolset # priority: 22901 - - ai # priority: 22900 - - client-control # priority: 22000 - - proxy-buffering # priority: 21991 - - proxy-control # priority: 21990 - - request-id # priority: 12015 - - zipkin # priority: 12011 - #- skywalking # priority: 12010 - #- opentelemetry # priority: 12009 - - ext-plugin-pre-req # priority: 12000 - - fault-injection # priority: 11000 - - mocking # priority: 10900 - - serverless-pre-function # priority: 10000 - #- batch-requests # priority: 4010 - - cors # priority: 4000 - - ip-restriction # priority: 3000 - - ua-restriction # priority: 2999 - - referer-restriction # priority: 2990 - - csrf # priority: 2980 - - uri-blocker # priority: 2900 - - request-validation # priority: 2800 - - portal-auth # priority: 2750 - - chaitin-waf # priority: 2700 - - multi-auth # priority: 2600 - - openid-connect # priority: 2599 - - saml-auth # priority: 2598 - - cas-auth # priority: 2597 - - authz-casbin # priority: 2560 - - authz-casdoor # priority: 2559 - - wolf-rbac # priority: 2555 - - ldap-auth # priority: 2540 - - hmac-auth # priority: 2530 - - basic-auth # priority: 2520 - - jwt-auth # priority: 2510 - - key-auth # priority: 2500 - - acl # priority: 2410 - - consumer-restriction # priority: 2400 - - attach-consumer-label # priority: 2399 - - forward-auth # priority: 2002 - - opa # priority: 2001 - - authz-keycloak # priority: 2000 - - data-mask # priority: 1500 - #- error-log-logger # priority: 1091 - - proxy-cache # priority: 1085 - - body-transformer # priority: 1080 - - ai-request-rewrite # priority: 1073 - - ai-prompt-template # priority: 1071 - - ai-prompt-decorator # priority: 1070 - - ai-prompt-guard # priority: 1072 - - ai-rag # priority: 1060 - - ai-aws-content-moderation # priority: 1050 - - ai-proxy-multi # priority: 1041 - - ai-proxy # priority: 1040 - - ai-rate-limiting # priority: 1030 - - ai-aliyun-content-moderation # priority: 1029 - - proxy-mirror # priority: 1010 - - graphql-proxy-cache # priority: 1009 - - proxy-rewrite # priority: 1008 - - workflow # priority: 1006 - - api-breaker # priority: 1005 - - graphql-limit-count # priority: 1004 - - limit-conn # priority: 1003 - - limit-count # priority: 1002 - - limit-count-advanced # priority: 1001 - - limit-req # priority: 999 - #- node-status # priority: 998 - - traffic-label # priority: 995 - - gzip # priority: 994 - - server-info # priority: 990 - - api7-traffic-split # priority: 966 - - traffic-split # priority: 966 - - redirect # priority: 900 - - response-rewrite # priority: 899 - - soap # priority: 554 - - openapi-to-mcp # priority: 540 - - oas-validator # priority: 510 - - degraphql # priority: 509 - - kafka-proxy # priority: 508 - #- dubbo-proxy # priority: 507 - - grpc-transcode # priority: 506 - - grpc-web # priority: 505 - - public-api # priority: 501 - - prometheus # priority: 500 - - datadog # priority: 495 - - lago # priority: 415 - - error-page # priority: 450 - - loki-logger # priority: 414 - - elasticsearch-logger # priority: 413 - - echo # priority: 412 - - loggly # priority: 411 - - http-logger # priority: 410 - - splunk-hec-logging # priority: 409 - - skywalking-logger # priority: 408 - - google-cloud-logging # priority: 407 - - sls-logger # priority: 406 - - tcp-logger # priority: 405 - - kafka-logger # priority: 403 - - rocketmq-logger # priority: 402 - - syslog # priority: 401 - - udp-logger # priority: 400 - - file-logger # priority: 399 - - clickhouse-logger # priority: 398 - - tencent-cloud-cls # priority: 397 - #- log-rotate # priority: 100 - # <- recommend to use priority (0, 100) for your custom plugins - - example-plugin # priority: 0 - #- gm # priority: -43 - - aws-lambda # priority: -1899 - - azure-functions # priority: -1900 - - openwhisk # priority: -1901 - - openfunction # priority: -1902 - - serverless-post-function # priority: -2000 - - ext-plugin-post-req # priority: -3000 - - ext-plugin-post-resp # priority: -4000 - -stream_plugins: # sorted by priority - - ip-restriction # priority: 3000 - - limit-conn # priority: 1003 - - mqtt-proxy # priority: 1000 - - traffic-split # priority: 966 - #- prometheus # priority: 500 - - syslog # priority: 401 - # <- recommend to use priority (0, 100) for your custom plugins - -#wasm: - #plugins: - #- name: wasm_log - #priority: 7999 - #file: t/wasm/log/main.go.wasm - -#xrpc: - #protocols: - #- name: pingpong - -plugin_attr: - log-rotate: - enable: false - timeout: 10000 # maximum wait time for a log rotation(unit: millisecond) interval: 3600 # rotate interval (unit: second) - max_kept: 168 # max number of log files will be kept - max_size: -1 # max size bytes of log files to be rotated, size check would be skipped with a value less than 0 - enable_compression: false # enable log file compression(gzip) or not, default false - skywalking: - service_name: APISIX - service_instance_name: APISIX Instance Name - endpoint_addr: http://127.0.0.1:12800 - soap: - endpoint: http://127.0.0.1:5000 - timeout: 3000 - prometheus: - export_uri: /apisix/prometheus/metrics - metric_prefix: apisix_ - enable_export_server: true - export_addr: - ip: 127.0.0.1 - port: 9091 - fetch_metric_timeout: 5 - allow_degradation: false - degradation_pause_steps: [ 60 ] - #metrics: - # http_status: - # # extra labels from nginx variables - # extra_labels: - # # the label name doesn't need to be the same as variable name - # # below labels are only examples, you could add any valid variables as you need - # - upstream_addr: $upstream_addr - # - upstream_status: $upstream_status - # expire: 0 # The expiration time after which metrics are removed. unit: second. - # # 0 means the metrics will not expire - # http_latency: - # extra_labels: - # - upstream_addr: $upstream_addr - # expire: 0 # The expiration time after which metrics are removed. unit: second. - # # 0 means the metrics will not expire - # bandwidth: - # extra_labels: - # - upstream_addr: $upstream_addr - # expire: 0 # The expiration time after which metrics are removed. unit: second. - # # 0 means the metrics will not expire - # default_buckets: - # - 10 - # - 50 - # - 100 - # - 200 - # - 500 - server-info: - report_ttl: 60 # live time for server info in etcd (unit: second) - dubbo-proxy: - upstream_multiplex_count: 32 - proxy-mirror: - timeout: # proxy timeout in mirrored sub-request - connect: 60s - read: 60s - send: 60s -# redirect: -# https_port: 8443 # the default port for use by HTTP redirects to HTTPS - inspect: - delay: 3 # in seconds - hooks_file: "/usr/local/apisix/plugin_inspect_hooks.lua" - zipkin: # Plugin: zipkin - set_ngx_var: false # export zipkin variables to nginx variables - -deployment: - role: traditional - role_traditional: - config_provider: etcd - admin: - # Default token when use API to call for Admin API. - # *NOTE*: Highly recommended to modify this value to protect APISIX's Admin API. - # Disabling this configuration item means that the Admin API does not - # require any authentication. - admin_key: - - - name: admin - key: edd1c9f034335f136f87ad84b625c8f1 - role: admin # admin: manage all configuration data - # viewer: only can view configuration data - - - name: viewer - key: 4054f7cf07e344346cd3f287985e76a2 - role: viewer - - enable_admin_cors: true # Admin API support CORS response headers. - allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow - - 127.0.0.0/24 # If we don't set any IP list, then any IP access is allowed by default. - #- "::/64" - admin_listen: # use a separate port - ip: 0.0.0.0 # Specific IP, if not set, the default value is `0.0.0.0`. - port: 9180 # Specific port, which must be different from node_listen's port. - - #https_admin: true # enable HTTPS when use a separate port for Admin API. - # Admin API will use conf/apisix_admin_api.crt and conf/apisix_admin_api.key as certificate. - - admin_api_mtls: # Depends on `admin_listen` and `https_admin`. - admin_ssl_cert: "" # Path of your self-signed server side cert. - admin_ssl_cert_key: "" # Path of your self-signed server side key. - admin_ssl_ca_cert: "" # Path of your self-signed ca cert.The CA is used to sign all admin api callers' certificates. - - admin_api_version: v3 # The version of admin api, latest version is v3. - - # fallback_cp: - # mode: "write" # "write" specifies that the dp instance will run on backup mode and not accept/proxy any requests. defaults to "write", which specifies "fallback" mode - # interval: 1 # interval in seconds for backup mode - # aws_s3: # configuration for AWS S3 to store configuration/resources either to push (in backup mode) or pull (in fallback mode) - # access_key: "just-access" - # secret_key: "super-secret" - # resource_bucket: "resource_bucket" - # config_bucket: "config_bucket" - # region: "ap-south-1" - # endpoint: "http://localhost:6969" - # azure_blob: # configuration for Azure Blob Storage to store configuration/resources either to push (in backup mode) or pull (in fallback mode) - # account_name: devstoreaccount1 - # account_key: "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" - # resource_container: yaml - # config_container: config - # endpoint: "http://localhost:10000/devstoreaccount1" - - etcd: - host: # it's possible to define multiple etcd hosts addresses of the same etcd cluster. - - "http://127.0.0.1:2379" # multiple etcd address, if your etcd cluster enables TLS, please use https scheme, - # e.g. https://127.0.0.1:2379. - prefix: /apisix # configuration prefix in etcd - timeout: 30 # The timeout when connect/read/write to etcd. - watch_timeout: 50 # The timeout when watch etcd - #resync_delay: 5 # when sync failed and a rest is needed, resync after the configured seconds plus 50% random jitter - #health_check_timeout: 10 # etcd retry the unhealthy nodes after the configured seconds - startup_retry: 2 # the number of retry to etcd during the startup, default to 2 - #user: root # root username for etcd - #password: 5tHkHhYkjr6cQY # root password for etcd - tls: - # To enable etcd client certificate you need to build APISIX-Base, see - # https://apisix.apache.org/docs/apisix/FAQ#how-do-i-build-the-apisix-base-environment - #cert: /path/to/cert # path of certificate used by the etcd client - #key: /path/to/key # path of key used by the etcd client - - verify: true # whether to verify the etcd endpoint certificate when setup a TLS connection to etcd, - # the default value is true, e.g. the certificate will be verified strictly. - #sni: # the SNI for etcd TLS requests. If missed, the host part of the URL will be used. - -api7ee: - telemetry: - enable: true # enable telemetry data report to the control plane - interval: 15 # interval in seconds to send telemetry data to the control plane - max_metrics_size: 33554432 # max size in bytes(32M) of the metrics data sent to the control plane, if the size exceeds, the data will be truncated - metrics_batch_size: 4194304 # Max batch size before compression (bytes, 4 MiB). - compression_level: -1 # gzip compression level. -1 uses library default (usually 6). - # Range 0-9; 1 fastest, 9 highest compression. Gzip is enabled by default. - healthcheck_report_interval: 120 # healthcheck data report interval in seconds - http_timeout: 30s # http timeout used while sending healthcheck and telemetry data to the control plane - consumer_proxy: - enable: false # if enable is true, the consumer resources will be proxied by the control plane. - cache_success_count: 512 # cache the count of successful results queried from the control plane. - cache_success_ttl: 60 # (unit:sec), cache the ttl of successful results queried from the control plane. - cache_failure_count: 512 # cache the count of failed results queried from the control plane. - cache_failure_ttl: 60 # (unit:sec), cache the ttl of failed results queried from the control plane. - developer_proxy: # proxy portal-auth plugin - cache_success_count: 256 # cache the count of successful results queried from the control plane. - cache_success_ttl: 15 # (unit:sec), cache the ttl of successful results queried from the control plane. - cache_failure_count: 256 # cache the count of failed results queried from the control plane. - cache_failure_ttl: 15 # (unit:sec), cache the ttl of failed results queried from the control plane. From 49a55bc282ba4bd1f3db0154c7aef4e494b1cbf3 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 10:33:19 +0800 Subject: [PATCH 40/52] f --- apisix/core/response.lua | 2 -- apisix/init.lua | 5 ----- apisix/ssl/router/radixtree_sni.lua | 5 ----- 3 files changed, 12 deletions(-) diff --git a/apisix/core/response.lua b/apisix/core/response.lua index f182abf9c3c0..60f83762af36 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -63,7 +63,6 @@ function resp_exit(code, ...) ngx.status = code end - local message for i = 1, select('#', ...) do local v = select(i, ...) if type(v) == "table" then @@ -75,7 +74,6 @@ function resp_exit(code, ...) t[idx] = body idx = idx + 1 t[idx] = "\n" - message = body end elseif v ~= nil then diff --git a/apisix/init.lua b/apisix/init.lua index ed3d1a67f200..1f682414d469 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -208,8 +208,6 @@ function _M.ssl_client_hello_phase() tracer.start(ngx_ctx, "ssl_client_hello_phase", tracer.kind.server) - local span = tracer.new_span("ssl_client_hello_phase", tracer.kind.server) - local ok, err = router.router_ssl.match_and_set(api_ctx, true, sni) ngx_ctx.matched_ssl = api_ctx.matched_ssl @@ -676,7 +674,6 @@ end function _M.http_access_phase() - tracer.new_span("apisix.phase.access", tracer.kind.server) -- from HTTP/3 to HTTP/1.1 we need to convert :authority pesudo-header -- to Host header, so we set upstream_host variable here. if ngx.req.http_version() == 3 then @@ -926,10 +923,8 @@ end function _M.http_body_filter_phase() - tracer.new_span("apisix.phase.body_filter", tracer.kind.server) common_phase("body_filter") common_phase("delayed_body_filter") - tracer.finish_current_span() end diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index a1fd29736de4..ed6638c59bf7 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -150,15 +150,11 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) local err if not radixtree_router or radixtree_router_ver ~= ssl_certificates.conf_version then - local span = tracer.new_span("create_router", tracer.kind.internal) radixtree_router, err = create_router(ssl_certificates.values) if not radixtree_router then - span:set_status(tracer.status.ERROR, "failed create router") - tracer.finish_current_span() return false, "failed to create radixtree router: " .. err end radixtree_router_ver = ssl_certificates.conf_version - tracer.finish_current_span() end local sni = alt_sni @@ -176,7 +172,6 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) tracer.start(api_ctx.ngx_ctx, "sni_radixtree_match", tracer.kind.internal) local sni_rev = sni:reverse() - local span = tracer.new_span("sni_radixtree_match", tracer.kind.internal) local ok = radixtree_router:dispatch(sni_rev, nil, api_ctx) if not ok then if not alt_sni then From 4edbbbbc7daa3340db140babe81160ace0c1cc4a Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 12:53:34 +0800 Subject: [PATCH 41/52] fix test --- apisix/secret.lua | 1 + t/plugin/opentelemetry3.t | 13 ------------- t/router/radixtree-sni2.t | 3 --- t/stream-node/sanity.t | 1 - t/stream-node/tls.t | 4 ---- 5 files changed, 1 insertion(+), 21 deletions(-) diff --git a/apisix/secret.lua b/apisix/secret.lua index 24a5e611aec3..72146c48cd17 100644 --- a/apisix/secret.lua +++ b/apisix/secret.lua @@ -29,6 +29,7 @@ local byte = string.byte local type = type local pcall = pcall local pairs = pairs +local ngx = ngx local _M = {} diff --git a/t/plugin/opentelemetry3.t b/t/plugin/opentelemetry3.t index 228f0f6d6763..ff7ea14e56e5 100644 --- a/t/plugin/opentelemetry3.t +++ b/t/plugin/opentelemetry3.t @@ -161,19 +161,6 @@ hello world qr/opentelemetry export span/ --- grep_error_log_out opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span -opentelemetry export span --- error_log eval qr/request log: \{.*"opentelemetry_context_traceparent":"00-\w{32}-\w{16}-01".*\}/ diff --git a/t/router/radixtree-sni2.t b/t/router/radixtree-sni2.t index bbc36a733d3d..79d23df8e232 100644 --- a/t/router/radixtree-sni2.t +++ b/t/router/radixtree-sni2.t @@ -611,9 +611,6 @@ ssl handshake: true qr/(fetch|release) table \w+/ --- grep_error_log_out fetch table api_ctx -fetch table opentelemetry_span -fetch table opentelemetry_span -fetch table opentelemetry_span release table api_ctx diff --git a/t/stream-node/sanity.t b/t/stream-node/sanity.t index c0fc0bd5b5b2..556c4bc9983a 100644 --- a/t/stream-node/sanity.t +++ b/t/stream-node/sanity.t @@ -394,7 +394,6 @@ hello world qr/(fetch|release) (ctx var|table \w+)/ --- grep_error_log_out fetch table api_ctx -fetch table opentelemetry_span fetch ctx var fetch table ctx_var fetch table plugins diff --git a/t/stream-node/tls.t b/t/stream-node/tls.t index 2c0003ef5823..13bdcba0f1f5 100644 --- a/t/stream-node/tls.t +++ b/t/stream-node/tls.t @@ -126,12 +126,8 @@ hello world qr/(fetch|release) table \w+/ --- grep_error_log_out fetch table api_ctx -fetch table opentelemetry_span -fetch table opentelemetry_span -fetch table opentelemetry_span release table api_ctx fetch table api_ctx -fetch table opentelemetry_span fetch table ctx_var fetch table plugins release table ctx_var From 1c3f90a3a53bfe486e4c0a9488dc0474f8a8f8ef Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 15:06:41 +0800 Subject: [PATCH 42/52] update md --- docs/en/latest/plugins/opentelemetry.md | 15 +++++++++++++++ docs/zh/latest/plugins/opentelemetry.md | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index 6891a377e273..ae758b60efb9 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -95,6 +95,21 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/opentelemetry -H "X-API- The examples below demonstrate how you can work with the `opentelemetry` Plugin for different scenarios. +### Enable Comprehensive Request Lifecycle Tracing + +:::note + +Enabling comprehensive tracing adds span creation and export overhead across the request lifecycle, which may impact throughput and latency. + +::: + +To enable comprehensive tracing across the request lifecycle (SSL/SNI, access, header/body filter, upstream, and logging), set the `tracing` field to `true` in the configuration file: + +```yaml title="config.yaml" +apisix: + tracing: true +``` + ### Enable `opentelemetry` Plugin By default, the `opentelemetry` Plugin is disabled in APISIX. To enable, add the Plugin to your configuration file as such: diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index 18f0566bd03d..1e595c2029b0 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -94,6 +94,21 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/opentelemetry -H "X-API- 以下示例展示了如何在不同场景下使用 `opentelemetry` 插件。 +### 启用全面的请求生命周期追踪 + +:::note + +开启全面追踪会在请求生命周期的各个阶段引入 span 的创建与上报开销,会对 APISIX 吞吐量和延迟产生影响。 + +::: + +要在请求生命周期的各个阶段(包括 SSL/SNI、access、header/body filter、upstream 以及 logging)启用全面追踪,请在配置文件中将 `tracing` 字段设置为 `true`: + +```yaml title="config.yaml" +apisix: + tracing: true +``` + ### 启用 opentelemetry 插件 默认情况下,APISIX 中的 `opentelemetry` 插件是禁用的。要启用它,请将插件添加到配置文件中,如下所示: From b2cc72a7550d621ce85b997b8a894ca4dd702f5f Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 15:52:49 +0800 Subject: [PATCH 43/52] update config.yaml.example --- conf/config.yaml.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 67fd190d872f..6cab072c9b7b 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -149,6 +149,9 @@ apisix: count: 512 # Cache size neg_ttl: 60 # Negative cache TTL neg_count: 512 # Negative cache size + # enable more detailed tracing information + tracing: false + nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only # effective if the master process runs with super-user privileges. From 7a26e4164f5d220e49e7f3a78df7240c6d5d26ad Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 16:14:55 +0800 Subject: [PATCH 44/52] update config.yaml.example and doc --- conf/config.yaml.example | 5 +++-- docs/en/latest/plugins/opentelemetry.md | 2 +- docs/zh/latest/plugins/opentelemetry.md | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 6cab072c9b7b..3cb3d95f0d10 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -149,8 +149,9 @@ apisix: count: 512 # Cache size neg_ttl: 60 # Negative cache TTL neg_count: 512 # Negative cache size - # enable more detailed tracing information - tracing: false + +tracing: false # Enable comprehensive request lifecycle tracing (SSL/SNI, rewrite, access, header_filter, body_filter, and log). + # When disabled, OpenTelemetry collects only a single span per request. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index ae758b60efb9..98cf87b17872 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -103,7 +103,7 @@ Enabling comprehensive tracing adds span creation and export overhead across the ::: -To enable comprehensive tracing across the request lifecycle (SSL/SNI, access, header/body filter, upstream, and logging), set the `tracing` field to `true` in the configuration file: +To enable comprehensive tracing across the request lifecycle (SSL/SNI, rewrite/access, header_filter/body_filter, and log), set the `tracing` field to `true` in the configuration file: ```yaml title="config.yaml" apisix: diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index 1e595c2029b0..a2bd7ff038fa 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -102,7 +102,7 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/opentelemetry -H "X-API- ::: -要在请求生命周期的各个阶段(包括 SSL/SNI、access、header/body filter、upstream 以及 logging)启用全面追踪,请在配置文件中将 `tracing` 字段设置为 `true`: +要在请求生命周期的各个阶段(包括 SSL/SNI、rewrite、access、header_filter、body_filter、log)启用全面追踪,请在配置文件中将 `tracing` 字段设置为 `true`: ```yaml title="config.yaml" apisix: From 359ddc4321cf4588abb605305dee2731457d5b4a Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 29 Jan 2026 16:15:43 +0800 Subject: [PATCH 45/52] fix --- conf/config.yaml.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 3cb3d95f0d10..4d78ae5af330 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -150,8 +150,8 @@ apisix: neg_ttl: 60 # Negative cache TTL neg_count: 512 # Negative cache size -tracing: false # Enable comprehensive request lifecycle tracing (SSL/SNI, rewrite, access, header_filter, body_filter, and log). - # When disabled, OpenTelemetry collects only a single span per request. + tracing: false # Enable comprehensive request lifecycle tracing (SSL/SNI, rewrite, access, header_filter, body_filter, and log). + # When disabled, OpenTelemetry collects only a single span per request. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only From b9f3215718ab8f5d1cf7da2c504e4293e54d3397 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Tue, 3 Feb 2026 08:46:08 +0800 Subject: [PATCH 46/52] refactor v3 --- apisix/init.lua | 22 ++++----- apisix/plugin.lua | 8 +-- apisix/secret.lua | 4 +- apisix/ssl/router/radixtree_sni.lua | 4 +- apisix/tracer.lua | 70 ++++++++++++++++----------- apisix/utils/stack.lua | 75 ----------------------------- apisix/utils/upstream.lua | 4 +- 7 files changed, 62 insertions(+), 125 deletions(-) delete mode 100644 apisix/utils/stack.lua diff --git a/apisix/init.lua b/apisix/init.lua index 1f682414d469..e657623aa776 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -206,7 +206,7 @@ function _M.ssl_client_hello_phase() ngx_ctx.api_ctx = api_ctx api_ctx.ngx_ctx = ngx_ctx - tracer.start(ngx_ctx, "ssl_client_hello_phase", tracer.kind.server) + local span = tracer.start(ngx_ctx, "ssl_client_hello_phase", tracer.kind.server) local ok, err = router.router_ssl.match_and_set(api_ctx, true, sni) @@ -220,21 +220,21 @@ function _M.ssl_client_hello_phase() core.log.error("failed to fetch ssl config: ", err) end core.log.error("failed to match any SSL certificate by SNI: ", sni) - tracer.finish(ngx_ctx, tracer.status.ERROR, "failed match SNI") + tracer.finish(ngx_ctx, span, tracer.status.ERROR, "failed match SNI") ngx_exit(-1) end ok, err = apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols) if not ok then core.log.error("failed to set ssl protocols: ", err) - tracer.finish(ngx_ctx, tracer.status.ERROR, "failed set protocols") + tracer.finish(ngx_ctx, span, tracer.status.ERROR, "failed set protocols") ngx_exit(-1) end -- in stream subsystem, ngx.ssl.server_name() return hostname of ssl session in preread phase, -- so that we can't get real SNI without recording it in ngx.ctx during client_hello phase ngx.ctx.client_hello_sni = sni - tracer.finish(ngx_ctx) + tracer.finish(ngx_ctx, span) end @@ -688,7 +688,7 @@ function _M.http_access_phase() core.ctx.set_vars_meta(api_ctx) - tracer.start(ngx_ctx, "apisix.phase.access", tracer.kind.server) + local span = tracer.start(ngx_ctx, "apisix.phase.access", tracer.kind.server) if not verify_https_client(api_ctx) then return core.response.exit(400) @@ -727,12 +727,12 @@ function _M.http_access_phase() handle_x_forwarded_headers(api_ctx) - tracer.start(ngx_ctx, "http_router_match", tracer.kind.internal) + local match_span = tracer.start(ngx_ctx, "http_router_match", tracer.kind.internal) router.router_http.match(api_ctx) local route = api_ctx.matched_route if not route then - tracer.finish(ngx.ctx, tracer.status.ERROR, "no matched route") + tracer.finish(ngx.ctx, match_span, tracer.status.ERROR, "no matched route") -- run global rule when there is no matching route local global_rules, conf_version = apisix_global_rules.global_rules() plugin.run_global_rules(api_ctx, global_rules, conf_version, nil) @@ -741,7 +741,7 @@ function _M.http_access_phase() return core.response.exit(404, {error_msg = "404 Route Not Found"}) end - tracer.finish(ngx_ctx) + tracer.finish(ngx_ctx, match_span) core.log.info("matched route: ", core.json.delay_encode(api_ctx.matched_route, true)) @@ -833,7 +833,7 @@ function _M.http_access_phase() end plugin.run_plugin("access", plugins, api_ctx) end - tracer.finish(ngx_ctx) + tracer.finish(ngx_ctx, span) _M.handle_upstream(api_ctx, route, enable_websocket) @@ -893,7 +893,7 @@ end function _M.http_header_filter_phase() local ngx_ctx = ngx.ctx - tracer.start(ngx_ctx, "apisix.phase.header_filter", tracer.kind.server) + local span = tracer.start(ngx_ctx, "apisix.phase.header_filter", tracer.kind.server) core.response.set_header("Server", ver_header) local up_status = get_var("upstream_status") @@ -916,7 +916,7 @@ function _M.http_header_filter_phase() end core.response.set_header("Apisix-Plugins", core.table.concat(deduplicate, ", ")) end - tracer.finish(ngx_ctx) + tracer.finish(ngx_ctx, span) tracer.start(ngx_ctx, "apisix.phase.body_filter", tracer.kind.server) end diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 380d86efda6f..9e640aa0d750 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -1229,10 +1229,10 @@ function _M.run_plugin(phase, plugins, api_ctx) plugin_run = true run_meta_pre_function(conf, api_ctx, plugins[i]["name"]) api_ctx._plugin_name = plugins[i]["name"] - tracer.start(api_ctx.ngx_ctx, "apisix.phase." .. phase + local span = tracer.start(api_ctx.ngx_ctx, "apisix.phase." .. phase .. ".plugins." .. api_ctx._plugin_name) phase_func(conf, api_ctx) - tracer.finish(api_ctx.ngx_ctx) + tracer.finish(api_ctx.ngx_ctx, span) api_ctx._plugin_name = nil end end @@ -1305,7 +1305,7 @@ end function _M.run_global_rules(api_ctx, global_rules, conf_version, phase_name) if global_rules and #global_rules > 0 then - tracer.start(api_ctx.ngx_ctx, "run_global_rules", tracer.kind.internal) + local span = tracer.start(api_ctx.ngx_ctx, "run_global_rules", tracer.kind.internal) local orig_conf_type = api_ctx.conf_type local orig_conf_version = api_ctx.conf_version local orig_conf_id = api_ctx.conf_id @@ -1340,7 +1340,7 @@ function _M.run_global_rules(api_ctx, global_rules, conf_version, phase_name) api_ctx.conf_type = orig_conf_type api_ctx.conf_version = orig_conf_version api_ctx.conf_id = orig_conf_id - tracer.finish(api_ctx.ngx_ctx) + tracer.finish(api_ctx.ngx_ctx, span) end end diff --git a/apisix/secret.lua b/apisix/secret.lua index 72146c48cd17..d289a47dad27 100644 --- a/apisix/secret.lua +++ b/apisix/secret.lua @@ -150,7 +150,7 @@ local function fetch_by_uri_secret(secret_uri) return nil, "no secret conf, secret_uri: " .. secret_uri end - tracer.start(ngx.ctx, "fetch_secret", tracer.kind.client) + local span = tracer.start(ngx.ctx, "fetch_secret", tracer.kind.client) local ok, sm = pcall(require, "apisix.secret." .. opts.manager) if not ok then return nil, "no secret manager: " .. opts.manager @@ -162,7 +162,7 @@ local function fetch_by_uri_secret(secret_uri) return nil, err end - tracer.finish(ngx.ctx) + tracer.finish(ngx.ctx, span) return value end diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index ed6638c59bf7..c1d4f6c410f9 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -170,7 +170,7 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) core.log.debug("sni: ", sni) - tracer.start(api_ctx.ngx_ctx, "sni_radixtree_match", tracer.kind.internal) + local span = tracer.start(api_ctx.ngx_ctx, "sni_radixtree_match", tracer.kind.internal) local sni_rev = sni:reverse() local ok = radixtree_router:dispatch(sni_rev, nil, api_ctx) if not ok then @@ -182,7 +182,7 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) tracer.finish(api_ctx.ngx_ctx, tracer.status.ERROR, "failed match SNI") return false end - tracer.finish(api_ctx.ngx_ctx) + tracer.finish(api_ctx.ngx_ctx, span) if api_ctx.matched_sni == "*" then -- wildcard matches everything, no need for further validation diff --git a/apisix/tracer.lua b/apisix/tracer.lua index 459244f2195b..b6d57b707216 100644 --- a/apisix/tracer.lua +++ b/apisix/tracer.lua @@ -42,9 +42,13 @@ function _M.start(ctx, name, kind) local tracing = ctx and ctx.tracing if not tracing then + local root_span = span.new() tracing = tablepool.fetch("tracing", 0, 8) - tracing.context = stack.new() tracing.spans = tablepool.fetch("tracing_spans", 20, 0) + tracing.root_span = root_span + tracing.current_span = root_span + table.insert(tracing.spans, root_span) + root_span.id = 1 ctx.tracing = tracing end if tracing.skip then @@ -52,26 +56,28 @@ function _M.start(ctx, name, kind) end local spans = tracing.spans - local context = tracing.context - - table.insert(spans, span.new(name, kind)) - local idx = #spans - - if not context:is_empty() then - local parent_idx = context:peek() - local parent = spans[parent_idx] - parent:append_child(idx) + local sp = span.new(name, kind) + + table.insert(spans, sp) + local id = #spans + sp.id = id + local parent = tracing.current_span + if parent then + sp:set_parent(parent.id) + parent:append_child(id) end - context:push(idx) + tracing.current_span = sp + return sp end -local function finish_span(tracing, code, message) - local sp_idx = tracing.context:pop() - if not sp_idx then +local function finish_span(spans, sp, code, message) + if not sp or sp.end_time then return end - local sp = tracing.spans[sp_idx] + for _, id in ipairs(sp.child_ids or {}) do + finish_span(spans, spans[id]) + end if code then sp:set_status(code, message) end @@ -79,23 +85,19 @@ local function finish_span(tracing, code, message) end -function _M.finish(ctx, code, message) +function _M.finish(ctx, sp, code, message) local tracing = ctx and ctx.tracing if not tracing then return end - finish_span(tracing, code, message) -end - -function _M.finish_all(ctx, code, message) - local tracing = ctx and ctx.tracing - if not tracing then + sp = sp or tracing.current_span + if not sp then return end - while not tracing.context:is_empty() do - finish_span(tracing, code, message) - end + + finish_span(tracing.spans, sp, code, message) + tracing.current_span = tracing.spans[sp.parent_id] end @@ -105,10 +107,6 @@ function _M.release(ctx) return end - while not tracing.context:is_empty() do - finish_span(tracing) - end - for _, sp in ipairs(tracing.spans) do sp:release() end @@ -117,4 +115,18 @@ function _M.release(ctx) end +function _M.finish_all(ctx, code, message) + local tracing = ctx and ctx.tracing + if not tracing then + return + end + + local spans = tracing.spans + tracing.current_span = tracing.root_span + for _, id in ipairs(tracing.root_span.child_ids or {}) do + finish_span(spans, spans[id], code, message) + end +end + + return _M diff --git a/apisix/utils/stack.lua b/apisix/utils/stack.lua deleted file mode 100644 index af4e6a066831..000000000000 --- a/apisix/utils/stack.lua +++ /dev/null @@ -1,75 +0,0 @@ --- --- Licensed to the Apache Software Foundation (ASF) under one or more --- contributor license agreements. See the NOTICE file distributed with --- this work for additional information regarding copyright ownership. --- The ASF licenses this file to You under the Apache License, Version 2.0 --- (the "License"); you may not use this file except in compliance with --- the License. You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- -local _M = {} -local mt = { __index = _M } -local setmetatable = setmetatable -local table = require("apisix.core.table") - - -function _M.new() - local self = { - _data = {}, - _n = 0, - } - return setmetatable(self, mt) -end - - -function _M.push(self, value) - self._n = self._n + 1 - self._data[self._n] = value -end - - -function _M.pop(self) - if self._n == 0 then - return nil - end - - local value = self._data[self._n] - self._data[self._n] = nil - self._n = self._n - 1 - return value -end - - -function _M.peek(self) - if self._n == 0 then - return nil - end - - return self._data[self._n] -end - - -function _M.is_empty(self) - return self._n == 0 -end - - -function _M.size(self) - return self._n -end - - -function _M.clear(self) - table.clear(self._data) - self._n = 0 -end - - -return _M diff --git a/apisix/utils/upstream.lua b/apisix/utils/upstream.lua index 709dbff90561..f94fdb531d59 100644 --- a/apisix/utils/upstream.lua +++ b/apisix/utils/upstream.lua @@ -81,7 +81,7 @@ _M.compare_upstream_node = compare_upstream_node local function parse_domain_for_nodes(nodes) - tracer.start(ngx.ctx, "resolve_dns", tracer.kind.internal) + local span = tracer.start(ngx.ctx, "resolve_dns", tracer.kind.internal) local new_nodes = core.table.new(#nodes, 0) for _, node in ipairs(nodes) do local host = node.host @@ -102,7 +102,7 @@ local function parse_domain_for_nodes(nodes) core.table.insert(new_nodes, node) end end - tracer.finish(ngx.ctx) + tracer.finish(ngx.ctx, span) return new_nodes end _M.parse_domain_for_nodes = parse_domain_for_nodes From d3f9bd4586a9e9f3c82c038bb38b4bd6b643daeb Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Wed, 4 Feb 2026 08:59:27 +0800 Subject: [PATCH 47/52] fix test --- apisix/plugins/opentelemetry.lua | 20 +-- apisix/tracer.lua | 1 - apisix/utils/span.lua | 8 +- docs/en/latest/plugins/opentelemetry.md | 209 ++++++++++-------------- docs/zh/latest/plugins/opentelemetry.md | 209 ++++++++++-------------- 5 files changed, 190 insertions(+), 257 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 96cb168f045f..0d42d0fecc3f 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -388,8 +388,7 @@ function _M.rewrite(conf, api_ctx) end -local function create_child_span(tracer, parent_span_ctx, spans, span_idx) - local span = spans[span_idx] +local function create_child_span(tracer, parent_span_ctx, spans, span) if not span or span.finished then return end @@ -402,7 +401,7 @@ local function create_child_span(tracer, parent_span_ctx, spans, span_idx) new_span.start_time = span.start_time for _, idx in ipairs(span.child_ids or {}) do - create_child_span(tracer, new_span_ctx, spans, idx) + create_child_span(tracer, new_span_ctx, spans, spans[idx]) end if span.status then new_span:set_status(span.status.code, span.status.message) @@ -443,8 +442,10 @@ local function inject_core_spans(root_span_ctx, api_ctx, conf) return end span.start_time = tracing.spans[1].start_time - for i, _ in ipairs(tracing.spans or {}) do - create_child_span(tracer, root_span_ctx, tracing.spans, i) + local root_span = tracing.root_span + local spans = tracing.spans + for _, idx in ipairs(root_span.child_ids or {}) do + create_child_span(tracer, root_span_ctx, spans, spans[idx]) end end @@ -463,15 +464,10 @@ function _M.log(conf, api_ctx) end inject_core_spans(ctx, api_ctx, conf) - span:set_attributes(attr.int("http.status_code", upstream_status)) + span:set_attributes(attr.int("http.status_code", upstream_status), + attr.int("http.response.status_code", upstream_status)) update_time() span:finish() - if ngx.ctx._apisix_spans then - for _, sp in ipairs(ngx.ctx._apisix_spans) do - sp:release() - end - ngx.ctx._apisix_spans = nil - end end end diff --git a/apisix/tracer.lua b/apisix/tracer.lua index b6d57b707216..1e6b4e5c2288 100644 --- a/apisix/tracer.lua +++ b/apisix/tracer.lua @@ -16,7 +16,6 @@ -- local table = require("apisix.core.table") local tablepool = require("tablepool") -local stack = require("apisix.utils.stack") local span = require("apisix.utils.span") local span_kind = require("opentelemetry.trace.span_kind") local span_status = require("opentelemetry.trace.span_status") diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index 1b3f2ef675ac..a4f67a19df09 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -41,7 +41,6 @@ function _M.new(name, kind) self.start_time = get_time() self.name = name self.kind = kind - self.end_time = 0 self.status = nil self.dead = false return setmetatable(self, mt) @@ -50,12 +49,17 @@ end function _M.append_child(self, child_id) if not self.child_ids then - self.child_ids = {} + self.child_ids = table.new(10, 0) end table.insert(self.child_ids, child_id) end +function _M.set_parent(self, parent_id) + self.parent_id = parent_id +end + + function _M.release(self) tablepool.release(pool_name, self) end diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index 98cf87b17872..ce0f7ba28236 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -167,153 +167,120 @@ You should receive an `HTTP/1.1 200 OK` response. In OpenTelemetry collector's log, you should see information similar to the following: ```text -2024-02-18T17:14:03.825Z info ResourceSpans #0 +info ResourceSpans #0 +Resource SchemaURL: +Resource attributes: + -> telemetry.sdk.language: Str(lua) + -> telemetry.sdk.name: Str(opentelemetry-lua) + -> telemetry.sdk.version: Str(0.1.1) + -> hostname: Str(RC) + -> service.name: Str(APISIX) ScopeSpans #0 -ScopeSpans SchemaURL: -InstrumentationScope opentelemetry-lua +ScopeSpans SchemaURL: +InstrumentationScope opentelemetry-lua Span #0 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 5a3835b61110d942 + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : b125e88a4ed7e409 + ID : 5e1d6b61c31220c9 Name : http_router_match Kind : Internal - Start time : 2025-10-24 06:58:04.430430976 +0000 UTC - End time : 2025-10-24 06:58:04.431542016 +0000 UTC + Start time : 2026-02-04 00:55:09.468541952 +0000 UTC + End time : 2026-02-04 00:55:09.468577024 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #1 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 4ab25e2b92f394e1 - Name : resolve_dns - Kind : Internal - Start time : 2025-10-24 06:58:04.432521984 +0000 UTC - End time : 2025-10-24 06:58:04.44903296 +0000 UTC + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : b125e88a4ed7e409 + Name : apisix.phase.access + Kind : Server + Start time : 2026-02-04 00:55:09.468467968 +0000 UTC + End time : 2026-02-04 00:55:09.469076992 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #2 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 3620c0f05dd2be4f - Name : apisix.phase.header_filter + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : f603a09f54520f8b + Name : resolve_dns Kind : Internal - Start time : 2025-10-24 06:58:06.960481024 +0000 UTC - End time : 2025-10-24 06:58:06.960510976 +0000 UTC + Start time : 2026-02-04 00:55:09.469084928 +0000 UTC + End time : 2026-02-04 00:55:09.469104896 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #3 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 4c5f3476f62a7e8a - ID : a9bfad7bb6986e41 - Name : apisix.phase.body_filter - Kind : Internal - Start time : 2025-10-24 06:58:06.960579072 +0000 UTC - End time : 2025-10-24 06:58:06.96059008 +0000 UTC + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : 303694d8fc044ab2 + Name : apisix.phase.header_filter + Kind : Server + Start time : 2026-02-04 00:55:09.470095872 +0000 UTC + End time : 2026-02-04 00:55:09.470138112 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #4 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : b2994675df6baa83 - ID : 26705f9c47584a5b - Name : apisix.phase.delayed_body_filter.opentelemetry - Kind : Internal - Start time : 2025-10-24 06:58:06.960613888 +0000 UTC - End time : 2025-10-24 06:58:06.960687104 +0000 UTC - Status code : Unset - Status message : -Span #5 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 4c5f3476f62a7e8a - ID : b2994675df6baa83 - Name : apisix.phase.delayed_body_filter - Kind : Internal - Start time : 2025-10-24 06:58:06.96059904 +0000 UTC - End time : 2025-10-24 06:58:06.960692992 +0000 UTC - Status code : Unset - Status message : -Span #6 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 4c5f3476f62a7e8a + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : 078f82f64325c684 Name : apisix.phase.body_filter Kind : Server - Start time : 2025-10-24 06:58:06.96056704 +0000 UTC - End time : 2025-10-24 06:58:06.960698112 +0000 UTC - Status code : Unset - Status message : -Span #7 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 2024d73d32cbd81b - ID : 223c64fb691a24e8 - Name : apisix.phase.body_filter - Kind : Internal - Start time : 2025-10-24 06:58:06.961624064 +0000 UTC - End time : 2025-10-24 06:58:06.961635072 +0000 UTC + Start time : 2026-02-04 00:55:09.470141952 +0000 UTC + End time : 2026-02-04 00:55:09.47021696 +0000 UTC Status code : Unset - Status message : -Span #8 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : fd193dd24c618f60 - ID : 8729ad6e0d94a23b - Name : apisix.phase.delayed_body_filter.opentelemetry - Kind : Internal - Start time : 2025-10-24 06:58:06.961648896 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC - Status code : Unset - Status message : -Span #9 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 2024d73d32cbd81b - ID : fd193dd24c618f60 - Name : apisix.phase.delayed_body_filter + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 +Span #5 + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : fb6fba6b28496598 + Name : apisix.phase.log.plugins.opentelemetry Kind : Internal - Start time : 2025-10-24 06:58:06.961641984 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC + Start time : 2026-02-04 00:55:09.470256896 +0000 UTC + End time : 2026-02-04 00:55:09.479666944 +0000 UTC Status code : Unset - Status message : -Span #10 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 2024d73d32cbd81b - Name : apisix.phase.body_filter - Kind : Server - Start time : 2025-10-24 06:58:06.960980992 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC - Status code : Unset - Status message : -Span #11 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : cfb0b4603dc2e385 - ID : 905f850f13e32bfb - Name : apisix.phase.access - Kind : Server - Start time : 2025-10-24 06:58:04.427932928 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC - Status code : Unset - Status message : -Span #12 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : - ID : cfb0b4603dc2e385 - Name : GET /headers + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 +Span #6 + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : + ID : 8b00441884e3908f + Name : GET /anything Kind : Server - Start time : 2025-10-24 06:58:04.432427008 +0000 UTC - End time : 2025-10-24 06:58:06.962299904 +0000 UTC + Start time : 2026-02-04 00:55:09.468459008 +0000 UTC + End time : 2026-02-04 00:55:09.480956928 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Attributes: - -> net.host.name: Str(127.0.0.1) + -> net.host.name: Str(localhost) -> http.method: Str(GET) -> http.scheme: Str(http) - -> http.target: Str(/headers) - -> http.user_agent: Str(curl/8.16.0) + -> http.target: Str(/anything) + -> http.user_agent: Str(curl/7.81.0) -> apisix.route_id: Str(otel-tracing-route) -> apisix.route_name: Empty() - -> http.route: Str(/headers) + -> http.route: Str(/anything) -> http.status_code: Int(200) -{"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} -2025-10-24T06:58:13.893Z info Metrics {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 25, "data points": 26} -2025-10-24T06:58:13.893Z info ResourceMetrics #0 + -> http.response.status_code: Int(200) + {"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} ``` To visualize these traces, you can export your telemetry to backend Services, such as Zipkin and Prometheus. See [exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter) for more details. diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index a2bd7ff038fa..49c09b07d02a 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -166,153 +166,120 @@ curl "http://127.0.0.1:9080/anything" 在 OpenTelemetry collector 的日志中,你应该看到类似以下的信息: ```text -2024-02-18T17:14:03.825Z info ResourceSpans #0 +info ResourceSpans #0 +Resource SchemaURL: +Resource attributes: + -> telemetry.sdk.language: Str(lua) + -> telemetry.sdk.name: Str(opentelemetry-lua) + -> telemetry.sdk.version: Str(0.1.1) + -> hostname: Str(RC) + -> service.name: Str(APISIX) ScopeSpans #0 -ScopeSpans SchemaURL: -InstrumentationScope opentelemetry-lua +ScopeSpans SchemaURL: +InstrumentationScope opentelemetry-lua Span #0 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 5a3835b61110d942 + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : b125e88a4ed7e409 + ID : 5e1d6b61c31220c9 Name : http_router_match Kind : Internal - Start time : 2025-10-24 06:58:04.430430976 +0000 UTC - End time : 2025-10-24 06:58:04.431542016 +0000 UTC + Start time : 2026-02-04 00:55:09.468541952 +0000 UTC + End time : 2026-02-04 00:55:09.468577024 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #1 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 4ab25e2b92f394e1 - Name : resolve_dns - Kind : Internal - Start time : 2025-10-24 06:58:04.432521984 +0000 UTC - End time : 2025-10-24 06:58:04.44903296 +0000 UTC + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : b125e88a4ed7e409 + Name : apisix.phase.access + Kind : Server + Start time : 2026-02-04 00:55:09.468467968 +0000 UTC + End time : 2026-02-04 00:55:09.469076992 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #2 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 3620c0f05dd2be4f - Name : apisix.phase.header_filter + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : f603a09f54520f8b + Name : resolve_dns Kind : Internal - Start time : 2025-10-24 06:58:06.960481024 +0000 UTC - End time : 2025-10-24 06:58:06.960510976 +0000 UTC + Start time : 2026-02-04 00:55:09.469084928 +0000 UTC + End time : 2026-02-04 00:55:09.469104896 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #3 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 4c5f3476f62a7e8a - ID : a9bfad7bb6986e41 - Name : apisix.phase.body_filter - Kind : Internal - Start time : 2025-10-24 06:58:06.960579072 +0000 UTC - End time : 2025-10-24 06:58:06.96059008 +0000 UTC + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : 303694d8fc044ab2 + Name : apisix.phase.header_filter + Kind : Server + Start time : 2026-02-04 00:55:09.470095872 +0000 UTC + End time : 2026-02-04 00:55:09.470138112 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Span #4 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : b2994675df6baa83 - ID : 26705f9c47584a5b - Name : apisix.phase.delayed_body_filter.opentelemetry - Kind : Internal - Start time : 2025-10-24 06:58:06.960613888 +0000 UTC - End time : 2025-10-24 06:58:06.960687104 +0000 UTC - Status code : Unset - Status message : -Span #5 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 4c5f3476f62a7e8a - ID : b2994675df6baa83 - Name : apisix.phase.delayed_body_filter - Kind : Internal - Start time : 2025-10-24 06:58:06.96059904 +0000 UTC - End time : 2025-10-24 06:58:06.960692992 +0000 UTC - Status code : Unset - Status message : -Span #6 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 4c5f3476f62a7e8a + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : 078f82f64325c684 Name : apisix.phase.body_filter Kind : Server - Start time : 2025-10-24 06:58:06.96056704 +0000 UTC - End time : 2025-10-24 06:58:06.960698112 +0000 UTC - Status code : Unset - Status message : -Span #7 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 2024d73d32cbd81b - ID : 223c64fb691a24e8 - Name : apisix.phase.body_filter - Kind : Internal - Start time : 2025-10-24 06:58:06.961624064 +0000 UTC - End time : 2025-10-24 06:58:06.961635072 +0000 UTC + Start time : 2026-02-04 00:55:09.470141952 +0000 UTC + End time : 2026-02-04 00:55:09.47021696 +0000 UTC Status code : Unset - Status message : -Span #8 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : fd193dd24c618f60 - ID : 8729ad6e0d94a23b - Name : apisix.phase.delayed_body_filter.opentelemetry - Kind : Internal - Start time : 2025-10-24 06:58:06.961648896 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC - Status code : Unset - Status message : -Span #9 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 2024d73d32cbd81b - ID : fd193dd24c618f60 - Name : apisix.phase.delayed_body_filter + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 +Span #5 + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : 8b00441884e3908f + ID : fb6fba6b28496598 + Name : apisix.phase.log.plugins.opentelemetry Kind : Internal - Start time : 2025-10-24 06:58:06.961641984 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC + Start time : 2026-02-04 00:55:09.470256896 +0000 UTC + End time : 2026-02-04 00:55:09.479666944 +0000 UTC Status code : Unset - Status message : -Span #10 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : 905f850f13e32bfb - ID : 2024d73d32cbd81b - Name : apisix.phase.body_filter - Kind : Server - Start time : 2025-10-24 06:58:06.960980992 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC - Status code : Unset - Status message : -Span #11 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : cfb0b4603dc2e385 - ID : 905f850f13e32bfb - Name : apisix.phase.access - Kind : Server - Start time : 2025-10-24 06:58:04.427932928 +0000 UTC - End time : 1970-01-01 00:00:00 +0000 UTC - Status code : Unset - Status message : -Span #12 - Trace ID : 95a1644afaaf65e1f0193b1f193b990a - Parent ID : - ID : cfb0b4603dc2e385 - Name : GET /headers + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 +Span #6 + Trace ID : 6cd24a1d178f836f43de423a45ead766 + Parent ID : + ID : 8b00441884e3908f + Name : GET /anything Kind : Server - Start time : 2025-10-24 06:58:04.432427008 +0000 UTC - End time : 2025-10-24 06:58:06.962299904 +0000 UTC + Start time : 2026-02-04 00:55:09.468459008 +0000 UTC + End time : 2026-02-04 00:55:09.480956928 +0000 UTC Status code : Unset - Status message : + Status message : + DroppedAttributesCount: 0 + DroppedEventsCount: 0 + DroppedLinksCount: 0 Attributes: - -> net.host.name: Str(127.0.0.1) + -> net.host.name: Str(localhost) -> http.method: Str(GET) -> http.scheme: Str(http) - -> http.target: Str(/headers) - -> http.user_agent: Str(curl/8.16.0) + -> http.target: Str(/anything) + -> http.user_agent: Str(curl/7.81.0) -> apisix.route_id: Str(otel-tracing-route) -> apisix.route_name: Empty() - -> http.route: Str(/headers) + -> http.route: Str(/anything) -> http.status_code: Int(200) -{"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} -2025-10-24T06:58:13.893Z info Metrics {"resource": {"service.instance.id": "5006c483-d64c-4d1d-87ac-edb037ba3669", "service.name": "otelcol-contrib", "service.version": "0.138.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 25, "data points": 26} -2025-10-24T06:58:13.893Z info ResourceMetrics #0 + -> http.response.status_code: Int(200) + {"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} ``` 要可视化这些追踪,你可以将 traces 导出到后端服务,例如 Zipkin 和 Prometheus。有关更多详细信息,请参阅[exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter)。 From 03e50dc045bcc3471384562016df9052bede9768 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Wed, 4 Feb 2026 09:12:54 +0800 Subject: [PATCH 48/52] update doc --- docs/en/latest/plugins/opentelemetry.md | 22 +++++++++++----------- docs/zh/latest/plugins/opentelemetry.md | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index ce0f7ba28236..bb17f9ef7388 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -168,7 +168,7 @@ In OpenTelemetry collector's log, you should see information similar to the foll ```text info ResourceSpans #0 -Resource SchemaURL: +Resource SchemaURL: Resource attributes: -> telemetry.sdk.language: Str(lua) -> telemetry.sdk.name: Str(opentelemetry-lua) @@ -176,8 +176,8 @@ Resource attributes: -> hostname: Str(RC) -> service.name: Str(APISIX) ScopeSpans #0 -ScopeSpans SchemaURL: -InstrumentationScope opentelemetry-lua +ScopeSpans SchemaURL: +InstrumentationScope opentelemetry-lua Span #0 Trace ID : 6cd24a1d178f836f43de423a45ead766 Parent ID : b125e88a4ed7e409 @@ -187,7 +187,7 @@ Span #0 Start time : 2026-02-04 00:55:09.468541952 +0000 UTC End time : 2026-02-04 00:55:09.468577024 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -200,7 +200,7 @@ Span #1 Start time : 2026-02-04 00:55:09.468467968 +0000 UTC End time : 2026-02-04 00:55:09.469076992 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -213,7 +213,7 @@ Span #2 Start time : 2026-02-04 00:55:09.469084928 +0000 UTC End time : 2026-02-04 00:55:09.469104896 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -226,7 +226,7 @@ Span #3 Start time : 2026-02-04 00:55:09.470095872 +0000 UTC End time : 2026-02-04 00:55:09.470138112 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -239,7 +239,7 @@ Span #4 Start time : 2026-02-04 00:55:09.470141952 +0000 UTC End time : 2026-02-04 00:55:09.47021696 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -252,7 +252,7 @@ Span #5 Start time : 2026-02-04 00:55:09.470256896 +0000 UTC End time : 2026-02-04 00:55:09.479666944 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -265,7 +265,7 @@ Span #6 Start time : 2026-02-04 00:55:09.468459008 +0000 UTC End time : 2026-02-04 00:55:09.480956928 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -280,7 +280,7 @@ Attributes: -> http.route: Str(/anything) -> http.status_code: Int(200) -> http.response.status_code: Int(200) - {"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +{"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} ``` To visualize these traces, you can export your telemetry to backend Services, such as Zipkin and Prometheus. See [exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter) for more details. diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index 49c09b07d02a..89b2f261e880 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -167,7 +167,7 @@ curl "http://127.0.0.1:9080/anything" ```text info ResourceSpans #0 -Resource SchemaURL: +Resource SchemaURL: Resource attributes: -> telemetry.sdk.language: Str(lua) -> telemetry.sdk.name: Str(opentelemetry-lua) @@ -175,8 +175,8 @@ Resource attributes: -> hostname: Str(RC) -> service.name: Str(APISIX) ScopeSpans #0 -ScopeSpans SchemaURL: -InstrumentationScope opentelemetry-lua +ScopeSpans SchemaURL: +InstrumentationScope opentelemetry-lua Span #0 Trace ID : 6cd24a1d178f836f43de423a45ead766 Parent ID : b125e88a4ed7e409 @@ -186,7 +186,7 @@ Span #0 Start time : 2026-02-04 00:55:09.468541952 +0000 UTC End time : 2026-02-04 00:55:09.468577024 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -199,7 +199,7 @@ Span #1 Start time : 2026-02-04 00:55:09.468467968 +0000 UTC End time : 2026-02-04 00:55:09.469076992 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -212,7 +212,7 @@ Span #2 Start time : 2026-02-04 00:55:09.469084928 +0000 UTC End time : 2026-02-04 00:55:09.469104896 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -225,7 +225,7 @@ Span #3 Start time : 2026-02-04 00:55:09.470095872 +0000 UTC End time : 2026-02-04 00:55:09.470138112 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -238,7 +238,7 @@ Span #4 Start time : 2026-02-04 00:55:09.470141952 +0000 UTC End time : 2026-02-04 00:55:09.47021696 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -251,7 +251,7 @@ Span #5 Start time : 2026-02-04 00:55:09.470256896 +0000 UTC End time : 2026-02-04 00:55:09.479666944 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -264,7 +264,7 @@ Span #6 Start time : 2026-02-04 00:55:09.468459008 +0000 UTC End time : 2026-02-04 00:55:09.480956928 +0000 UTC Status code : Unset - Status message : + Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 @@ -279,7 +279,7 @@ Attributes: -> http.route: Str(/anything) -> http.status_code: Int(200) -> http.response.status_code: Int(200) - {"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +{"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} ``` 要可视化这些追踪,你可以将 traces 导出到后端服务,例如 Zipkin 和 Prometheus。有关更多详细信息,请参阅[exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter)。 From 9616ff50e7992bce91aba4e3ac056202090d829e Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Wed, 4 Feb 2026 09:14:37 +0800 Subject: [PATCH 49/52] update doc --- apisix/plugins/opentelemetry.lua | 8 +++ docs/en/latest/plugins/opentelemetry.md | 76 +++++++++++++------------ docs/zh/latest/plugins/opentelemetry.md | 76 +++++++++++++------------ 3 files changed, 88 insertions(+), 72 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 0d42d0fecc3f..2fed5bb90132 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -328,10 +328,18 @@ function _M.rewrite(conf, api_ctx) local attributes = { attr.string("net.host.name", vars.host), + -- deprecated attributes + -- https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/#deprecated-http-attributes attr.string("http.method", vars.method), attr.string("http.scheme", vars.scheme), attr.string("http.target", vars.request_uri), attr.string("http.user_agent", vars.http_user_agent), + + -- new attributes + attr.string("http.request.method", vars.method), + attr.string("url.scheme", vars.scheme), + attr.string("uri.path",vars.request_uri), + attr.string("user_agent.original", vars.http_user_agent), } if api_ctx.curr_req_matched then diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index bb17f9ef7388..e0ab3eb71b0e 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -179,91 +179,91 @@ ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope opentelemetry-lua Span #0 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : b125e88a4ed7e409 - ID : 5e1d6b61c31220c9 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0adf392b5c84111 + ID : d9816bbaef5ee63d Name : http_router_match Kind : Internal - Start time : 2026-02-04 00:55:09.468541952 +0000 UTC - End time : 2026-02-04 00:55:09.468577024 +0000 UTC + Start time : 2026-02-04 05:57:04.846881024 +0000 UTC + End time : 2026-02-04 05:57:04.846951936 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #1 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : b125e88a4ed7e409 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : d0adf392b5c84111 Name : apisix.phase.access Kind : Server - Start time : 2026-02-04 00:55:09.468467968 +0000 UTC - End time : 2026-02-04 00:55:09.469076992 +0000 UTC + Start time : 2026-02-04 05:57:04.846562048 +0000 UTC + End time : 2026-02-04 05:57:04.84724608 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #2 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : f603a09f54520f8b + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : 4eb72d55359331fa Name : resolve_dns Kind : Internal - Start time : 2026-02-04 00:55:09.469084928 +0000 UTC - End time : 2026-02-04 00:55:09.469104896 +0000 UTC + Start time : 2026-02-04 05:57:04.847251968 +0000 UTC + End time : 2026-02-04 05:57:04.84726912 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #3 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : 303694d8fc044ab2 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : de572aad9bad3b47 Name : apisix.phase.header_filter Kind : Server - Start time : 2026-02-04 00:55:09.470095872 +0000 UTC - End time : 2026-02-04 00:55:09.470138112 +0000 UTC + Start time : 2026-02-04 05:57:04.84793088 +0000 UTC + End time : 2026-02-04 05:57:04.848005888 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #4 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : 078f82f64325c684 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : 0baddeee6e5d500d Name : apisix.phase.body_filter Kind : Server - Start time : 2026-02-04 00:55:09.470141952 +0000 UTC - End time : 2026-02-04 00:55:09.47021696 +0000 UTC + Start time : 2026-02-04 05:57:04.848007936 +0000 UTC + End time : 2026-02-04 05:57:04.848103936 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #5 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : fb6fba6b28496598 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : d57d53882c40612a Name : apisix.phase.log.plugins.opentelemetry Kind : Internal - Start time : 2026-02-04 00:55:09.470256896 +0000 UTC - End time : 2026-02-04 00:55:09.479666944 +0000 UTC + Start time : 2026-02-04 05:57:04.84823296 +0000 UTC + End time : 2026-02-04 05:57:04.848385024 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #6 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : - ID : 8b00441884e3908f + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : + ID : d0c33adf97b099f3 Name : GET /anything Kind : Server - Start time : 2026-02-04 00:55:09.468459008 +0000 UTC - End time : 2026-02-04 00:55:09.480956928 +0000 UTC + Start time : 2026-02-04 05:57:04.84655488 +0000 UTC + End time : 2026-02-04 05:57:04.84839296 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 @@ -275,12 +275,16 @@ Attributes: -> http.scheme: Str(http) -> http.target: Str(/anything) -> http.user_agent: Str(curl/7.81.0) + -> http.request.method: Str(GET) + -> url.scheme: Str(http) + -> uri.path: Str(/anything) + -> user_agent.original: Str(curl/7.81.0) -> apisix.route_id: Str(otel-tracing-route) -> apisix.route_name: Empty() -> http.route: Str(/anything) -> http.status_code: Int(200) -> http.response.status_code: Int(200) -{"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +{"resource": {"service.instance.id": "ed436c1a-6ee7-46b0-ad58-527d0aaf4ade", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} ``` To visualize these traces, you can export your telemetry to backend Services, such as Zipkin and Prometheus. See [exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter) for more details. diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index 89b2f261e880..ec57697049c4 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -178,91 +178,91 @@ ScopeSpans #0 ScopeSpans SchemaURL: InstrumentationScope opentelemetry-lua Span #0 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : b125e88a4ed7e409 - ID : 5e1d6b61c31220c9 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0adf392b5c84111 + ID : d9816bbaef5ee63d Name : http_router_match Kind : Internal - Start time : 2026-02-04 00:55:09.468541952 +0000 UTC - End time : 2026-02-04 00:55:09.468577024 +0000 UTC + Start time : 2026-02-04 05:57:04.846881024 +0000 UTC + End time : 2026-02-04 05:57:04.846951936 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #1 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : b125e88a4ed7e409 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : d0adf392b5c84111 Name : apisix.phase.access Kind : Server - Start time : 2026-02-04 00:55:09.468467968 +0000 UTC - End time : 2026-02-04 00:55:09.469076992 +0000 UTC + Start time : 2026-02-04 05:57:04.846562048 +0000 UTC + End time : 2026-02-04 05:57:04.84724608 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #2 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : f603a09f54520f8b + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : 4eb72d55359331fa Name : resolve_dns Kind : Internal - Start time : 2026-02-04 00:55:09.469084928 +0000 UTC - End time : 2026-02-04 00:55:09.469104896 +0000 UTC + Start time : 2026-02-04 05:57:04.847251968 +0000 UTC + End time : 2026-02-04 05:57:04.84726912 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #3 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : 303694d8fc044ab2 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : de572aad9bad3b47 Name : apisix.phase.header_filter Kind : Server - Start time : 2026-02-04 00:55:09.470095872 +0000 UTC - End time : 2026-02-04 00:55:09.470138112 +0000 UTC + Start time : 2026-02-04 05:57:04.84793088 +0000 UTC + End time : 2026-02-04 05:57:04.848005888 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #4 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : 078f82f64325c684 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : 0baddeee6e5d500d Name : apisix.phase.body_filter Kind : Server - Start time : 2026-02-04 00:55:09.470141952 +0000 UTC - End time : 2026-02-04 00:55:09.47021696 +0000 UTC + Start time : 2026-02-04 05:57:04.848007936 +0000 UTC + End time : 2026-02-04 05:57:04.848103936 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #5 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : 8b00441884e3908f - ID : fb6fba6b28496598 + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : d0c33adf97b099f3 + ID : d57d53882c40612a Name : apisix.phase.log.plugins.opentelemetry Kind : Internal - Start time : 2026-02-04 00:55:09.470256896 +0000 UTC - End time : 2026-02-04 00:55:09.479666944 +0000 UTC + Start time : 2026-02-04 05:57:04.84823296 +0000 UTC + End time : 2026-02-04 05:57:04.848385024 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 DroppedEventsCount: 0 DroppedLinksCount: 0 Span #6 - Trace ID : 6cd24a1d178f836f43de423a45ead766 - Parent ID : - ID : 8b00441884e3908f + Trace ID : a5499493b517a3333578c2ac4fad3f4d + Parent ID : + ID : d0c33adf97b099f3 Name : GET /anything Kind : Server - Start time : 2026-02-04 00:55:09.468459008 +0000 UTC - End time : 2026-02-04 00:55:09.480956928 +0000 UTC + Start time : 2026-02-04 05:57:04.84655488 +0000 UTC + End time : 2026-02-04 05:57:04.84839296 +0000 UTC Status code : Unset Status message : DroppedAttributesCount: 0 @@ -274,12 +274,16 @@ Attributes: -> http.scheme: Str(http) -> http.target: Str(/anything) -> http.user_agent: Str(curl/7.81.0) + -> http.request.method: Str(GET) + -> url.scheme: Str(http) + -> uri.path: Str(/anything) + -> user_agent.original: Str(curl/7.81.0) -> apisix.route_id: Str(otel-tracing-route) -> apisix.route_name: Empty() -> http.route: Str(/anything) -> http.status_code: Int(200) -> http.response.status_code: Int(200) -{"resource": {"service.instance.id": "6f63899f-6fda-42db-a18b-b0892a77958a", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} +{"resource": {"service.instance.id": "ed436c1a-6ee7-46b0-ad58-527d0aaf4ade", "service.name": "otelcol-contrib", "service.version": "0.144.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} ``` 要可视化这些追踪,你可以将 traces 导出到后端服务,例如 Zipkin 和 Prometheus。有关更多详细信息,请参阅[exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter)。 From 9a006abd8e27ecddd9dc7341f828eb95a4e6ffec Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Wed, 4 Feb 2026 14:39:21 +0800 Subject: [PATCH 50/52] fix lint --- apisix/plugins/opentelemetry.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 2fed5bb90132..8e09887f271a 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -329,7 +329,6 @@ function _M.rewrite(conf, api_ctx) local attributes = { attr.string("net.host.name", vars.host), -- deprecated attributes - -- https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/#deprecated-http-attributes attr.string("http.method", vars.method), attr.string("http.scheme", vars.scheme), attr.string("http.target", vars.request_uri), From 460b316d7b9cc18a07dbc5efec413eee70821c6f Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Wed, 4 Feb 2026 14:49:20 +0800 Subject: [PATCH 51/52] fix finish span --- apisix/core/response.lua | 2 +- apisix/secret.lua | 2 +- apisix/ssl/router/radixtree_sni.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apisix/core/response.lua b/apisix/core/response.lua index 60f83762af36..ffc692eb869c 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -88,7 +88,7 @@ function resp_exit(code, ...) if code then if code >= 400 then - tracer.finish(ngx.ctx, tracer.status.ERROR, "response code " .. code) + tracer.finish_all(ngx.ctx, tracer.status.ERROR, "response code " .. code) end return ngx_exit(code) end diff --git a/apisix/secret.lua b/apisix/secret.lua index d289a47dad27..7ec526411675 100644 --- a/apisix/secret.lua +++ b/apisix/secret.lua @@ -158,7 +158,7 @@ local function fetch_by_uri_secret(secret_uri) local value, err = sm.get(conf, opts.key) if err then - tracer.finish(ngx.ctx, tracer.status.ERROR, err) + tracer.finish(ngx.ctx, span, tracer.status.ERROR, err) return nil, err end diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index c1d4f6c410f9..8736b608ecbe 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -179,7 +179,7 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) -- with it sometimes core.log.error("failed to find any SSL certificate by SNI: ", sni) end - tracer.finish(api_ctx.ngx_ctx, tracer.status.ERROR, "failed match SNI") + tracer.finish(api_ctx.ngx_ctx, span, tracer.status.ERROR, "failed match SNI") return false end tracer.finish(api_ctx.ngx_ctx, span) From 51ebadd7b0878e207545e34f455d7d7a381d3ca5 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Wed, 4 Feb 2026 16:22:24 +0800 Subject: [PATCH 52/52] udpate comments --- apisix/plugins/opentelemetry.lua | 2 +- apisix/tracer.lua | 3 +++ apisix/utils/span.lua | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua index 8e09887f271a..487c143819ce 100644 --- a/apisix/plugins/opentelemetry.lua +++ b/apisix/plugins/opentelemetry.lua @@ -337,7 +337,7 @@ function _M.rewrite(conf, api_ctx) -- new attributes attr.string("http.request.method", vars.method), attr.string("url.scheme", vars.scheme), - attr.string("uri.path",vars.request_uri), + attr.string("uri.path", vars.uri), attr.string("user_agent.original", vars.http_user_agent), } diff --git a/apisix/tracer.lua b/apisix/tracer.lua index 1e6b4e5c2288..efa8aa5eadd5 100644 --- a/apisix/tracer.lua +++ b/apisix/tracer.lua @@ -96,6 +96,9 @@ function _M.finish(ctx, sp, code, message) end finish_span(tracing.spans, sp, code, message) + if sp == tracing.root_span then + return + end tracing.current_span = tracing.spans[sp.parent_id] end diff --git a/apisix/utils/span.lua b/apisix/utils/span.lua index a4f67a19df09..d84fb347e08c 100644 --- a/apisix/utils/span.lua +++ b/apisix/utils/span.lua @@ -41,8 +41,6 @@ function _M.new(name, kind) self.start_time = get_time() self.name = name self.kind = kind - self.status = nil - self.dead = false return setmetatable(self, mt) end @@ -85,6 +83,9 @@ end function _M.set_attributes(self, ...) + if not self.attributes then + self.attributes = table.new(10, 0) + end local count = select('#', ...) for i = 1, count do local attr = select(i, ...)