This is an automated email from the ASF dual-hosted git repository.

shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 3586b2f80 fix(opentelemetry): preserve booleans, handle multi-value 
headers, tighten test (#13315)
3586b2f80 is described below

commit 3586b2f8051ca06ec7686511ed709ca9fa553a66
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Thu May 7 10:40:14 2026 +0800

    fix(opentelemetry): preserve booleans, handle multi-value headers, tighten 
test (#13315)
    
    Three follow-ups to #13146 (which started coercing additional_attributes
    values to strings before sending them to OTel attr.string):
    
    1. inject_attributes() gated insertion on `if val then`, which silently
       drops boolean false (Lua falsy) — regressing any user whose nginx
       variable resolves to a boolean false flag. Switch to
       `if val ~= nil then` so false survives.
    
    2. ngx.req.get_headers() returns a Lua table for multi-value headers
       (e.g. multiple Set-Cookie / X-Forwarded-For entries). tostring()
       over a table emits 'table: 0x...' and that's what was landing in
       the span attribute. Extract a coerce_attr_value() helper that joins
       multi-value entries with ', ' and skips when the source is missing
       entirely.
    
    3. TEST 22's response_body regex was qr/.*opentelemetry-lua.*/ which
       matches the OTLP exporter marker even when the numeric attributes
       are entirely absent from the export — i.e. the test would still
       pass with the regression #13146 was supposed to fix. Tighten it to
       assert each of request_time / bytes_sent appears as a stringValue.
       (upstream_response_time is intentionally not asserted: TEST 21
       serves /opentracing directly without proxying, so
       $upstream_response_time is nil and the plugin correctly omits the
       attribute.)
    
    Signed-off-by: Shreemaan Abhishek <[email protected]>
    Signed-off-by: Abhishek Choudhary <[email protected]>
---
 apisix/plugins/opentelemetry.lua | 29 ++++++++++++++++++++++++++---
 t/plugin/opentelemetry.t         |  7 ++++++-
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua
index 84bf6202e..2b99948a3 100644
--- a/apisix/plugins/opentelemetry.lua
+++ b/apisix/plugins/opentelemetry.lua
@@ -287,6 +287,22 @@ local function create_tracer_obj(conf, plugin_info)
 end
 
 
+-- Coerce a header/var value to a string suitable for an OTel string attribute.
+-- ngx.req.get_headers() returns a Lua table for multi-value headers — running
+-- `tostring()` over a table emits "table: 0x..." which is useless in a span,
+-- so join multi-value entries instead. Returns nil when the value cannot be
+-- represented (caller should skip the attribute in that case).
+local function coerce_attr_value(val)
+    if val == nil then
+        return nil
+    end
+    if type(val) == "table" then
+        return table.concat(val, ", ")
+    end
+    return tostring(val)
+end
+
+
 local function inject_attributes(attributes, wanted_attributes, source, 
with_prefix)
     for _, key in ipairs(wanted_attributes) do
         local is_key_a_match = #key >= 2 and key:byte(-1) == asterisk and 
with_prefix
@@ -295,13 +311,20 @@ local function inject_attributes(attributes, 
wanted_attributes, source, with_pre
             local prefix = key:sub(0, -2)
             for possible_key, value in pairs(source) do
                 if core.string.has_prefix(possible_key, prefix) then
-                    core.table.insert(attributes, attr.string(possible_key, 
tostring(value)))
+                    local coerced = coerce_attr_value(value)
+                    if coerced ~= nil then
+                        core.table.insert(attributes, 
attr.string(possible_key, coerced))
+                    end
                 end
             end
         else
+            -- ~= nil so boolean `false` survives instead of being silently 
dropped.
             local val = source[key]
-            if val then
-                core.table.insert(attributes, attr.string(key, tostring(val)))
+            if val ~= nil then
+                local coerced = coerce_attr_value(val)
+                if coerced ~= nil then
+                    core.table.insert(attributes, attr.string(key, coerced))
+                end
             end
         end
     end
diff --git a/t/plugin/opentelemetry.t b/t/plugin/opentelemetry.t
index 0063cda64..a260a62a2 100644
--- a/t/plugin/opentelemetry.t
+++ b/t/plugin/opentelemetry.t
@@ -484,10 +484,15 @@ opentracing
 
 
 === TEST 22: check span exported with numeric additional attributes
+# Asserts that numeric nginx variables land as `stringValue` (not intValue,
+# not dropped) once additional_attributes is configured for them.
+# upstream_response_time is intentionally not checked: this test serves
+# /opentracing directly, so $upstream_response_time is nil and the plugin
+# correctly omits the attribute. request_time and bytes_sent are always set.
 --- exec
 tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
 --- response_body eval
-qr/.*opentelemetry-lua.*/
+qr/.*opentelemetry-lua.*"key":"request_time","value":\{"stringValue":"[^"]+"\}.*"key":"bytes_sent","value":\{"stringValue":"[^"]+"\}.*/s
 
 
 

Reply via email to