Set up observability for Kong Event Gateway

TL;DR

Export metrics, traces, and logs from Kong Event Gateway into your own observability systems using OpenTelemetry (OTEL), which helps you understand how Event Gateway functions and how to troubleshoot it when something goes wrong.

In this tutorial, we’re using the Grafana LGTM stack (Loki, Grafana, Tempo, Prometheus), but you can substitute your own preferred OTLP-compatible tools as well.

Prerequisites

Install kafkactl. You’ll need it to interact with Kafka clusters.

Start a Docker Compose cluster with multiple Kafka services.

First, we need to create a docker-compose.yaml file. This file will define the services we want to run in our local environment:

cat <<EOF > docker-compose.yaml
name: kafka_cluster

networks:
  kafka:
    name: kafka_event_gateway

services:
  kafka1:
    image: apache/kafka:4.2.0
    networks:
      - kafka
    container_name: kafka1
    ports:
      - "9094:9094"
    environment:
      KAFKA_NODE_ID: 0
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENERS: INTERNAL://kafka1:9092,CONTROLLER://kafka1:9093,EXTERNAL://0.0.0.0:9094
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka1:9092,EXTERNAL://localhost:9094
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_CONTROLLER_QUORUM_VOTERS: 0@kafka1:9093,1@kafka2:9093,2@kafka3:9093
      KAFKA_CLUSTER_ID: 'abcdefghijklmnopqrstuv'
      KAFKA_LOG_DIRS: /tmp/kraft-combined-logs

  kafka2:
    image: apache/kafka:4.2.0
    networks:
      - kafka
    container_name: kafka2
    ports:
      - "9095:9095"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENERS: INTERNAL://kafka2:9092,CONTROLLER://kafka2:9093,EXTERNAL://0.0.0.0:9095
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka2:9092,EXTERNAL://localhost:9095
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_CONTROLLER_QUORUM_VOTERS: 0@kafka1:9093,1@kafka2:9093,2@kafka3:9093
      KAFKA_CLUSTER_ID: 'abcdefghijklmnopqrstuv'
      KAFKA_LOG_DIRS: /tmp/kraft-combined-logs

  kafka3:
    image: apache/kafka:4.2.0
    networks:
      - kafka
    container_name: kafka3
    ports:
      - "9096:9096"
    environment:
      KAFKA_NODE_ID: 2
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENERS: INTERNAL://kafka3:9092,CONTROLLER://kafka3:9093,EXTERNAL://0.0.0.0:9096
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka3:9092,EXTERNAL://localhost:9096
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_CONTROLLER_QUORUM_VOTERS: 0@kafka1:9093,1@kafka2:9093,2@kafka3:9093
      KAFKA_CLUSTER_ID: 'abcdefghijklmnopqrstuv'
      KAFKA_LOG_DIRS: /tmp/kraft-combined-logs
EOF

Now, let’s start the local setup:

docker compose up -d

If you don’t have a Konnect account, you can get started quickly with our onboarding wizard.

  1. The following Konnect items are required to complete this tutorial:
    • Personal access token (PAT): Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
  2. Set the personal access token as an environment variable:

    export KONNECT_TOKEN='YOUR KONNECT TOKEN'
    

In this guide, you’ll configure the Grafana LGTM stack to receive and visualize observability data from Event Gateway. The LGTM stack bundles Grafana, Loki (logs), Tempo (traces), Prometheus (metrics), and a built-in OpenTelemetry Collector in a single container.

Since Event Gateway 1.1 can push traces, metrics, and logs directly via OTLP to the LGTM stack, you don’t need a separate collector container or any custom collector configuration.

Here’s how it works:

 
flowchart LR

    A[Traces]
    B[Metrics]
    C[Logs]

    subgraph id1 [Event Gateway]
    A
    B
    C
    end

    D[OTLP
    endpoint]
    E[Tempo]
    F[Prometheus]
    G[Loki]
    H[Grafana]

    A --push via OTLP--> D
    B --push via OTLP--> D
    C --push via OTLP--> D

    subgraph id2 [Grafana LGTM]
    D
    E
    F
    G
    H
    end

    D --> E
    D --> F
    D --> G
    E --> H
    F --> H
    G --> H
  

