Skip to content
You are viewing the latest documentation for Solo Enterprise for kgateway, formerly known as Gloo Gateway. To access the documentation for older Gloo Gateway versions, such as 2.0 and 1.x, use the version switcher.

Enrich access logs

Page as Markdown

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 postRouting transformation stage reads the metadata back with the dynamic_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

  1. Follow the Get started guide to install Solo Enterprise for kgateway.

  1. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
  1. 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_ADDRESS  
    kubectl port-forward deployment/http -n kgateway-system 8080:8080

Enrich access logs

  1. Follow the steps to enable access logs on your gateway proxy.

  2. Create an EnterpriseKgatewayTrafficPolicy with two transformation stages:

    • Early stage (write): The env Inja function extracts the POD_NAME environment variable from the gateway proxy pod. The dynamicMetadataValues field stores the value under the pod_name key in the default io.solo.transformation metadata namespace.
    • PostRouting stage (read): The dynamic_metadata() Inja function reads the pod_name value back from dynamic metadata and injects it into an x-pod-name response 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
  3. Update the HTTPListenerPolicy resource to include the pod_name from 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)%'
    EOF
  4. Send 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"

  5. In the response headers, verify that the x-pod-name header is present. The postRouting transformation read the pod_name value from dynamic metadata with the dynamic_metadata() function and injected it into this response header.

    Example output:

    HTTP/1.1 200 OK
    x-pod-name: http-844ff8bc4d-dh4pn
    ...
  6. Review the access logs and verify that the pod_name is 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-keys

    Example 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