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:
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-nameCreate a dedicated network:
docker network create gateway-rsyslog-netConnect 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-04Set the collector hostname used by the APISIX Admin API and ADC examples:
export SYSLOG_HOST=rsyslog-collectorIn a separate terminal, follow logs received by the collector:
docker exec rsyslog-collector tail -f /var/log/all.logCreate 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: TCPApply the manifest:
kubectl apply -f syslog-server.yamlWait for the collector to become ready:
kubectl rollout status -n aic deployment/rsyslog-collectorSet the collector hostname used by the APISIX Admin API and ADC examples:
export SYSLOG_HOST=rsyslog-collector.aic.svcIn a separate terminal, follow logs received by the collector:
kubectl exec -n aic deploy/rsyslog-collector -- \
tail -f /var/log/all.logSend 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:
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"
}
}
EOFservices:
- 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: 1Preview the changes to services with the example label:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-loggingSynchronize the reviewed changes:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-loggingapiVersion: 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: 80apiVersion: 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: 1Apply 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:
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"
}
}
EOFservices:
- 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: 1Preview the changes to services with the example label:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-loggingSynchronize the reviewed changes:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-loggingapiVersion: 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: 80apiVersion: 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: 1Apply the configuration:
kubectl apply -f syslog-ic.yamlConfigure plugin metadata for syslog:
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_metadataAdd 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_metadataSynchronize the reviewed plugin metadata collection:
adc sync -f adc-metadata.yaml \
--include-resource-type plugin_metadataAdd 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:
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"
}
}
EOFservices:
- 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: 1Preview the changes to services with the example label:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-loggingSynchronize the reviewed changes:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=syslog-loggingapiVersion: 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: 80apiVersion: 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.
info
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.