Enrich access logs
Use a transformation template to inject additional metadata to your access logs.
In this guide, you use the dynamicMetadataValues field to write values into Envoy’s dynamic metadata, and then consume those values in two ways:
- Access logs: Envoy’s access log formatter reads the metadata with
%DYNAMIC_METADATA(...)%syntax. - Response headers: A
postRoutingtransformation stage reads the metadata back with thedynamic_metadata()Inja function and injects it into a response header.
For more information about these two approaches, see the dynamicMetadataValues field and the dynamic_metadata function on the templating language page.
Before you begin
Follow the Get started guide to install Solo Enterprise for kgateway.
- Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
- Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n kgateway-system 8080:8080
Enrich access logs
Follow the steps to enable access logs on your gateway proxy.
Create an EnterpriseKgatewayTrafficPolicy with two transformation stages:
- Early stage (write): The
envInja function extracts thePOD_NAMEenvironment variable from the gateway proxy pod. ThedynamicMetadataValuesfield stores the value under thepod_namekey in the defaultio.solo.transformationmetadata namespace. - PostRouting stage (read): The
dynamic_metadata()Inja function reads thepod_namevalue back from dynamic metadata and injects it into anx-pod-nameresponse header.
kubectl apply -f- <<EOF apiVersion: enterprisekgateway.solo.io/v1alpha1 kind: EnterpriseKgatewayTrafficPolicy metadata: name: transformation namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin entTransformation: stages: early: requests: - transformation: template: dynamicMetadataValues: - key: 'pod_name' value: '{{ env("POD_NAME") }}' postRouting: responses: - transformation: template: headers: x-pod-name: '{{ dynamic_metadata("pod_name") }}' EOF- Early stage (write): The
Update the HTTPListenerPolicy resource to include the
pod_namefrom the dynamic metadata values.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: HTTPListenerPolicy metadata: name: access-logs namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http accessLog: - fileSink: path: /dev/stdout jsonFormat: start_time: "%START_TIME%" method: "%REQ(X-ENVOY-ORIGINAL-METHOD?:METHOD)%" path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" protocol: "%PROTOCOL%" response_code: "%RESPONSE_CODE%" response_flags: "%RESPONSE_FLAGS%" bytes_received: "%BYTES_RECEIVED%" bytes_sent: "%BYTES_SENT%" total_duration: "%DURATION%" resp_backend_service_time: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%" req_x_forwarded_for: "%REQ(X-FORWARDED-FOR)%" user_agent: "%REQ(USER-AGENT)%" request_id: "%REQ(X-REQUEST-ID)%" authority: "%REQ(:AUTHORITY)%" backendHost: "%UPSTREAM_HOST%" backendCluster: "%UPSTREAM_CLUSTER%" pod_name: '%DYNAMIC_METADATA(io.solo.transformation:pod_name)%' EOFSend another request to the httpbin app.
curl -i http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.example.com:8080"curl -i localhost:8080/status/200 -H "host: www.example.com:8080"In the response headers, verify that the
x-pod-nameheader is present. ThepostRoutingtransformation read thepod_namevalue from dynamic metadata with thedynamic_metadata()function and injected it into this response header.Example output:
HTTP/1.1 200 OK x-pod-name: http-844ff8bc4d-dh4pn ...Review the access logs and verify that the
pod_nameis also injected into your access logs. The access log formatter reads the same dynamic metadata value directly with the%DYNAMIC_METADATA(...)%syntax.kubectl -n kgateway-system logs deployments/http | tail -1 | jq --sort-keysExample output:
{ "authority": "www.example.com:8080", "bytes_received": 0, "bytes_sent": 0, "method": "GET", "path": "/status/200", "pod_name": "http-844ff8bc4d-dh4pn", "protocol": "HTTP/1.1", "req_x_forwarded_for": null, "request_id": "132892e6-085e-400f-98da-c055ca04b714", "resp_upstream_service_time": "2", "response_code": 200, "response_flags": "-", "start_time": "2025-05-14T16:43:07.506Z", "total_duration": 2, "upstreamCluster": "kube-svc_httpbin-httpbin-8000_httpbin_httpbin_httpbin_8000", "upstreamHost": "10.0.12.250:8080", "user_agent": "curl/8.7.1" }
Cleanup
You can remove the resources that you created in this guide.
kubectl delete EnterpriseKgatewayTrafficPolicy transformation -n httpbin
kubectl delete httplistenerpolicy access-logs -n kgateway-system