You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Proposal] Custom traffic log format support
#3856
The gateway's traffic log line has a fixed shape today. This proposal adds two opt-in features:
[traffic_logging.json_fields] lets you define the whole traffic log line, the same way [router.access_logs.json_fields] defines the access log. Each key is a field name. Each value is either a literal or a $ctx: expression.
Latency time unit: every latency is available in microseconds (as today) and in decimal milliseconds, so you choose the unit for each field.
Existing configurations behave exactly as before. Implementation: #3846.
Feedback is welcome on the configuration and on the review questions at the end.
Problem
The line can't be reshaped. Operators can add fields under properties and drop fields with exclude_fields. They can't rename fields or flatten the nested objects (api, operation, latencies, ...). The access log already allows this through json_fields.
Latencies are in microseconds only (durationUs, backendLatencyUs, ...). Most log backends and dashboards use milliseconds, so every consumer has to convert.
Lines are long, mostly because of the latencies object, and there is no way to keep only the fields a consumer needs.
When json_fields is set, it replaces the default line. Only these fields are written.
A value is either a literal string or a $ctx: CEL expression. Expressions use the same variables as properties, plus the new ones listed below. Results keep their type: numbers stay numbers, and maps become objects.
If properties is also set, it is ignored and a warning is logged at startup.
masked_headers still applies. For example, $ctx:request.header['authorization'] logs ****.
An expression that doesn't compile is dropped at startup with an error log. The other fields are still written.
Data a request doesn't have, such as latencies on a request rejected before reaching the backend, is written as its zero value ("" or 0).
Keys are flat and written in alphabetical order. "http.method" is a single key, not a nested object.
In TOML, quote keys that contain characters other than letters, digits, _ and -, such as "@timestamp" and "http.method". An unquoted dot creates a nested table.
2. Latency time unit
Each latency is available in both units:
Microseconds (integer)
Milliseconds (decimal)
latencies.durationUs
latencies.durationMs
latencies.requestMediationLatencyUs
latencies.requestMediationLatencyMs
latencies.responseMediationLatencyUs
latencies.responseMediationLatencyMs
latencies.backendLatencyUs
latencies.backendLatencyMs
Milliseconds keep microsecond precision: 15321 µs becomes 15.321. You pick the unit for each field by choosing the variable. The default line's latencies object stays in microseconds.
Other units need no new variables, because a $ctx: expression can do the arithmetic: latencies.durationUs * 1000 for nanoseconds (still at microsecond precision), or latencies.durationMs / 1000.0 for seconds. CEL doesn't mix decimal and whole numbers, so latencies.durationMs / 1000 is rejected at startup; write 1000.0.
3. New $ctx: variables
These variables work in both json_fields and properties, except where noted:
Variable
Value
request.timestamp
Request time in UTC, RFC 3339 with milliseconds (same as the default line's timestamp)
client.ip, client.userAgent
Same as the default line's client object
latencies.*Us, latencies.*Ms
See the latency table above
errorType, error.<field>
Same as the default line's errorType and error fields: errorCode, errorMessage, wso2ErrorCode, type, direction, summary, policy, policyPhase, source, originalStatus, jsonRpcCode
request.body, response.body
json_fields only. They need [collector] body capture and are cut to max_payload_size. The request_body/response_body toggles aren't needed.
An unknown $ctx: variable logs a warning at startup, so a typo doesn't silently leave data in the log. $ctx: entries don't apply to properties; properties are still excluded by name (properties.<key>).
The chart renders this as [traffic_logging.json_fields] and quotes every key.
Backward compatibility
The default line, properties and exclude_fields don't change unless json_fields is set.
The new variables and $ctx: exclude entries are additions. Existing configurations stay valid as they are.
Review questions
Both set: when json_fields and properties are both set, properties is ignored with a startup warning. Should this be a startup error instead?
Exclusion scope:$ctx: exclude entries apply only to json_fields. properties keep name-based exclusion (properties.<key>), so there aren't two ways to do the same thing. Is that acceptable?
Default-line unit: there is no latency_unit setting for the default line. Its keys name the unit (durationUs), so changing the unit would break existing consumers, and json_fields already lets users choose. Is such a setting still needed?
Absent values: these are written as "" or 0. Should those fields be left out instead?
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
The gateway's traffic log line has a fixed shape today. This proposal adds two opt-in features:
[traffic_logging.json_fields]lets you define the whole traffic log line, the same way[router.access_logs.json_fields]defines the access log. Each key is a field name. Each value is either a literal or a$ctx:expression.Existing configurations behave exactly as before. Implementation: #3846.
Feedback is welcome on the configuration and on the review questions at the end.
Problem
propertiesand drop fields withexclude_fields. They can't rename fields or flatten the nested objects (api,operation,latencies, ...). The access log already allows this throughjson_fields.durationUs,backendLatencyUs, ...). Most log backends and dashboards use milliseconds, so every consumer has to convert.latenciesobject, and there is no way to keep only the fields a consumer needs.Proposed configuration
1.
[traffic_logging.json_fields]This config writes one line like this per request:
{"@timestamp":"2026-10-21T09:12:33.482Z","api":"PetStore/v1","backendMs":12.874,"correlationId":"5f6b6e2a-2f38-4b7a-9c2f-6b6a2f384b7a","durationMs":15.321,"env":"prod","method":"GET","status":200}Behaviour:
json_fieldsis set, it replaces the default line. Only these fields are written.$ctx:CEL expression. Expressions use the same variables asproperties, plus the new ones listed below. Results keep their type: numbers stay numbers, and maps become objects.propertiesis also set, it is ignored and a warning is logged at startup.masked_headersstill applies. For example,$ctx:request.header['authorization']logs****.""or0)."http.method"is a single key, not a nested object._and-, such as"@timestamp"and"http.method". An unquoted dot creates a nested table.2. Latency time unit
Each latency is available in both units:
latencies.durationUslatencies.durationMslatencies.requestMediationLatencyUslatencies.requestMediationLatencyMslatencies.responseMediationLatencyUslatencies.responseMediationLatencyMslatencies.backendLatencyUslatencies.backendLatencyMsMilliseconds keep microsecond precision:
15321µs becomes15.321. You pick the unit for each field by choosing the variable. The default line'slatenciesobject stays in microseconds.Other units need no new variables, because a
$ctx:expression can do the arithmetic:latencies.durationUs * 1000for nanoseconds (still at microsecond precision), orlatencies.durationMs / 1000.0for seconds. CEL doesn't mix decimal and whole numbers, solatencies.durationMs / 1000is rejected at startup; write1000.0.3. New
$ctx:variablesThese variables work in both
json_fieldsandproperties, except where noted:request.timestamptimestamp)client.ip,client.userAgentclientobjectlatencies.*Us,latencies.*MserrorType,error.<field>errorTypeanderrorfields:errorCode,errorMessage,wso2ErrorCode,type,direction,summary,policy,policyPhase,source,originalStatus,jsonRpcCoderequest.body,response.bodyjson_fieldsonly. They need[collector]body capture and are cut tomax_payload_size. Therequest_body/response_bodytoggles aren't needed.Existing variables (
request.*,response.*,api.*,application.*,auth.*,metadata, ...) are unchanged.4.
exclude_fieldswithjson_fieldsFor the default line,
exclude_fieldsworks as it does today. Withjson_fields:json_fieldsrequestHeaders.<name>,responseHeaders.<name>request.header/response.header$ctx:<variable>, e.g.$ctx:error.policy$ctx:auth.property.<key>,$ctx:metadata.<key>latencies,api.name, ...)An unknown
$ctx:variable logs a warning at startup, so a typo doesn't silently leave data in the log.$ctx:entries don't apply toproperties; properties are still excluded by name (properties.<key>).5. Helm
The chart renders this as
[traffic_logging.json_fields]and quotes every key.Backward compatibility
propertiesandexclude_fieldsdon't change unlessjson_fieldsis set.$ctx:exclude entries are additions. Existing configurations stay valid as they are.Review questions
json_fieldsandpropertiesare both set,propertiesis ignored with a startup warning. Should this be a startup error instead?$ctx:exclude entries apply only tojson_fields.propertieskeep name-based exclusion (properties.<key>), so there aren't two ways to do the same thing. Is that acceptable?latency_unitsetting for the default line. Its keys name the unit (durationUs), so changing the unit would break existing consumers, andjson_fieldsalready lets users choose. Is such a setting still needed?""or0. Should those fields be left out instead?All reactions