In this setup:

  1. Event Gateway generates traces, metrics, and logs.
  2. All three signals are pushed directly to the LGTM stack’s OTLP endpoint (gRPC on port 4317).
  3. Inside the LGTM stack, the built-in OTEL Collector routes traces to Tempo, metrics to Prometheus, and logs to Loki.
  4. Grafana provides a unified UI to explore all signals.

Create an Event Gateway control plane and data plane

Run the quickstart script to automatically provision a demo Event Gateway control plane and data plane, and configure your environment for exporting observability data:

curl -Ls https://get.konghq.com/event-gateway | bash -s -- \
  -k $KONNECT_TOKEN \
  -N kafka_event_gateway \
  -e "OTEL_EXPORTER_OTLP_PROTOCOL=grpc" \
  -e "OTEL_EXPORTER_OTLP_ENDPOINT=http://lgtm:4317" \
  -e "OTEL_EXPORTER_OTLP_TIMEOUT=10s" \
  -e "OTEL_SERVICE_NAME=eventgw"

Where you configure the following custom telemetry settings:

Parameter

Default

New value

Description

OTEL_EXPORTER_OTLP_PROTOCOL http/binary grpc Protocol used to export OpenTelemetry data.
OTEL_EXPORTER_OTLP_ENDPOINT none http://lgtm:4317 Endpoint to send OpenTelemetry data. Setting this enables all OTLP signals (traces, metrics, and logs). In most cases, this will be the URL of your OTLP-compatible backend.
OTEL_EXPORTER_OTLP_TIMEOUT 10s 10s Max waiting time for the backend to process each batch. We’re not adjusting this for the tutorial, but you can adjust as needed for troubleshooting.
OTEL_SERVICE_NAME none eventgw Name of the OTEL service identified in the observability tools. For example, in Grafana/Tempo, the service will appear as eventgw.

This sets up an Event Gateway control plane named event-gateway-quickstart, provisions a local data plane, and prints out the following environment variable export:

export EVENT_GATEWAY_ID=your-gateway-id

Copy and paste this into your terminal to configure your session.

This quickstart script is meant for demo purposes only, therefore it runs locally with most default parameters and a small number of exposed ports. If you want to run Kong Gateway as a part of a production-ready platform, set up your control plane and data planes through the Konnect UI, or using Terraform.

Launch the Grafana LGTM stack

The grafana/otel-lgtm image bundles Grafana, Tempo, Prometheus, Loki, and a built-in OTEL Collector in a single container. No custom configuration files are needed.

Run the following command to start the LGTM stack on the same network as your Kafka cluster and Event Gateway data plane:

docker run -d --name lgtm \
  --network kafka_event_gateway \
  -p 3000:3000 \
  -p 4317:4317 \
  -p 4318:4318 \
  grafana/otel-lgtm:latest

Add Kafka configuration

Use the following Kafka configuration to access your Kafka resources from the virtual clusters:

cat <<EOF > kafkactl.yaml
contexts:
  direct:
    brokers:
      - localhost:9095
      - localhost:9096
      - localhost:9094
  vc:
    brokers:
      - localhost:19092
EOF

Create a backend cluster

Use the following command to create a backend cluster that connects to the Kafka servers you set up:

BACKEND_CLUSTER_ID=$(curl -X POST "https://us.api.konghq.com/v1/event-gateways/$EVENT_GATEWAY_ID/backend-clusters" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "backend_cluster",
       "bootstrap_servers": [
         "kafka1:9092",
         "kafka2:9092",
         "kafka3:9092"
       ],
       "authentication": {
         "type": "anonymous"
       },
       "tls": {
         "enabled": false
       },
       "insecure_allow_anonymous_virtual_cluster_auth": true
     }' | jq -r ".id"
)

In this example configuration:

  • bootstrap_servers: Points the backend cluster to the three bootstrap servers that we launched in the prerequisites.
  • authentication and insecure_allow_anonymous_virtual_cluster_auth: For demo purposes, we’re allowing insecure anonymous connections, which means no authentication required.
  • tls: TLS is disabled so that we can easily test the connection.

