syslog
The syslog plugin sends APISIX and API7 Gateway request and response logs as JSON objects to syslog servers in batches. It supports TCP, UDP, TLS, and customizable log formats.
Examples
The examples show how to send gateway request logs to a syslog collector, customize their contents, and conditionally include request bodies.
To follow the examples, prepare a syslog collector:
- Docker
- Kubernetes
The pinned rsyslog collector image is published for amd64. Set the running APISIX or API7 Gateway container name:
export GATEWAY_CONTAINER=replace-with-gateway-container-name
Create a dedicated network:
docker network create gateway-rsyslog-net
Connect the gateway to the network:
docker network connect gateway-rsyslog-net "$GATEWAY_CONTAINER"
Start the collector with TCP reception enabled:
docker run -d \
--platform linux/amd64 \
--name rsyslog-collector \
--network gateway-rsyslog-net \
-e ENABLE_TCP=on \
-e ENABLE_UDP=off \
rsyslog/rsyslog-collector:2026-04
Set the collector hostname used by the APISIX Admin API and ADC examples:
export SYSLOG_HOST=rsyslog-collector
In a separate terminal, follow logs received by the collector:
docker exec rsyslog-collector tail -f /var/log/all.log
Create a Kubernetes manifest for a sample TCP syslog receiver:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: rsyslog-collector
spec:
replicas: 1
selector:
matchLabels:
app: rsyslog-collector
template:
metadata:
labels:
app: rsyslog-collector
spec:
nodeSelector:
kubernetes.io/arch: amd64
containers:
- name: rsyslog-collector
image: rsyslog/rsyslog-collector:2026-04
env:
- name: ENABLE_TCP
value: "on"
- name: ENABLE_UDP
value: "off"
ports:
- name: syslog-tcp
containerPort: 514
protocol: TCP
readinessProbe:
tcpSocket:
port: syslog-tcp
initialDelaySeconds: 2
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: rsyslog-collector
spec:
selector:
app: rsyslog-collector
ports:
- name: syslog-tcp
port: 514
targetPort: syslog-tcp
protocol: TCP
Apply the manifest:
kubectl apply -f syslog-server.yaml
Wait for the collector to become ready:
kubectl rollout status -n aic deployment/rsyslog-collector
Set the collector hostname used by the APISIX Admin API and ADC examples:
export SYSLOG_HOST=rsyslog-collector.aic.svc
In a separate terminal, follow logs received by the collector:
kubectl exec -n aic deploy/rsyslog-collector -- \
tail -f /var/log/all.log
Send Logs to a Syslog Server
The following example enables the syslog plugin on a route and sends logs for matching requests to the collector.
Create a route with syslog as follows:
- APISIX Admin API
- ADC
- Ingress Controller (Kubernetes)
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d @- <<EOF
{
"id": "syslog-route",
"uri": "/anything",
"plugins": {
"syslog": {
"host": "$SYSLOG_HOST",
"port": 514,
"flush_limit": 1
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}
EOF
services:
- name: httpbin
labels:
docs-example: syslog-logging
routes:
- name: syslog-route
uris:
- /anything
plugins:
syslog:
host: "${SYSLOG_HOST}"
port: 514
flush_limit: 1
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Preview the changes to services with the example label:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-logging
Synchronize the reviewed changes:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-logging
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: syslog-plugin-config
spec:
plugins:
- name: syslog
config:
host: rsyslog-collector.aic.svc
port: 514
flush_limit: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: syslog-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: syslog-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: syslog-route
spec:
ingressClassName: apisix
http:
- name: syslog-route
match:
paths:
- /anything
methods:
- GET
upstreams:
- name: httpbin-external-domain
plugins:
- name: syslog
enable: true
config:
host: rsyslog-collector.aic.svc
port: 514
flush_limit: 1
Apply the configuration:
kubectl apply -f syslog-ic.yaml
❶ Configure the collector hostname reachable from the gateway.
❷ port: replace with the port of your syslog server.
❸ flush_limit: set to 1 to push logs to the syslog server immediately.
Send a request to the route:
curl -i "http://127.0.0.1:9080/anything"
You should receive an HTTP/1.1 200 OK response.
The collector prefixes each received line with syslog metadata. The JSON message should be similar to the following:
{
"upstream": "100.31.16.17:80",
"service_id": "",
"client_ip": "192.168.155.1",
"response": {
"headers": {
"date": "Mon, 21 Sep 2026 09:44:46 GMT",
"content-type": "application/json",
"access-control-allow-origin": "*",
"server": "APISIX/3.18.0",
"access-control-allow-credentials": "true",
"connection": "close",
"content-length": "399"
},
"size": 627,
"status": 200
},
"start_time": 1789983884322,
"apisix_latency": 650.00001144409,
"latency": 1513.0000114441,
"upstream_latency": 863,
"request": {
"size": 85,
"uri": "/anything",
"headers": {
"x-forwarded-proto": "http",
"user-agent": "curl/8.7.1",
"accept": "*/*",
"host": "127.0.0.1:9080",
"x-forwarded-port": "9080",
"x-forwarded-host": "127.0.0.1:9080"
},
"querystring": {},
"url": "http://127.0.0.1:9080/anything",
"method": "GET"
},
"route_id": "syslog-route",
"server": {
"version": "3.18.0",
"hostname": "dd2886d0b7bf"
}
}
Add Fields With Plugin Metadata
The following example uses plugin metadata and built-in variables to add selected request and response fields to syslog instances that do not define their own log_format_extra.
Create a route with the syslog plugin:
- APISIX Admin API
- ADC
- Ingress Controller (Kubernetes)
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d @- <<EOF
{
"id": "syslog-route",
"uri": "/anything",
"plugins": {
"syslog": {
"host": "$SYSLOG_HOST",
"port": 514,
"flush_limit": 1
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}
EOF
services:
- name: httpbin
labels:
docs-example: syslog-logging
routes:
- name: syslog-route
uris:
- /anything
plugins:
syslog:
host: "${SYSLOG_HOST}"
port: 514
flush_limit: 1
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Preview the changes to services with the example label:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-logging
Synchronize the reviewed changes:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-logging
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: syslog-plugin-config
spec:
plugins:
- name: syslog
config:
host: rsyslog-collector.aic.svc
port: 514
flush_limit: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: syslog-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: syslog-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: syslog-route
spec:
ingressClassName: apisix
http:
- name: syslog-route
match:
paths:
- /anything
methods:
- GET
upstreams:
- name: httpbin-external-domain
plugins:
- name: syslog
enable: true
config:
host: rsyslog-collector.aic.svc
port: 514
flush_limit: 1
Apply the configuration:
kubectl apply -f syslog-ic.yaml
Configure plugin metadata for syslog:
- APISIX Admin API
- ADC
- Ingress Controller (Kubernetes)
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/syslog" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"log_format_extra": {
"host": "$host",
"@timestamp": "$time_iso8601",
"route_id": "$route_id",
"client_ip": "$remote_addr",
"resp_content_type": "$sent_http_Content_Type"
}
}'
Plugin metadata is a global collection and cannot be isolated with a label selector. Export the complete collection before changing this entry:
adc dump -o adc-metadata.yaml --with-id \
--include-resource-type plugin_metadata
Add or update the syslog entry while preserving every other entry in adc-metadata.yaml:
plugin_metadata:
# Keep all other plugin metadata entries from the exported file.
syslog:
log_format_extra:
host: "$host"
"@timestamp": "$time_iso8601"
route_id: "$route_id"
client_ip: "$remote_addr"
resp_content_type: "$sent_http_Content_Type"
Preview the complete metadata change and confirm that it contains no unintended updates or deletions:
adc diff -f adc-metadata.yaml \
--include-resource-type plugin_metadata
Synchronize the reviewed plugin metadata collection:
adc sync -f adc-metadata.yaml \
--include-resource-type plugin_metadata
Add the following entry under spec.pluginMetadata in the complete GatewayProxy manifest used by the deployment:
syslog:
log_format_extra:
host: "$host"
"@timestamp": "$time_iso8601"
route_id: "$route_id"
client_ip: "$remote_addr"
resp_content_type: "$sent_http_Content_Type"
Apply the updated complete manifest through the deployment's normal Kubernetes or GitOps workflow.
Send a request to the route:
curl -i "http://127.0.0.1:9080/anything"
In the collector log, the JSON message should include fields similar to the following:
{
"@timestamp": "2026-04-17T05:39:46+00:00",
"resp_content_type": "application/json",
"host": "127.0.0.1",
"route_id": "syslog-route",
"client_ip": "192.168.155.1"
}
Log Request Bodies Conditionally
The following example includes request bodies only when a query parameter satisfies a configured expression.
Create a route with the syslog plugin as follows:
- APISIX Admin API
- ADC
- Ingress Controller (Kubernetes)
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d @- <<EOF
{
"id": "syslog-route",
"uri": "/anything",
"plugins": {
"syslog": {
"host": "$SYSLOG_HOST",
"port": 514,
"flush_limit": 1,
"include_req_body": true,
"include_req_body_expr": [["arg_log_body", "==", "yes"]]
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}
EOF
services:
- name: httpbin
labels:
docs-example: syslog-logging
routes:
- name: syslog-route
uris:
- /anything
plugins:
syslog:
host: "${SYSLOG_HOST}"
port: 514
flush_limit: 1
include_req_body: true
include_req_body_expr:
- - arg_log_body
- ==
- "yes"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Preview the changes to services with the example label:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-logging
Synchronize the reviewed changes:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-logging
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: syslog-plugin-config
spec:
plugins:
- name: syslog
config:
host: rsyslog-collector.aic.svc
port: 514
flush_limit: 1
include_req_body: true
include_req_body_expr:
- - arg_log_body
- ==
- "yes"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: syslog-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: syslog-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: syslog-route
spec:
ingressClassName: apisix
http:
- name: syslog-route
match:
paths:
- /anything
methods:
- POST
upstreams:
- name: httpbin-external-domain
plugins:
- name: syslog
enable: true
config:
host: rsyslog-collector.aic.svc
port: 514
flush_limit: 1
include_req_body: true
include_req_body_expr:
- - arg_log_body
- ==
- "yes"
Apply the configuration:
kubectl apply -f syslog-ic.yaml
❶ include_req_body: set to true to include request body.
❷ include_req_body_expr: only include request body if the URL query string log_body is yes. In YAML-based configurations, quote "yes" to avoid YAML converting it to a boolean.
Send a request to the route with a URL query string satisfying the condition:
curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST \
-H "Content-Type: application/json" \
-d '{"env":"dev"}'
You should see the request body logged:
{
"upstream": "52.71.230.193:80",
"service_id": "",
"client_ip": "192.168.155.1",
"response": {
"headers": {
"date": "Mon, 21 Sep 2026 09:19:41 GMT",
"content-type": "application/json",
"access-control-allow-origin": "*",
"access-control-allow-credentials": "true",
"server": "APISIX/3.18.0",
"connection": "close",
"content-length": "543"
},
"size": 771,
"status": 200
},
"start_time": 1789982377612,
"apisix_latency": 2.0000038146973,
"latency": 3921.0000038147,
"upstream_latency": 3919,
"request": {
"size": 164,
"body": "{\"env\":\"dev\"}",
"uri": "/anything?log_body=yes",
"headers": {
"x-forwarded-port": "9080",
"content-type": "application/json",
"x-forwarded-proto": "http",
"user-agent": "curl/8.7.1",
"accept": "*/*",
"host": "127.0.0.1:9080",
"x-forwarded-host": "127.0.0.1:9080",
"content-length": "13"
},
"querystring": {
"log_body": "yes"
},
"url": "http://127.0.0.1:9080/anything?log_body=yes",
"method": "POST"
},
"route_id": "syslog-route",
"server": {
"version": "3.18.0",
"hostname": "dd2886d0b7bf"
}
}
Send a request to the route without any URL query string:
curl -i "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-d '{"env":"dev"}'
You should not observe the request body in the log.
The log_format_extra field shown above preserves the default log entry, including request and response bodies collected by the plugin. If you configure log_format instead, include the corresponding variables explicitly:
{
"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.