syslog
The syslog plugin pushes request and response logs as JSON objects to syslog servers in batches and supports the customization of log formats.
Examples
The examples below demonstrate how you can configure syslog plugin for different scenarios.
To follow along the examples, prepare a syslog server:
- Docker
- Kubernetes
docker run -d -p 514:514/tcp --name example-rsyslog-server rsyslog/syslog_appliance_alpine
To view the logs received by the server:
docker logs -f example-rsyslog-server
Create a Kubernetes manifest for a sample TCP syslog receiver:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: example-rsyslog-server
spec:
replicas: 1
selector:
matchLabels:
app: example-rsyslog-server
template:
metadata:
labels:
app: example-rsyslog-server
spec:
containers:
- name: tcp-syslog
image: alpine/socat
args:
- -v
- TCP-LISTEN:514,reuseaddr,fork
- STDOUT
ports:
- containerPort: 514
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: example-rsyslog-server
spec:
selector:
app: example-rsyslog-server
ports:
- name: tcp-syslog
port: 514
targetPort: 514
protocol: TCP
type: ClusterIP
Apply the manifest:
kubectl apply -f syslog-server.yaml
To view the logs received by the server:
kubectl logs -n aic deploy/example-rsyslog-server -f
Push Log to Syslog Server
The following example demonstrates how you can enable the syslog plugin on a route, which logs client requests to the route and pushes logs to a syslog server.
Create a route with syslog as follows:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "syslog-route",
"uri": "/anything",
"plugins": {
"syslog": {
"host": "127.0.0.1",
"port": 514,
"flush_limit": 1
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
services:
- name: httpbin
routes:
- name: syslog-route
uris:
- /anything
plugins:
syslog:
host: 127.0.0.1
port: 514
flush_limit: 1
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- 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: syslog-plugin-config
spec:
plugins:
- name: syslog
config:
host: example-rsyslog-server.aic.svc.cluster.local
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: example-rsyslog-server.aic.svc.cluster.local
port: 514
flush_limit: 1
Apply the configuration:
kubectl apply -f syslog-ic.yaml
❶ host: replace with the address of your syslog server. For Kubernetes, use the in-cluster Service address such as example-rsyslog-server.aic.svc.cluster.local.
❷ 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.
In the syslog server, you should see a log entry similar to the following:
{
"response": {
"status": 200,
"headers": {
"access-control-allow-credentials": "true",
"connection": "close",
"date": "Fri, 17 Apr 2026 05:39:46 GMT",
"access-control-allow-origin": "*",
"server": "APISIX/3.16.0",
"content-type": "application/json",
"content-length": "387"
},
"size": 614
},
"service_id": "",
"client_ip": "172.19.0.1",
"server": {
"hostname": "eff61bf7be4d",
"version": "3.16.0"
},
"upstream": "35.171.123.176:80",
"apisix_latency": 13.999900817871,
"request": {
"method": "GET",
"url": "http://127.0.0.1:9080/anything",
"querystring": {},
"size": 86,
"uri": "/anything",
"headers": {
"host": "127.0.0.1:9080",
"accept": "*/*",
"user-agent": "curl/7.29.0"
}
},
"route_id": "syslog-route",
"upstream_latency": 165,
"latency": 178.99990081787,
"start_time": 1709334859598
}
Customize Log Format With Plugin Metadata
The following example demonstrates how you can customize log format using plugin metadata. The log format configured in plugin metadata will apply to all syslog plugin instances.
Create a route with the syslog plugin:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "syslog-route",
"uri": "/anything",
"plugins": {
"syslog": {
"host": "127.0.0.1",
"port": 514,
"flush_limit": 1
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
services:
- name: httpbin
routes:
- name: syslog-route
uris:
- /anything
plugins:
syslog:
host: 127.0.0.1
port: 514
flush_limit: 1
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- 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: syslog-plugin-config
spec:
plugins:
- name: syslog
config:
host: example-rsyslog-server.aic.svc.cluster.local
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: example-rsyslog-server.aic.svc.cluster.local
port: 514
flush_limit: 1
Apply the configuration:
kubectl apply -f syslog-ic.yaml
Configure plugin metadata for syslog:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/syslog" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"log_format": {
"host": "$host",
"@timestamp": "$time_iso8601",
"route_id": "$route_id",
"client_ip": "$remote_addr",
"resp_content_type": "$sent_http_Content_Type"
}
}'
plugin_metadata:
- name: syslog
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
route_id: "$route_id"
client_ip: "$remote_addr"
resp_content_type: "$sent_http_Content_Type"
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: GatewayProxy
metadata:
namespace: aic
name: apisix-config
spec:
provider:
type: ControlPlane
controlPlane:
# ...
# your control plane connection configuration
pluginMetadata:
syslog:
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
route_id: "$route_id"
client_ip: "$remote_addr"
resp_content_type: "$sent_http_Content_Type"
apiVersion: apisix.apache.org/v1alpha1
kind: GatewayProxy
metadata:
namespace: aic
name: apisix-config
spec:
provider:
type: ControlPlane
controlPlane:
# ...
# your control plane connection configuration
pluginMetadata:
syslog:
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
route_id: "$route_id"
client_ip: "$remote_addr"
resp_content_type: "$sent_http_Content_Type"
Apply the configuration:
kubectl apply -f gatewayproxy.yaml
Send a request to the route:
curl -i "http://127.0.0.1:9080/anything"
In the syslog server, you should see a log entry 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": "172.19.0.1"
}
Log Request Bodies Conditionally
The following example demonstrates how you can conditionally log request body.
Create a route with the syslog plugin as follows:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "syslog-route",
"uri": "/anything",
"plugins": {
"syslog": {
"host": "127.0.0.1",
"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"
}
}'
services:
- name: httpbin
routes:
- name: syslog-route
uris:
- /anything
plugins:
syslog:
host: 127.0.0.1
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
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- 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: syslog-plugin-config
spec:
plugins:
- name: syslog
config:
host: example-rsyslog-server.aic.svc.cluster.local
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: example-rsyslog-server.aic.svc.cluster.local
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:
{
"response": {
"status": 200,
"headers": {
"connection": "close",
"server": "APISIX/3.16.0",
"date": "Fri, 17 Apr 2026 05:55:06 GMT",
"access-control-allow-origin": "*",
"access-control-allow-credentials": "true",
"content-type": "application/json",
"content-length": "531"
},
"size": 759
},
"service_id": "",
"client_ip": "172.19.0.1",
"server": {
"hostname": "eff61bf7be4d",
"version": "3.16.0"
},
"upstream": "35.171.123.176:80",
"apisix_latency": 0,
"request": {
"method": "POST",
"url": "http://127.0.0.1:9080/anything?log_body=yes",
"querystring": {
"log_body": "yes"
},
"size": 164,
"body": "{\"env\":\"dev\"}",
"uri": "/anything?log_body=yes",
"headers": {
"accept": "*/*",
"user-agent": "curl/7.29.0",
"host": "127.0.0.1:9080",
"content-type": "application/json",
"content-length": "13"
}
},
"route_id": "syslog-route",
"upstream_latency": 892,
"latency": 1011.0001564026,
"start_time": 1709340364390
}
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.
If you have customized the log_format in addition to setting include_req_body or include_resp_body to true, the plugin would not include the bodies in the logs.
As a workaround, you may be able to use the NGINX variable $request_body in the log format, such as:
{
"syslog": {
...,
"log_format": {"body": "$request_body"}
}
}