Skip to main content

Overview

Kong AI Gateway (3.13 or later) is an API gateway with built-in AI proxy and OpenTelemetry support. Fiddler integrates with Kong at the gateway layer via Kong’s opentelemetry plugin, giving you full LLM observability — prompts, responses, token usage, latency — without adding any SDK to your application code.
Kong’s Gen AI OpenTelemetry tracing is labelled Tech Preview by Kong — “currently in Tech Preview and should not be used in a production environment.” This page’s tracing setup is built on that feature; re-verify after Kong upgrades. The current Kong Gateway release is 3.15.0.3 (as of 2026-08-19); the 3.13+ floor is the minimum that emits Gen AI span attributes.

Architecture

Kong exposes an OpenAI-compatible endpoint at /openai. Your application requires no SDK — it just calls Kong instead of the provider directly.

Prerequisites

  • Fiddler account with a GenAI application already created
  • A running Kong Gateway 3.13+ instance (Gen AI OTel attributes require 3.13+)
  • A valid LLM provider API key (e.g. OPENAI_API_KEY)
  • Your Fiddler API key (found under organizational settings) and application UUID (found under application settings)

Quick Start

Step 1: Create the Kong Configuration File

Save the following as kong_fiddler_config.yaml. Kong performs no shell-style ${VAR} substitution in its declarative config, so the ${...} placeholders below must be handled one of two ways. On fields Kong marks referenceable — such as ai-proxy’s auth.header_value — you can use a Kong Vault reference like {vault://env/OPENAI_KEY}, which Kong resolves from an environment variable at runtime (supported in declarative config on OSS and Enterprise alike, at no license cost). On non-referenceable fields you replace the placeholder with the real value in Step 2. The example uses a vault reference for the provider key and literal replacement for the rest.
service.name: kong in resource_attributes is how Fiddler routes Kong spans to its Kong mapper, and setting it is the documented default on every release. From 26.17, Fiddler also recognizes Kong’s kong-internal instrumentation scope; scope-based routing is evaluated first, so a Kong span is routed correctly even if a deployment overrides service.name. application.id is also required: a resource with no application.id is skipped — Fiddler logs a warning and increments a missing_application_id_failure counter, so the drop is visible in ingestion logs and metrics rather than silent.

Step 2: Replace the Placeholders With Your Values

Supply each value the way its field allows. Kong resolves a {vault://env/...} reference only on referenceable fields; on all other fields you replace the ${...} placeholder with the literal value before starting Kong. Because the Fiddler API key must be written in literally, the finished file contains a live secret: restrict its file permissions and inject it at deploy time from your secret manager rather than committing it. If any ${...} placeholder is left unreplaced, Kong fails to start with an error like 'traces_endpoint': missing host in url. Apply the config the way you already manage Kong — a DB-less declarative file, decK, the Admin API, or your Helm chart’s config.
If you already have a Kong declarative config, you do not need to replace your existing file. Copy just the three plugin entries (ai-proxy, pre-function, opentelemetry) and add them under your existing plugins: block. The services: and routes: blocks in the example above are only needed if you do not already have an OpenAI route configured.

Step 3: Enable Tracing on Kong

The opentelemetry plugin only emits spans if Kong’s tracing is enabled at the process level. These three settings cannot be set in the declarative config — set them wherever your Kong reads its configuration (kong.conf, KONG_* environment variables, or your Helm chart’s env: values): tracing_instrumentations and tracing_sampling_rate are what make Kong produce OTel spans at all — without them no spans are emitted regardless of the opentelemetry plugin config. untrusted_lua = on is required for the pre-function session-grouping plugin to run its inline Lua.

Step 4: Point Your Application at Kong

Traces appear in Fiddler automatically — no SDK import, no callback registration, no changes to your application logic. To override the default Kong URL, set KONG_URL (defaults to http://localhost:8000):

Step 5: Verify Traces Are Arriving

Open the Fiddler UI and navigate to your application’s Explorer. You should see the trace within a few seconds of making your first completion call.

Span Type Mapping

Fiddler classifies Kong spans based on the span name and gen_ai.operation.name. Kong 3.13 names LLM spans "{operation} {model}" (e.g. "chat gpt-5-mini"). All infrastructure spans start with "kong". Kong emits a span hierarchy per request: a root HTTP span (kong), routing and plugin spans (all starting with kong.), and the LLM Gen AI span (named "{operation} {model}"). Fiddler keeps only the spans it classifies — the LLM span, plus a fiddler-guardrail span if you run guardrails — and drops every other Kong-resource span. That includes infrastructure spans and any Gen AI operation outside chat, text_completion, and generate_content: an embeddings call through the same route is dropped with no separate signal. Kong is the only Fiddler integration that filters its own infrastructure spans this way. The forwarded LLM span carries gen_ai.conversation.id, so multi-turn calls sharing one conversation id group under a single Session.

Attribute Mapping

Kong follows the OTel Gen AI semantic conventions. Fiddler’s mapper normalizes these automatically:

Session Grouping

Each Kong request is its own trace, so without a shared gen_ai.conversation.id Fiddler falls back to trace_id.hex() and every LLM call shows up as a separate Session. To group a multi-turn conversation into one Session, the same gen_ai.conversation.id must be set on each call’s LLM span. Kong 3.13 has no native conversation-id field — it only supports W3C traceparent for distributed tracing. The Quick Start config above solves this with the built-in pre-function plugin (this is the approach Kong Support recommends for adding a custom attribute from a request header):
  • In the access phase it reads the X-Fiddler-Conversation-Id request header and stashes it in the per-request ngx.ctx.
  • In the header_filter phase it stamps that value as gen_ai.conversation.id on the request’s OTel spans, before the opentelemetry plugin exports them in the log phase.
Because only the LLM span is forwarded to Fiddler (Kong’s infrastructure spans are dropped), stamping in header_filter is sufficient: the LLM span already exists at that point, so it always receives the conversation id. There are no later-created infrastructure spans to worry about — they are discarded by Fiddler’s Kong mapper before reaching the UI. Your application just sends the same header value on every call in a conversation:
Why iterate ngx.ctx.KONG_SPANS instead of kong.tracing.active_span()? The documented kong.tracing.active_span() returns the root (kong) span, which Fiddler drops as infrastructure. Fiddler keeps only Kong’s Gen AI/LLM span, so the attribute must land on that span — iterating every span guarantees it does, regardless of which span is the LLM one. ngx.ctx.KONG_SPANS is an internal Kong field rather than a stable public API. This mechanism has no automated test coverage in Fiddler — no unit, fixture, or e2e case exercises the header-to-ngx.ctx-to-attribute path or the KONG_UNTRUSTED_LUA requirement — and Kong has no local Fiddler harness, so treat it as manually verified only and re-verify session grouping after every Kong upgrade. The pre-function plugin also requires KONG_UNTRUSTED_LUA=on and a sampling rate of 1.0 (so spans always exist when the plugin runs). If you would rather not depend on an internal field, use the application-side alternative below.
Alternative — application-side root span (no Kong plugin). If you already instrument your app with the OpenTelemetry SDK, open a parent span per conversation, tag it, and let Kong nest its spans under your trace via the propagated traceparent. This is the pattern in Kong’s Voice AI observability cookbook. It avoids the pre-function plugin but requires OTel SDK code in your application.

Guardrails

Beyond tracing, Kong can enforce Fiddler Guardrails on prompts before they reach the model — blocking PII and secrets at the gateway via Kong’s ai-custom-guardrail plugin (which requires Kong 3.14+ with an AI Gateway Enterprise license). The guardrail plugin’s own check function can also emit a connected Guardrail span in the same trace as the LLM span. See Kong Guardrails for setup, check behavior, and the full configuration.

Troubleshooting

Kong fails to start (missing host in url or similar) This means the config still contains ${...} placeholders. Kong does not read environment variables — open kong_fiddler_config.yaml and replace every ${...} with your actual value (see Step 2). Traces not appearing in Fiddler Verify kong_fiddler_config.yaml has no ${...} placeholders left (every value filled in):
Also confirm Kong is emitting spans at all — check your Kong logs for OpenTelemetry export activity (for example, grep -i otel over your Kong proxy/error logs). Both application.id (OTel resource attribute) and fiddler-application-id (HTTP header on the export request) are required. If application.id is missing, Fiddler skips those spans, logs a warning, and increments a missing_application_id_failure counter — so check your Fiddler ingestion logs and metrics if traces are absent. Fiddler checks that the attribute is present; it does not validate the UUID’s format at this stage. Prompt and response content not showing log_payloads: true is required in the ai-proxy plugin logging config. Without it, gen_ai.input.messages and gen_ai.output.messages are not captured by Kong, so Fiddler will show token counts and model info but no text content. Spans not being emitted by Kong Confirm tracing is enabled at the Kong process level (see Step 3): tracing_instrumentations=all and tracing_sampling_rate=1.0, set as kong.conf settings or KONG_* environment variables. These cannot be configured via the declarative config file. Span type showing as Unknown, or unexpected extra spans in the trace Fiddler routes Kong spans to its Kong mapper by service.name == "kong" (exact string match, case-sensitive) and, from 26.17, also by the kong-internal instrumentation scope. Verify the resource_attributes block in the opentelemetry plugin config has service.name: kong. The same routing decision also gates the dropping of Kong’s infrastructure spans, so a mismatch has two symptoms at once: LLM spans show as Unknown, and Kong’s infrastructure spans stop being filtered out, appearing as extra orphaned spans in the trace. For guardrail-specific troubleshooting, see Kong Guardrails → Troubleshooting.

Known Limitations

Deployment Modes

Fiddler’s Kong tracing works wherever Kong runs the opentelemetry plugin and can export over HTTPS. Two conveniences documented here and on the guardrails page — the pre-function session-grouping plugin and the guardrail check function — additionally require untrusted_lua = on, a process-level setting that some managed Kong offerings do not expose. Unverified means Fiddler has not confirmed the mode — it is not a statement that the mode is unsupported. If your managed gateway does not allow untrusted_lua = on, use the application-side session-grouping alternative, which needs no Kong plugin.

Next Steps