Propagating Custom Request Headers to OTel Spans without modifying the OTel Plugin

Hi everyone,

I’m currently running Kong Gateway (v3.4.x, but planning for v3.6+) and using the OpenTelemetry (OTEL) plugin to send traces to Dynatrace.

The Challenge: We need to capture a specific custom header—x-dyntrace-test (used by NeoLoad for performance testing)—and include it as an attribute within the OpenTelemetry span.

According to Kong Support, the current OTEL plugin only supports standard tracing propagation headers (W3C, B3, etc.) and cannot be configured to “pluck” a custom, non-tracing header and inject it into the span attributes. A feature request has been filed (GTWY-I-1369), but we need a solution sooner.

The Constraint: I am looking for suggestions that do not involve modifying the core OTEL plugin code or writing a heavy custom plugin if possible.

Current Setup:

JSON

{
  "endpoint": "http://traceingest.dynatrace.svc.cluster.local:4318/v1/traces",
  "header_type": "w3c",
  "resource_attributes": {
    "service.name": "kong-gateway"
  }
}
  1. Has anyone successfully used the Serverless Post-Function (or Pre-Function) plugin to access the current span and manually add an attribute via the Lua SDK (kong.tracing)?

  2. Is there a way to leverage standard OpenTelemetry environment variables (like OTEL_RESOURCE_ATTRIBUTES) to dynamically pick up request headers, or is that strictly limited to static process-level data?

  3. Are there any clever “sidecar” patterns or OpenTelemetry Collector transformations that could intercept the traffic and map a header to an attribute before it hits the backend?

I’d love to hear how others are handling custom business/test metadata in their traces.

@Cherry_m Thank you for the excellent question. I’m unable to help you directly, but I passed your question through our Documentation AI Agent and pasted the response here. I hope this provides some potential clues to solutions. If you would like to interact with the agent directly please see the link near the top of the documentation home page: https://developer.konghq.com/ If you continue to have issues, please let me know and I can try and find some assistance.


Using Pre/Post-Function Plugin to Add Custom Header as Span Attribute

Your first approach — using the Serverless Pre-Function or Post-Function plugin — is the most directly supported solution based on the available Kong documentation.

Recommended Approach: Pre-Function Plugin in header_filter Phase

Kong Support explicitly documents this pattern for adding custom attributes to spans. Here’s how you can adapt it for your x-dyntrace-test header:

-- Capture the custom header and add it as a span attribute
local root_span = kong.tracing.active_span()
if root_span then
  local dyntrace_header = kong.request.get_header("x-dyntrace-test")
  if dyntrace_header then
    root_span:set_attribute("x-dyntrace-test", dyntrace_header)
  end
end

Apply this via the Pre-Function plugin in the header_filter phase (scoped globally or to the same service/route as your OTEL plugin): [add status code to spans]

plugins:
  - name: pre-function
    config:
      header_filter:
        - |
          local root_span = kong.tracing.active_span()
          if root_span then
            local dyntrace_header = kong.request.get_header("x-dyntrace-test")
            if dyntrace_header then
              root_span:set_attribute("x-dyntrace-test", dyntrace_header)
            end
          end

Alternatively, you can use the Post-Function plugin with a Lua file upload: [create a custom span]

curl -i -X POST http://localhost:8001/plugins \
  -F "name=post-function" \
  -F "config.access[1]=@custom-span.lua"

Regarding Your Other Questions

OTEL_RESOURCE_ATTRIBUTES / resource_attributes: The config.resource_attributes parameter in the OTEL plugin is for static, process-level resource attributes (e.g., service.name). It is not designed to dynamically pick up per-request headers. [resource attributes]

OTel Collector sidecar transformations: The knowledge sources do not contain information about using an OTel Collector processor to map request headers to span attributes. I cannot confirm or deny whether that pattern would work in your environment.

Summary

The Pre/Post-Function plugin approach is the documented, lightweight workaround that avoids modifying the core OTEL plugin. It works by accessing the active span via kong.tracing.active_span() and calling set_attribute() with your custom header value — exactly as Kong Support recommends for similar use cases. [add status code to spans]

Hey Cherry_m,

I’ve been exploring similar scenarios with Kong and OTEL, and one approach that worked for me was leveraging a small Post-Function Lua script to access the request headers and manually add them as span attributes via kong.tracing. You can inspect the current span and attach custom headers without touching the core OTEL plugin.

Another angle is using the OpenTelemetry Collector as a sidecar or gateway transformation layer—this lets you map custom headers like x-dyntrace-test to span attributes before they reach Dynatrace. It’s a bit more setup initially but keeps the plugin untouched and scalable.

For quick references and step-by-step examples, I often check resources like quiz answers today—they have practical tips that can save a lot of trial and error when working with headers and tracing.

Would love to hear if anyone has combined the Post-Function approach with Collector transformations for a fully automated pipeline.