Add a virtual cluster

Run the following command to create a new virtual cluster associated with our backend cluster. This will let you route event traffic and apply policies:

VIRTUAL_CLUSTER_ID=$(curl -X POST "https://us.api.konghq.com/v1/event-gateways/$EVENT_GATEWAY_ID/virtual-clusters" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "example_virtual_cluster",
       "destination": {
         "id": "'$BACKEND_CLUSTER_ID'"
       },
       "dns_label": "vcluster-1",
       "authentication": [
         {
           "type": "anonymous"
         }
       ],
       "acl_mode": "passthrough"
     }' | jq -r ".id"
)

In this example:

  • authentication: Allows anonymous authentication.
  • acl_mode: The setting passthrough means that all clients are allowed and don’t have to match a defined ACL. In a production environment, you would set this to enforce_on_gateway and define an ACL policy.
  • name is an internal name for the configuration object, while the dns_label is necessary for SNI routing.

Add a listener and policy

For testing purposes, we’ll use port forwarding to route traffic to the virtual cluster.
In production environments, you should use SNI routing instead.

Run the following command to create a new listener:

LISTENER_ID=$(curl -X POST "https://us.api.konghq.com/v1/event-gateways/$EVENT_GATEWAY_ID/listeners" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "example_listener",
       "addresses": [
         "0.0.0.0"
       ],
       "ports": [
         "19092-19095"
       ]
     }' | jq -r ".id"
)

Create the port mapping policy:

curl -X POST "https://us.api.konghq.com/v1/event-gateways/$EVENT_GATEWAY_ID/listeners/$LISTENER_ID/policies" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "type": "forward_to_virtual_cluster",
       "name": "forward",
       "config": {
         "type": "port_mapping",
         "advertised_host": "localhost",
         "destination": {
           "id": "'$VIRTUAL_CLUSTER_ID'"
         }
       }
     }'

Validate the cluster

Create a topic using the direct context, which is a direct connection to our Kafka cluster:

kafkactl -C kafkactl.yaml --context direct create topic my-test-topic

Then produce a message through the virtual cluster:

kafkactl -C kafkactl.yaml --context vc produce my-test-topic --value="test message"

You should see the following responses:

topic created: my-test-topic
message produced (partition=0	offset=0)

View metrics in Grafana

Now that Event Gateway is pushing metrics via OTLP to the LGTM stack, let’s explore them in Grafana.

  1. In your browser, open Grafana at http://localhost:3000/.
  2. Navigate to Drilldown > Metrics.
  3. Search for kong to see the list of available metrics.
  4. Let’s look at a sample metric: kong_keg_kafka_backend_roundtrip_duration_seconds_count.

This metric reports how many Kafka backend round-trip observations have been recorded (the number of requests Event Gateway has sent to the backend cluster and received responses for).

On startup, metrics can take up to 5 minutes to start showing up.

View traces in Grafana

Let’s explore the traces generated by Event Gateway.

  1. Send any command through the virtual cluster, such as list topics:

    kafkactl -C kafkactl.yaml --context vc list topics
    
  2. In your browser, open Grafana at http://localhost:3000/.
  3. Navigate to Drilldown > Traces.
  4. Select service.name=eventgw and name=request and click Run query.

Here you can see the full trace generated by Event Gateway for each command. For example, you can click a trace to see the span details, including information about the virtual cluster and backend cluster.

View logs in Grafana

Event Gateway also exports logs via OTLP. Let’s explore them in Grafana.

  1. In Grafana, navigate to Drilldown > Logs.
  2. Use the label filter service_name = eventgw to see Event Gateway logs.

FAQs

You can find the list of all available metrics in the metrics reference.

Kong Event Gateway logs are also available on STDOUT, so you can use docker logs event-gateway-quickstart.

Yes, but by default the health server is only accessible on localhost. To make it more widely available, set KEG__RUNTIME__HEALTH_LISTENER_ADDRESS_PORT at data plane startup.

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!