Log request and response data over UDP to Loggly.
Loggly Policy
Process errors
This Policy logs request and response data for each proxied request.
The AI Gateway process error file is the Nginx error file. You can find it at the following path:
$PREFIX/logs/error.log
Configure the prefix in kong.conf.
Log format
Every request is logged separately as a JSON object, separated by a new line \n.
When a request is proxied to an LLM service through an AI Model, the log entry also includes an ai.proxy object with the provider, model, token usage, cost, and latency for that request.
Expand this block to see a sample log object for a proxied LLM request
{
"workspace": "b4a1e9a2-6b4a-4e91-9c2e-3b6a9d8f1c72",
"workspace_name": "default",
"source": "upstream",
"upstream_uri": "/v1/chat/completions",
"upstream_status": 200,
"client_ip": "192.168.65.1",
"started_at": 1787113201719,
"tries": [
{
"ip": "162.159.140.245",
"port": 443,
"hostname": "api.openai.com",
"balancer_latency": 0,
"balancer_start": 1787113201722,
"keepalive": true
}
],
"request": {
"id": "9ec9264afbbbad5fe5c64877d92eeb04",
"uri": "/v1/chat/completions",
"url": "http://localhost:8000/v1/chat/completions",
"method": "POST",
"size": 323,
"querystring": {},
"headers": {
"host": "localhost:8000",
"user-agent": "curl/8.7.1",
"content-type": "application/json",
"content-length": "94",
"authorization": "REDACTED"
}
},
"response": {
"status": 200,
"size": 1845,
"headers": {
"content-type": "application/json",
"content-length": "847",
"date": "Wed, 19 Aug 2026 04:20:02 GMT",
"server": "cloudflare",
"via": "1.1 kong/2.0.2-ai-gateway",
"x-kong-request-id": "9ec9264afbbbad5fe5c64877d92eeb04",
"x-kong-proxy-latency": "3",
"x-kong-upstream-latency": "970",
"x-kong-llm-model": "openai/gpt-4o",
"x-ratelimit-remaining-requests": "4999",
"x-ratelimit-remaining-tokens": "799993",
"openai-organization": "org-example123456",
"openai-project": "proj_example7890abcd"
}
},
"latencies": {
"kong": 3,
"proxy": 970,
"request": 976,
"receive": 3
},
"service": {
"id": "947a4e23-b483-5a65-8165-da9aed24565b",
"name": "ai-gateway",
"host": "ai-gateway.upstream.local",
"port": 80,
"protocol": "http"
},
"route": {
"id": "8ae36805-da7a-55b9-b056-72bde6bb937d",
"name": "openai-chat",
"paths": ["/v1/chat/completions"],
"methods": ["POST"]
},
"ai": {
"proxy": {
"tried_targets": [
{
"route_type": "llm/v1/chat",
"provider": "openai",
"host": "api.openai.com",
"port": 443,
"upstream_scheme": "https",
"upstream_uri": "/v1/chat/completions",
"model": "gpt-4o"
}
],
"meta": {
"request_mode": "oneshot",
"provider_name": "openai",
"request_model": "gpt-4o",
"response_model": "gpt-4o-2024-08-06",
"operation_name": "chat",
"llm_latency": 972,
"plugin_id": "f3adc007-171c-5519-8763-3a0eef79bde3"
},
"usage": {
"prompt_tokens": 13,
"completion_tokens": 12,
"total_tokens": 25,
"cost": 0,
"time_per_token": 81,
"time_to_first_token": 971
}
}
}
}The following table describes the core objects in the log. For a full breakdown of the ai.* fields shown in the previous example, see AI Gateway logs.
|
Log item |
Description |
|---|---|
service
|
Properties of the AI Gateway Service associated with the requested Route. |
route
|
Properties of the specific Route requested. |
request
|
Properties of the request sent by the client. |
response
|
Properties of the response sent to the client. |
latencies
|
Latency data for the request. |
tries
|
A list of iterations made by the load balancer for this request, including each upstream target that was tried. |
client_ip
|
The original client IP address. |
upstream_uri
|
The URI, including query parameters, for the configured upstream service. |
consumer
|
The authenticated AI Consumer. Only present if authentication is enabled. |
started_at
|
The Unix timestamp of when the request started to be processed. |
source
|
Indicates whether the response is generated by kong or upstream.
|
upstream_status
|
The status code received from the upstream service in the response. |
ai
|
Present only for requests proxied to an LLM. Contains provider, model, usage, cost, and cache metrics, keyed by Policy name (for example, ai.proxy).
|
Custom fields by Lua
The config.custom_fields_by_lua configuration lets you dynamically modify log fields using Lua code. The following example configuration removes the route field from the logs:
New fields can be added the same way:
Dot characters (.) in the field key create nested fields. Use a backslash \ to escape a dot if you want to keep it as part of a flat field name instead of nesting it. For example, [my_entry.log\.field] produces a my_entry object with a single log.field key, instead of nesting into log and field.
Targeting AI Gateway fields
AI Gateway logs the outcome of an LLM request under a nested ai object, for example ai.$POLICY_NAME.meta, ai.$POLICY_NAME.usage, and, when payload logging is enabled, ai.$POLICY_NAME.payload.request and ai.$POLICY_NAME.payload.response. Because custom_fields_by_lua keys are split into nested table accesses the same way, you can use the same unescaped, dotted-key syntax to remove or override those fields.
For example, to stop logging LLM request and response payloads:
Note: Escaping the dots (for example,
ai\.loggly\.payload\.request) targets a literal flat key instead of the nestedai.loggly.payload.requestfield, so it won’t match. Use unescaped dots to target AI Gateway fields.
Because Loggly applies custom_fields_by_lua in its own log phase, which runs after AI Gateway sets the ai.* fields on the request, it can override or remove any ai.* field. The reverse isn’t possible, since AI Gateway can’t run after a logging Policy’s log phase to override a field the Policy already set.
Policy precedence and managing fields
All logging Policies use the same table for logging. If you set config.custom_fields_by_lua in one Policy, all logging Policies that run after it also use that configuration. For example, if you configure fields in the File Log Policy, those same fields appear in the Syslog Policy too, since File Log executes first.
- If you want all logging Policies to use the same configuration, use the Pre-function Policy to call
kong.log.set_serialize_valueso the function is applied predictably and is easier to manage. - If you don’t want all logging Policies to share the same configuration, disable the relevant field in each Policy explicitly. For example, if you configure a field in the File Log Policy that you don’t want appearing in the Syslog Policy, set that field to
return nilin the File Log Policy’scustom_fields_by_luaconfiguration.
Limitations
Lua code runs in a restricted sandbox environment, whose behavior is governed by the untrusted_lua configuration.
As this code runs in the log phase, only PDK methods that can run in that phase can be used.