Skip to content

Custom traffic log format

Overview

By default, Traffic Logging writes every request in a fixed JSON shape. A traffic log template writes each line in your own JSON layout instead.

With a template, you can:

  • Choose which fields each line has, what they are called and in what order they appear.
  • Group fields into nested objects that match your log platform's schema.
  • Add fixed values, such as a service or environment name.
  • Convert latencies to the time unit you need.

You set the template inline in the gateway configuration.

Configuration

The template is set in the [traffic_logging] section, which must have enabled = true.

Parameter Type Default Description
template string "" The template, inline in the gateway configuration.

When template is empty, the gateway writes the default line.

Template format

A template is a JSON object that describes one log line.

  • A value that starts with $ctx: is filled in from the request. The text after $ctx: is an expression that reads the available variables.
  • Any other value is written exactly as given. It can be a string, number, true, false, null, an object or an array.

For example, this template:

{
  "time": "$ctx:request.timestamp",
  "service": "orders",
  "api": {"name": "$ctx:api.name", "version": "$ctx:api.version"},
  "http": {
    "method": "$ctx:request.method",
    "path": "$ctx:request.path",
    "status": "$ctx:response.status"
  },
  "client": "$ctx:client.ip",
  "app": "$ctx:application.name",
  "durationMs": "$ctx:convertUnit(latencies.duration, 'ms')"
}

writes lines such as:

{"time":"2026-11-10T09:12:33.482Z","service":"orders","api":{"name":"Weather-API","version":"1.0.0"},"http":{"method":"GET","path":"/current","status":200},"client":"203.0.113.10","app":null,"durationMs":18.342}

Each line has the fields in the template's order. Numbers stay numbers. A field the request has no value for is written as null. In this example, app is null because no application was identified for the request.

Passing the template

The gateway reads the template when it starts. After you change the template, restart the gateway.

Set template to the template. A TOML multi-line string (''') keeps it readable.

[traffic_logging]
enabled = true
template = '''
{
  "service": "orders",
  "http": {"method": "$ctx:request.method", "status": "$ctx:response.status"},
  "durationMs": "$ctx:convertUnit(latencies.duration, 'ms')"
}
'''

Helm: Set gateway.config.traffic_logging.template as a YAML string. Use a | block, not a YAML map, so that the field order is kept.

gateway:
  config:
    traffic_logging:
      enabled: true
      template: |
        {
          "service": "orders",
          "http": {"method": "$ctx:request.method", "status": "$ctx:response.status"},
          "durationMs": "$ctx:convertUnit(latencies.duration, 'ms')"
        }

Template lines go to the same outputs (stdout, file or http) as default lines.

How template lines behave

  • A template replaces the default line for every request.
  • masked_headers, max_payload_size and exclude_fields apply to template lines. In exclude_fields, a name is a path in your template's output, for example http.status.
  • The template decides which headers and bodies are logged. They have a value only when the matching [collector] setting (request_headers, response_headers, request_body or response_body) is on.

Available variables

Use these variables in $ctx: values. A variable the request has no value for is written as null.

Request and response

These variables describe the request and the response sent to the client.

Variable Type Description
request.timestamp string When the gateway received the request, in UTC. For example, 2026-11-10T09:12:33.482Z.
request.id string The request's correlation ID.
request.method string HTTP method.
request.path string Request path.
request.pathTemplate string The API resource path that matched the request, for example /orders/{id}.
request.header map Request headers, with lower-case names. Read one with request.header['<name>']. Requires collector.request_headers.
request.body string Request body, cut to max_payload_size. Requires collector.request_body.
response.status number HTTP status code sent to the client.
response.header map Response headers, with lower-case names. Read one with response.header['<name>']. Requires collector.response_headers.
response.body string Response body, cut to max_payload_size. Requires collector.response_body.

Header values in masked_headers are written as ****.

API and backend

These variables describe the API that handled the request and the backend it called.

Variable Type Description
api.id string API ID.
api.name string API name.
api.version string API version.
api.context string API context.
api.kind string API kind.
project.id string Project ID.
target.statusCode number Status code returned by the backend.
target.destination string Backend URL the request was sent to.

Client and application

These variables describe the caller.

Variable Type Description
client.ip string Client IP address.
client.userAgent string Client user agent.
application.id string ID of the application identified for the request.
application.name string Application name.
application.owner string Application owner.
application.keyType string Application key type.

Authentication

These variables describe the authenticated caller and the token.

Variable Type Description
auth.authenticated true/false Whether the request was authenticated.
auth.authorized true/false Whether the request was authorized.
auth.subject string Authenticated subject.
auth.type string Authentication type.
auth.issuer string Token issuer.
auth.credential_id string Credential ID.
auth.token_id string Token ID.
auth.audience list Token audiences.
auth.scopes list Granted scopes.
auth.property map Token claims. Read one with auth.property['<claim>'].

Note

Claims are logged as they appear in the token. To log only the claims you need, read them one by one, for example auth.property['tenant']. To leave out a specific claim, use exclude_fields.

Errors

These variables have a value only when the request failed.

Variable Type Description
error.category string Error category, for example AUTH.
error.errorCode number Error code.
error.errorMessage string Error classification, for example AUTHENTICATION_FAILURE.
error.wso2ErrorCode number WSO2 fault code.
error.type string Type of failure, for example authentication or guardrail.
error.direction string Whether the request or the response was rejected.
error.summary string Message sent to the client.
error.policy string Policy that caused the error.
error.policyPhase string Phase in which that policy ran.
error.source string What produced the response, for example gateway or backend.
error.originalStatus number Backend status code before a policy changed it.
error.jsonRpcCode number JSON-RPC error code, for MCP requests.
error.guardrail.name string Guardrail that intervened.
error.guardrail.action string Action the guardrail took.
error.guardrail.reason string Reason the guardrail gave.

Latencies

Latencies are in microseconds.

Variable Type Description
latencies.duration number Total time to handle the request.
latencies.backendLatency number Time spent waiting for the backend.
latencies.requestMediationLatency number Time spent processing the request in the gateway.
latencies.responseMediationLatency number Time spent processing the response in the gateway.

To write a latency in another unit, use convertUnit(<latency>, '<unit>'). The unit is one of 'ns', 'us', 'ms' or 's'.

{"durationMs": "$ctx:convertUnit(latencies.duration, 'ms')"}

Metadata

This variable holds values that policies share for the request.

Variable Type Description
metadata map Values that policies share for the request. Read one with metadata['<key>'].

Note

metadata values are logged as the policies set them. Reference only keys whose values are safe to log.

Expression examples

The following expressions cover common needs.

To write Use
One header "$ctx:request.header['x-request-id']"
One claim "$ctx:auth.property['tenant']"
All claims, as an object "$ctx:auth.property"
A default value when there is none "$ctx:auth.authenticated ? auth.subject : 'anonymous'"
Text combined with a value "$ctx:'v' + api.version"

Troubleshooting

If the log lines aren't what you expect, check the following.

Symptom What to do
The gateway does not start, and the error names template. Fix the problem the error describes, such as invalid JSON. Then restart the gateway.
A field is always null, and a warning at startup names the field. Fix the $ctx: expression the warning describes, for example a misspelled variable.
A header or body field is always null. Turn on the matching [collector] setting.
A warning at startup says an exclude_fields entry matches no field. Change the entry to a path in your template's output.
Lines still look like the default line. Check that traffic_logging.enabled is true and that template is spelled correctly.