elasticsearch-logger
The elasticsearch-logger plugin sends request and response logs to Elasticsearch in batches. The plugin serializes each log entry in the Elasticsearch Bulk API format and supports customized log fields and index names. This integration centralizes gateway logs for searching, analysis, and visualization in Kibana.
Examples
The examples below configure the elasticsearch-logger plugin for common logging scenarios.
To follow the examples, start Elasticsearch and Kibana. Both products use the same Elastic Stack version. In Docker, the gateway and Elasticsearch share a dedicated network so that the gateway can reach Elasticsearch by container name.
Elastic Stack enables authentication and TLS by default. The following setup keeps authentication enabled but disables TLS on the Elasticsearch HTTP and transport interfaces to simplify local evaluation. The Docker ports are bound to the loopback interface, and the Kubernetes services are available only inside the cluster.
For a production deployment, use HTTPS with certificates issued by a trusted certificate authority. If the certificate uses a private CA, add the CA bundle to apisix.ssl.ssl_trusted_certificate, and keep the plugin's ssl_verify option enabled. Store credentials in a secret manager instead of configuration files.
On Linux, Elasticsearch requires vm.max_map_count to be at least 1048576 on the host or virtual machine that runs the containers. Check the current value before starting the Docker or Kubernetes setup:
sysctl vm.max_map_count
If the value is lower, increase it on the Docker host or on every Kubernetes node. This command requires administrative privileges:
sudo sysctl -w vm.max_map_count=1048576
For Docker Desktop and managed Kubernetes environments, follow Elastic's platform-specific virtual memory instructions to apply the setting to the underlying Linux environment.
- Docker
- Kubernetes
Set GATEWAY_CONTAINER to the name of the running APISIX or API7 Gateway container. Create a dedicated Docker network and connect the gateway to it:
export GATEWAY_CONTAINER=replace-with-gateway-container-name
docker network create gateway-elasticsearch-net
docker network connect gateway-elasticsearch-net "$GATEWAY_CONTAINER"
Start Elasticsearch with authentication enabled:
docker run -d \
--name elasticsearch \
--network gateway-elasticsearch-net \
-v elasticsearch_logger_vol:/usr/share/elasticsearch/data/ \
-p 127.0.0.1:9200:9200 \
-e ELASTIC_PASSWORD=gateway-elastic-password \
-e ES_JAVA_OPTS="-Xms768m -Xmx768m" \
-e discovery.type=single-node \
-e xpack.security.enabled=true \
-e xpack.security.autoconfiguration.enabled=false \
-e xpack.security.http.ssl.enabled=false \
-e xpack.security.transport.ssl.enabled=false \
docker.elastic.co/elasticsearch/elasticsearch:9.5.3
Wait for Elasticsearch to become available:
until curl -fsS -u "elastic:gateway-elastic-password" \
"http://127.0.0.1:9200/_cluster/health?wait_for_status=yellow" > /dev/null; do
sleep 2
done
Set the password for the internal Kibana user:
curl "http://127.0.0.1:9200/_security/user/kibana_system/_password" \
-u "elastic:gateway-elastic-password" \
-H "Content-Type: application/json" \
-X POST \
-d '{"password":"gateway-kibana-password"}'
Create a role that can monitor the cluster and write only to the indices used in these examples:
curl "http://127.0.0.1:9200/_security/role/gateway_logger" \
-u "elastic:gateway-elastic-password" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["gateway", "gateway-*"],
"privileges": ["auto_configure", "create_index", "write"]
}
]
}'
Create a user and assign the logger role:
curl "http://127.0.0.1:9200/_security/user/gateway_logger" \
-u "elastic:gateway-elastic-password" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"password": "gateway-logger-password",
"roles": ["gateway_logger"]
}'
Start Kibana to visualize the indexed data:
docker run -d \
--name kibana \
--network gateway-elasticsearch-net \
-p 127.0.0.1:5601:5601 \
-e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" \
-e ELASTICSEARCH_USERNAME=kibana_system \
-e ELASTICSEARCH_PASSWORD=gateway-kibana-password \
docker.elastic.co/kibana/kibana:9.5.3
Create a Kubernetes manifest for Elasticsearch and its credentials:
apiVersion: v1
kind: Secret
metadata:
namespace: aic
name: elasticsearch-credentials
type: Opaque
stringData:
elastic-password: gateway-elastic-password
kibana-password: gateway-kibana-password
---
apiVersion: v1
kind: Secret
metadata:
namespace: aic
name: elasticsearch-logger-credentials
type: Opaque
stringData:
auth.password: gateway-logger-password
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: elasticsearch
spec:
replicas: 1
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:9.5.3
env:
- name: ELASTIC_PASSWORD
valueFrom:
secretKeyRef:
name: elasticsearch-credentials
key: elastic-password
- name: ES_JAVA_OPTS
value: "-Xms768m -Xmx768m"
- name: discovery.type
value: single-node
- name: xpack.security.enabled
value: "true"
- name: xpack.security.autoconfiguration.enabled
value: "false"
- name: xpack.security.http.ssl.enabled
value: "false"
- name: xpack.security.transport.ssl.enabled
value: "false"
ports:
- containerPort: 9200
readinessProbe:
exec:
command:
- sh
- -c
- curl -fsS -u "elastic:${ELASTIC_PASSWORD}" http://127.0.0.1:9200 > /dev/null
initialDelaySeconds: 20
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: elasticsearch
spec:
selector:
app: elasticsearch
ports:
- name: http
port: 9200
targetPort: 9200
type: ClusterIP
Apply the manifest and wait for Elasticsearch:
kubectl apply -f elasticsearch.yaml
kubectl rollout status -n aic deployment/elasticsearch
Set the password for the internal Kibana user:
kubectl exec -n aic deployment/elasticsearch -- sh -c '
curl -fsS -u "elastic:${ELASTIC_PASSWORD}" \
-H "Content-Type: application/json" \
-X POST \
-d "{\"password\":\"gateway-kibana-password\"}" \
"http://127.0.0.1:9200/_security/user/kibana_system/_password"
'
Create the restricted logger role:
kubectl exec -n aic deployment/elasticsearch -- sh -c '
curl -fsS -u "elastic:${ELASTIC_PASSWORD}" \
-H "Content-Type: application/json" \
-X PUT \
-d "{\"cluster\":[\"monitor\"],\"indices\":[{\"names\":[\"gateway\",\"gateway-*\"],\"privileges\":[\"auto_configure\",\"create_index\",\"write\"]}]}" \
"http://127.0.0.1:9200/_security/role/gateway_logger"
'
Create the logger user and assign the role:
kubectl exec -n aic deployment/elasticsearch -- sh -c '
curl -fsS -u "elastic:${ELASTIC_PASSWORD}" \
-H "Content-Type: application/json" \
-X PUT \
-d "{\"password\":\"gateway-logger-password\",\"roles\":[\"gateway_logger\"]}" \
"http://127.0.0.1:9200/_security/user/gateway_logger"
'
Create a Kubernetes manifest for Kibana:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:9.5.3
env:
- name: ELASTICSEARCH_HOSTS
value: "http://elasticsearch.aic.svc:9200"
- name: ELASTICSEARCH_USERNAME
value: kibana_system
- name: ELASTICSEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: elasticsearch-credentials
key: kibana-password
ports:
- containerPort: 5601
readinessProbe:
httpGet:
path: /api/status
port: 5601
initialDelaySeconds: 15
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: kibana
spec:
selector:
app: kibana
ports:
- name: http
port: 5601
targetPort: 5601
type: ClusterIP
Apply the manifest:
kubectl apply -f kibana.yaml
kubectl rollout status -n aic deployment/kibana
To access Kibana, forward the service port:
kubectl port-forward -n aic svc/kibana 5601:5601
When Kibana becomes available, open localhost:5601 and log in as elastic with password gateway-elastic-password.
Log Requests in the Default Format
The following example enables the plugin on a route to send request and response information to the gateway index.
Create the route:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/elasticsearch-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/anything",
"plugins": {
"elasticsearch-logger": {
"endpoint_addrs": ["http://elasticsearch:9200"],
"field": {
"index": "gateway"
},
"auth": {
"username": "gateway_logger",
"password": "gateway-logger-password"
}
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything
name: elasticsearch-logger-route
plugins:
elasticsearch-logger:
endpoint_addrs:
- "http://elasticsearch:9200"
field:
index: gateway
auth:
username: gateway_logger
password: gateway-logger-password
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC reconciles services as desired state. The label selector limits this example to its own labeled resources. Preview the scoped changes and confirm that they contain no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=elasticsearch-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=elasticsearch-logger
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: elasticsearch-logger-plugin-config
spec:
plugins:
- name: elasticsearch-logger
config:
endpoint_addrs:
- "http://elasticsearch.aic.svc:9200"
field:
index: gateway
auth:
username: gateway_logger
password: gateway-logger-password
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: elasticsearch-logger-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: elasticsearch-logger-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: elasticsearch-logger-route
spec:
ingressClassName: apisix
http:
- name: elasticsearch-logger-route
match:
paths:
- /anything
methods:
- GET
upstreams:
- name: httpbin-external-domain
plugins:
- name: elasticsearch-logger
enable: true
config:
endpoint_addrs:
- "http://elasticsearch.aic.svc:9200"
field:
index: gateway
auth:
username: gateway_logger
secretRef: elasticsearch-logger-credentials
Apply the configuration:
kubectl apply -f elasticsearch-logger-ic.yaml
❶ Configure the Elasticsearch endpoint. The endpoint does not end with a trailing slash.
❷ Configure the index field as gateway.
The credentials in these examples belong to the local evaluation user created earlier. Replace them with credentials managed according to your organization's security policies.
Send a request to the route to generate a log entry:
curl -i "http://127.0.0.1:9080/anything"
You should receive an HTTP/1.1 200 OK response. The batch processor can take several seconds to send the log entry.
In Kibana, open Discover and create a data view with the index pattern gateway. The new log entry should contain fields similar to the following:
{
"_index": "gateway",
"_id": "CE-JL5QBOkdYRG7kEjTJ",
"_version": 1,
"_score": 1,
"_source": {
"request": {
"headers": {
"host": "127.0.0.1:9080",
"accept": "*/*",
"user-agent": "curl/8.6.0"
},
"size": 85,
"querystring": {},
"method": "GET",
"url": "http://127.0.0.1:9080/anything",
"uri": "/anything"
},
"response": {
"headers": {
"content-type": "application/json",
"access-control-allow-credentials": "true",
"content-length": "390",
"access-control-allow-origin": "*",
"connection": "close",
"date": "Mon, 13 Jan 2025 10:18:14 GMT"
},
"status": 200,
"size": 618
},
"route_id": "elasticsearch-logger-route",
"latency": 585.00003814697,
"apisix_latency": 18.000038146973,
"upstream_latency": 567,
"upstream": "50.19.58.113:80",
"service_id": "",
"client_ip": "192.168.65.1"
},
"fields": {
...
}
}
Log Request and Response Headers with Plugin Metadata
The following example uses plugin metadata and built-in variables to record selected request and response headers.
Plugin metadata configures common metadata fields for all instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to its metadata fields.
First, create a route with the plugin enabled:
curl "http://127.0.0.1:9180/apisix/admin/routes/elasticsearch-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/anything",
"plugins": {
"elasticsearch-logger": {
"endpoint_addrs": ["http://elasticsearch:9200"],
"field": {
"index": "gateway"
},
"auth": {
"username": "gateway_logger",
"password": "gateway-logger-password"
}
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
Next, configure the plugin metadata for elasticsearch-logger:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"log_format": {
"host": "$host",
"@timestamp": "$time_iso8601",
"client_ip": "$remote_addr",
"env": "$http_env",
"resp_content_type": "$sent_http_Content_Type"
}
}'
Export the complete plugin metadata collection:
adc dump -o adc.yaml --with-id --include-resource-type plugin_metadata
Add or update this entry under the exported plugin_metadata mapping while preserving every other entry:
plugin_metadata:
elasticsearch-logger:
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
client_ip: "$remote_addr"
env: "$http_env"
resp_content_type: "$sent_http_Content_Type"
Preview the complete plugin metadata collection and confirm that it contains no unintended updates or deletions:
adc diff -f adc.yaml --include-resource-type plugin_metadata
Synchronize the reviewed plugin metadata:
adc sync -f adc.yaml --include-resource-type plugin_metadata
apiVersion: apisix.apache.org/v1alpha1
kind: GatewayProxy
metadata:
namespace: aic
name: apisix-config
spec:
provider:
type: ControlPlane
controlPlane:
service:
name: apisix-admin
port: 9180
auth:
type: AdminKey
adminKey:
value: edd1c9f034335f136f87ad84b625c8f1
pluginMetadata:
elasticsearch-logger:
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
client_ip: "$remote_addr"
env: "$http_env"
resp_content_type: "$sent_http_Content_Type"
Apply the configuration:
kubectl apply -f elasticsearch-logger-metadata.yaml
❶ Log the custom request header env.
❷ Log the response header Content-Type.
Send a request to the route with the env header:
curl -i "http://127.0.0.1:9080/anything" -H "env: dev"
You should receive an HTTP/1.1 200 OK response.
In Kibana Discover, the log entry should contain the custom fields:
{
"_index": "gateway",
"_id": "Ck-WL5QBOkdYRG7kODS0",
"_version": 1,
"_score": 1,
"_source": {
"client_ip": "192.168.65.1",
"route_id": "elasticsearch-logger-route",
"@timestamp": "2025-01-06T10:32:36+00:00",
"host": "127.0.0.1",
"env": "dev",
"resp_content_type": "application/json"
},
"fields": {
...
}
}
Log Request Bodies Conditionally
The following example records a request body only when the request satisfies an APISIX expression.
If you completed the preceding example with the Admin API, remove the custom plugin metadata before continuing:
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger" -X DELETE \
-H "X-API-KEY: ${ADMIN_API_KEY}"
When using ADC, synchronize an empty plugin_metadata mapping with --include-resource-type plugin_metadata. When using the Ingress Controller, remove the plugin metadata from the GatewayProxy resource and reapply it.
Create the route:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/elasticsearch-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"plugins": {
"elasticsearch-logger": {
"endpoint_addrs": ["http://elasticsearch:9200"],
"field": {
"index": "gateway"
},
"auth": {
"username": "gateway_logger",
"password": "gateway-logger-password"
},
"include_req_body": true,
"include_req_body_expr": [["arg_log_body", "==", "yes"]]
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
},
"uri": "/anything"
}'
services:
- name: httpbin
routes:
- uris:
- /anything
name: elasticsearch-logger-route
plugins:
elasticsearch-logger:
endpoint_addrs:
- "http://elasticsearch:9200"
field:
index: gateway
auth:
username: gateway_logger
password: gateway-logger-password
include_req_body: true
include_req_body_expr:
- - arg_log_body
- "=="
- "yes"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC reconciles services as desired state. The label selector limits this example to its own labeled resources. Preview the scoped changes and confirm that they contain no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=elasticsearch-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=elasticsearch-logger
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: elasticsearch-logger-plugin-config
spec:
plugins:
- name: elasticsearch-logger
config:
endpoint_addrs:
- "http://elasticsearch.aic.svc:9200"
field:
index: gateway
auth:
username: gateway_logger
password: gateway-logger-password
include_req_body: true
include_req_body_expr:
- - arg_log_body
- "=="
- "yes"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: elasticsearch-logger-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: elasticsearch-logger-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: elasticsearch-logger-route
spec:
ingressClassName: apisix
http:
- name: elasticsearch-logger-route
match:
paths:
- /anything
methods:
- POST
upstreams:
- name: httpbin-external-domain
plugins:
- name: elasticsearch-logger
enable: true
config:
endpoint_addrs:
- "http://elasticsearch.aic.svc:9200"
field:
index: gateway
auth:
username: gateway_logger
include_req_body: true
include_req_body_expr:
- - arg_log_body
- "=="
- "yes"
secretRef: elasticsearch-logger-credentials
Apply the configuration:
kubectl apply -f elasticsearch-logger-ic.yaml
❶ Set include_req_body to true to include the request body.
❷ Set include_req_body_expr to include the body only when the log_body query parameter is yes.
Send a request with a query parameter that satisfies the condition:
curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}'
You should receive an HTTP/1.1 200 OK response.
In Kibana Discover, the log entry should include the request body:
{
"_index": "gateway",
"_id": "Dk-cL5QBOkdYRG7k7DSW",
"_version": 1,
"_score": 1,
"_source": {
"request": {
"headers": {
"user-agent": "curl/8.6.0",
"accept": "*/*",
"content-length": "14",
"host": "127.0.0.1:9080",
"content-type": "application/x-www-form-urlencoded"
},
"size": 182,
"querystring": {
"log_body": "yes"
},
"body": "{\"env\": \"dev\"}",
"method": "POST",
"url": "http://127.0.0.1:9080/anything?log_body=yes",
"uri": "/anything?log_body=yes"
},
"start_time": 1735965595203,
"response": {
"headers": {
"content-type": "application/json",
"access-control-allow-credentials": "true",
"content-length": "548",
"access-control-allow-origin": "*",
"connection": "close",
"date": "Mon, 13 Jan 2025 11:02:32 GMT"
},
"status": 200,
"size": 776
},
"route_id": "elasticsearch-logger-route",
"latency": 703.9999961853,
"apisix_latency": 34.999996185303,
"upstream_latency": 669,
"upstream": "34.197.122.172:80",
"service_id": "",
"client_ip": "192.168.65.1"
},
"fields": {
...
}
}
Send a request to the route without any URL query string:
curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}'
In Kibana Discover, the new log entry should not include the request body:
{
"_index": "gateway",
"_id": "EU-eL5QBOkdYRG7kUDST",
"_version": 1,
"_score": 1,
"_source": {
"request": {
"headers": {
"content-type": "application/x-www-form-urlencoded",
"accept": "*/*",
"content-length": "14",
"host": "127.0.0.1:9080",
"user-agent": "curl/8.6.0"
},
"size": 169,
"querystring": {},
"method": "POST",
"url": "http://127.0.0.1:9080/anything",
"uri": "/anything"
},
"start_time": 1735965686363,
"response": {
"headers": {
"content-type": "application/json",
"access-control-allow-credentials": "true",
"content-length": "510",
"access-control-allow-origin": "*",
"connection": "close",
"date": "Mon, 13 Jan 2025 11:15:54 GMT"
},
"status": 200,
"size": 738
},
"route_id": "elasticsearch-logger-route",
"latency": 680.99999427795,
"apisix_latency": 4.9999942779541,
"upstream_latency": 676,
"upstream": "34.197.122.172:80",
"service_id": "",
"client_ip": "192.168.65.1"
},
"fields": {
...
}
}
Custom log formats do not add collected request or response bodies automatically. Include the corresponding variables in the format:
{
"include_req_body": true,
"include_resp_body": true,
"log_format": {
"request_body": "$request_body",
"response_body": "$resp_body"
}
}
Body size limits still apply. Use log_format_extra to add custom fields without replacing the default log entry.
Include Request Date in Elasticsearch Index
The following example uses a Lua time format in the index name to organize logs by request date.
Create the route:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/elasticsearch-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/anything",
"plugins": {
"elasticsearch-logger": {
"endpoint_addrs": ["http://elasticsearch:9200"],
"field": {
"index": "gateway-{%Y.%m.%d}"
},
"auth": {
"username": "gateway_logger",
"password": "gateway-logger-password"
}
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything
name: elasticsearch-logger-route
plugins:
elasticsearch-logger:
endpoint_addrs:
- "http://elasticsearch:9200"
field:
index: "gateway-{%Y.%m.%d}"
auth:
username: gateway_logger
password: gateway-logger-password
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
ADC reconciles services as desired state. The label selector limits this example to its own labeled resources. Preview the scoped changes and confirm that they contain no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=elasticsearch-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=elasticsearch-logger
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: elasticsearch-logger-plugin-config
spec:
plugins:
- name: elasticsearch-logger
config:
endpoint_addrs:
- "http://elasticsearch.aic.svc:9200"
field:
index: "gateway-{%Y.%m.%d}"
auth:
username: gateway_logger
password: gateway-logger-password
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: elasticsearch-logger-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: elasticsearch-logger-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: elasticsearch-logger-route
spec:
ingressClassName: apisix
http:
- name: elasticsearch-logger-route
match:
paths:
- /anything
methods:
- GET
upstreams:
- name: httpbin-external-domain
plugins:
- name: elasticsearch-logger
enable: true
config:
endpoint_addrs:
- "http://elasticsearch.aic.svc:9200"
field:
index: "gateway-{%Y.%m.%d}"
auth:
username: gateway_logger
secretRef: elasticsearch-logger-credentials
Apply the configuration:
kubectl apply -f elasticsearch-logger-ic.yaml
❶ Configure the endpoint address to Elasticsearch.
❷ Configure the index field to use the current year, month, and date.
Send a request to the route to generate a log entry:
curl -i "http://127.0.0.1:9080/anything"
You should receive an HTTP/1.1 200 OK response.
In Kibana, create a data view with the index pattern gateway-*. The log entry should use an index name containing the request date:
{
"_index": "gateway-2026.09.14",
"_id": "CE-KL5QB0kdYRG7dEiTJ",
"_version": 1,
"_score": 1,
"_source": {
"request": {
...
},
"response": {
"status": 200,
"size": 618,
...
}
},
...
}