Skip to main content

Log with Elasticsearch

Elasticsearch is a popular JSON-based datastore for storing and indexing large volumes of data. It is often used to store logs from various sources and works with tools like Logstash and Kibana to form an entire observability stack known as the Elastic (ELK) Stack.

APISIX supports forwarding its logs directly to Elasticsearch through the elasticsearch-logger plugin. These logs can then be searched, filtered, and visualized through Kibana to gather insights to manage applications.

This guide will show you how to enable the elasticsearch-logger plugin to integrate APISIX with the ELK stack for observability.

Prerequisite(s)

Start Elasticsearch and Kibana

Set passwords for the built-in Elasticsearch users used in this local example:

export ELASTIC_PASSWORD="apisix-elastic-pass"
export KIBANA_SYSTEM_PASSWORD="apisix-kibana-pass"

Create a data volume and start Elasticsearch with authentication enabled:

docker volume create apisix-quickstart-elasticsearch-data

docker run -d --name apisix-quickstart-elasticsearch \
--network apisix-quickstart-net \
-p 9200:9200 \
-e discovery.type=single-node \
-e xpack.security.enabled=true \
-e xpack.security.http.ssl.enabled=false \
-e ELASTIC_PASSWORD="${ELASTIC_PASSWORD}" \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-v apisix-quickstart-elasticsearch-data:/usr/share/elasticsearch/data \
docker.elastic.co/elasticsearch/elasticsearch:9.4.2

❶ Attach Elasticsearch to the APISIX quickstart network so that APISIX and Kibana can reach it by container name.

❷ Disable HTTP TLS only for this isolated local example. In production, use HTTPS endpoints and keep the plugin's ssl_verify option enabled so that APISIX verifies the Elasticsearch certificate.

❸ Persist Elasticsearch data in the named Docker volume created above.

Wait up to two minutes for Elasticsearch to start, then verify that authentication succeeds:

curl --fail --silent --show-error \
--user "elastic:${ELASTIC_PASSWORD}" \
--retry 24 \
--retry-delay 5 \
--retry-connrefused \
--retry-max-time 120 \
--max-time 5 \
--output /dev/null \
"http://127.0.0.1:9200"

Set the password Kibana uses to connect to Elasticsearch:

curl -fsS -u "elastic:${ELASTIC_PASSWORD}" \
"http://127.0.0.1:9200/_security/user/kibana_system/_password" \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"password": "${KIBANA_SYSTEM_PASSWORD}"
}
EOF

Create a data volume and start the matching Kibana version:

docker volume create apisix-quickstart-kibana-data

docker run -d --name apisix-quickstart-kibana \
--network apisix-quickstart-net \
-p 5601:5601 \
-e ELASTICSEARCH_HOSTS="http://apisix-quickstart-elasticsearch:9200" \
-e ELASTICSEARCH_USERNAME="kibana_system" \
-e ELASTICSEARCH_PASSWORD="${KIBANA_SYSTEM_PASSWORD}" \
-v apisix-quickstart-kibana-data:/usr/share/kibana/data \
docker.elastic.co/kibana/kibana:9.4.2

Enable elasticsearch-logger Plugin

Enable elasticsearch-logger globally and create a sample route to generate logs. Alternatively, you can enable the plugin on a route.

Enable the elasticsearch-logger plugin on all routes:

curl "http://127.0.0.1:9180/apisix/admin/global_rules/elasticsearch" -X PUT \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"plugins": {
"elasticsearch-logger": {
"endpoint_addrs": [
"http://apisix-quickstart-elasticsearch:9200"
],
"field": {
"index": "gateway-{%Y.%m.%d}"
},
"auth": {
"username": "elastic",
"password": "${ELASTIC_PASSWORD}"
},
"timeout": 60,
"retry_delay": 1,
"buffer_duration": 60,
"max_retry_count": 0,
"batch_max_size": 5,
"inactive_timeout": 5
}
}
}
EOF

❶ Create a daily index by resolving the date expression, for example, to gateway-2026.07.21.

Create a sample route. Setting pass_host to node sends the upstream hostname in the Host header:

curl "http://127.0.0.1:9180/apisix/admin/routes/observability-logs" -X PUT \
-H "Content-Type: application/json" \
-d '{
"uri": "/get",
"upstream": {
"type": "roundrobin",
"pass_host": "node",
"nodes": {
"postman-echo.com:80": 1
}
}
}'

Customize Log Format

Customize the log format for elasticsearch-logger before generating traffic. The log format of most APISIX logging plugins can be customized locally on the plugin (e.g. bound to a route) and/or globally with plugin metadata.

Add host address, timestamp, and client IP address to the logs with built-in variables:

curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger" -X PUT \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"log_format": {
"host": "$host",
"timestamp": "$time_iso8601",
"client_ip": "$remote_addr"
}
}
EOF

Configure Kibana

Send ten requests, then allow the inactive buffer to flush to Elasticsearch:

for i in {1..10}; do
curl -sS "http://127.0.0.1:9080/get" > /dev/null
done

sleep 6

List the date-based indices:

curl -u "elastic:${ELASTIC_PASSWORD}" \
"http://127.0.0.1:9200/_cat/indices/gateway-*?v"

You should see a gateway-YYYY.MM.DD index with documents. Inspect one indexed document:

curl -u "elastic:${ELASTIC_PASSWORD}" \
"http://127.0.0.1:9200/gateway-*/_search?pretty&size=1"

The document _source should contain host, timestamp, and client_ip fields.

View Logs in Kibana

Open Kibana and sign in as elastic with the password stored in ELASTIC_PASSWORD.

Go to Stack Management > Data Views, select Create data view, and configure these values:

  • Name: APISIX logs
  • Index pattern: gateway-*
  • Timestamp field: timestamp
Kibana data view form configured for APISIX gateway indices

Select Save data view to Kibana, then go to Discover and select the APISIX logs data view. The indexed gateway requests should be available for searching and filtering.

Kibana Discover showing APISIX log documents from Elasticsearch

Next Steps

See elasticsearch-logger plugin reference to learn more about the plugin configuration options.