rocketmq-logger
The rocketmq-logger plugin sends gateway request and response logs to Apache RocketMQ in batches. It supports the default structured log format, the original HTTP request format, and customized log fields.
Examples
The examples below configure the rocketmq-logger plugin for common logging scenarios.
To follow the examples, start a RocketMQ NameServer and broker. The Docker setup uses a dedicated network so that a running APISIX or API7 Gateway container can reach RocketMQ by container name.
- 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-rocketmq-net
docker network connect gateway-rocketmq-net "$GATEWAY_CONTAINER"
Create the Docker Compose file:
services:
rocketmq-namesrv:
image: apache/rocketmq:5.5.0
container_name: rocketmq-namesrv
restart: unless-stopped
command: ["nameserver"]
networks:
- gateway-rocketmq-net
rocketmq-broker:
image: apache/rocketmq:5.5.0
container_name: rocketmq-broker
restart: unless-stopped
depends_on:
- rocketmq-namesrv
environment:
NAMESRV_ADDR: rocketmq-namesrv:9876
command: ["broker", "-n", "rocketmq-namesrv:9876"]
networks:
- gateway-rocketmq-net
networks:
gateway-rocketmq-net:
external: true
Start containers:
docker compose up -d
Wait for the broker to register with the NameServer:
until docker exec rocketmq-namesrv sh mqadmin clusterList \
-n rocketmq-namesrv:9876 2>/dev/null | grep -q "DefaultCluster"; do
sleep 2
done
Create the TopicTest topic:
docker exec rocketmq-namesrv sh mqadmin updateTopic \
-n rocketmq-namesrv:9876 \
-t TopicTest \
-c DefaultCluster
Create a Kubernetes manifest for the RocketMQ NameServer and broker:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: rocketmq-namesrv
spec:
replicas: 1
selector:
matchLabels:
app: rocketmq-namesrv
template:
metadata:
labels:
app: rocketmq-namesrv
spec:
containers:
- name: rocketmq-namesrv
image: apache/rocketmq:5.5.0
args:
- nameserver
ports:
- containerPort: 9876
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: rocketmq-namesrv
spec:
selector:
app: rocketmq-namesrv
ports:
- port: 9876
targetPort: 9876
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: rocketmq-broker
spec:
replicas: 1
selector:
matchLabels:
app: rocketmq-broker
template:
metadata:
labels:
app: rocketmq-broker
spec:
containers:
- name: rocketmq-broker
image: apache/rocketmq:5.5.0
args:
- broker
- -n
- rocketmq-namesrv:9876
env:
- name: NAMESRV_ADDR
value: rocketmq-namesrv:9876
ports:
- containerPort: 10909
- containerPort: 10911
- containerPort: 10912
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: rocketmq-broker
spec:
selector:
app: rocketmq-broker
ports:
- name: fast-listen
port: 10909
targetPort: 10909
- name: listen
port: 10911
targetPort: 10911
- name: ha-service
port: 10912
targetPort: 10912
Apply the manifests:
kubectl apply -f rocketmq-deployment.yaml
Wait for the deployments to become available:
kubectl rollout status -n aic deployment/rocketmq-namesrv
kubectl rollout status -n aic deployment/rocketmq-broker
Wait for the broker to register with the NameServer:
until kubectl exec -n aic deployment/rocketmq-namesrv -- \
sh mqadmin clusterList \
-n rocketmq-namesrv:9876 2>/dev/null | grep -q "DefaultCluster"; do
sleep 2
done
Create the TopicTest topic:
kubectl exec -n aic deployment/rocketmq-namesrv -- \
sh mqadmin updateTopic \
-n rocketmq-namesrv:9876 \
-t TopicTest \
-c DefaultCluster
Inspect RocketMQ Messages
After sending a request in the following examples, use the command for your environment to inspect messages in TopicTest:
- Docker
- Kubernetes
docker exec rocketmq-namesrv sh mqadmin printMsg \
-n rocketmq-namesrv:9876 \
-t TopicTest
kubectl exec -n aic deployment/rocketmq-namesrv -- \
sh mqadmin printMsg \
-n rocketmq-namesrv:9876 \
-t TopicTest
Compare Structured and Original Request Logs
The default metadata format sends a structured JSON log entry, while the origin format sends the original HTTP request. This example configures both formats so that you can compare their output.
Create a route with rocketmq-logger as follows:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/rocketmq-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/anything",
"plugins": {
"rocketmq-logger": {
"nameserver_list": [ "rocketmq-namesrv:9876" ],
"topic": "TopicTest",
"key": "key1",
"timeout": 30,
"meta_format": "default",
"batch_max_size": 1
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything
name: rocketmq-logger-route
plugins:
rocketmq-logger:
nameserver_list:
- "rocketmq-namesrv:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
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=rocketmq-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=rocketmq-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: rocketmq-logger-plugin-config
spec:
plugins:
- name: rocketmq-logger
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: rocketmq-logger-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: rocketmq-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: rocketmq-logger-route
spec:
ingressClassName: apisix
http:
- name: rocketmq-logger-route
match:
paths:
- /anything*
upstreams:
- name: httpbin-external-domain
plugins:
- name: rocketmq-logger
enable: true
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
Apply the configuration:
kubectl apply -f rocketmq-logger-ic.yaml
❶ meta_format: use the structured JSON log format.
❷ batch_max_size: send each log entry immediately for testing.
Send a request to the route to generate a log entry:
curl -i "http://127.0.0.1:9080/anything"
Use the inspection command for your environment. The message body should contain a log entry similar to the following:
{
"client_ip": "127.0.0.1",
"upstream": "34.197.122.172:80",
"start_time": 1789377954622,
"request": {
"headers": {
"host": "127.0.0.1:9080",
"accept": "*/*",
"user-agent": "curl/8.6.0"
},
"querystring": {},
"size": 86,
"uri": "/anything",
"url": "http://127.0.0.1:9080/anything",
"method": "GET"
},
"route_id": "rocketmq-logger-route",
"apisix_latency": 8.9998455047607,
"upstream_latency": 503,
"latency": 511.99984550476,
"response": {
"size": 617,
"headers": {
"content-length": "391",
"connection": "close",
"date": "Mon, 14 Sep 2026 09:25:54 GMT",
"server": "APISIX/3.18.0",
"content-type": "application/json"
},
"status": 200
},
"server": {
"hostname": "apisix",
"version": "3.18.0"
},
"service_id": ""
}
Update the rocketmq-logger meta log format to origin:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/rocketmq-logger-route" -X PATCH \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"plugins": {
"rocketmq-logger": {
"meta_format": "origin"
}
}
}'
Update adc.yaml to change meta_format to origin:
services:
- name: httpbin
routes:
- uris:
- /anything
name: rocketmq-logger-route
plugins:
rocketmq-logger:
nameserver_list:
- "rocketmq-namesrv:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "origin"
batch_max_size: 1
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=rocketmq-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=rocketmq-logger
- Gateway API
- APISIX CRD
Update rocketmq-logger-ic.yaml to change meta_format to origin in the PluginConfig:
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: rocketmq-logger-plugin-config
spec:
plugins:
- name: rocketmq-logger
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "origin"
batch_max_size: 1
Apply the updated configuration:
kubectl apply -f rocketmq-logger-ic.yaml
Update rocketmq-logger-ic.yaml to change meta_format to origin in the ApisixRoute:
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: rocketmq-logger-route
spec:
ingressClassName: apisix
http:
- name: rocketmq-logger-route
match:
paths:
- /anything*
upstreams:
- name: httpbin-external-domain
plugins:
- name: rocketmq-logger
enable: true
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "origin"
batch_max_size: 1
Apply the updated configuration:
kubectl apply -f rocketmq-logger-ic.yaml
Send a request to the route again to generate a new log entry:
curl -i "http://127.0.0.1:9080/anything"
Use the inspection command again. The new message body should contain the request in its original HTTP format:
GET /anything HTTP/1.1
host: 127.0.0.1:9080
user-agent: curl/8.6.0
accept: */*
Customize Log Fields With Plugin Metadata
Use plugin metadata to apply a common log format to every rocketmq-logger instance. The following configuration uses built-in variables to record a request header and a response header.
First, create a route with rocketmq-logger as follows:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/rocketmq-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/anything",
"plugins": {
"rocketmq-logger": {
"nameserver_list": [ "rocketmq-namesrv:9876" ],
"topic": "TopicTest",
"key": "key1",
"timeout": 30,
"meta_format": "default",
"batch_max_size": 1
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
services:
- name: httpbin
routes:
- uris:
- /anything
name: rocketmq-logger-route
plugins:
rocketmq-logger:
nameserver_list:
- "rocketmq-namesrv:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
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=rocketmq-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=rocketmq-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: rocketmq-logger-plugin-config
spec:
plugins:
- name: rocketmq-logger
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: rocketmq-logger-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: rocketmq-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: rocketmq-logger-route
spec:
ingressClassName: apisix
http:
- name: rocketmq-logger-route
match:
paths:
- /anything*
upstreams:
- name: httpbin-external-domain
plugins:
- name: rocketmq-logger
enable: true
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
Apply the configuration:
kubectl apply -f rocketmq-logger-ic.yaml
❶ meta_format: use default because plugin metadata does not customize logs in the origin format.
❷ batch_max_size: send each log entry immediately for testing.
Next, configure the plugin metadata for rocketmq-logger:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/rocketmq-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:
rocketmq-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. The resource-type filter also keeps the existing route and service unchanged:
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:
# ...
# your control plane connection configuration
pluginMetadata:
rocketmq-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 gatewayproxy.yaml
❶ Record the custom request header env.
❷ Record 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"
Use the inspection command. The message body should contain a log entry similar to the following:
{
"host": "127.0.0.1",
"client_ip": "127.0.0.1",
"resp_content_type": "application/json",
"route_id": "rocketmq-logger-route",
"env": "dev",
"@timestamp": "2026-09-14T09:28:24+00:00"
}
Log Request Bodies Conditionally
The following example records the request body only when the log_body query parameter is yes.
Create a route with rocketmq-logger as follows:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes/rocketmq-logger-route" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"plugins": {
"rocketmq-logger": {
"nameserver_list": [ "rocketmq-namesrv:9876" ],
"topic": "TopicTest",
"key": "key1",
"timeout": 30,
"meta_format": "default",
"batch_max_size": 1,
"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: rocketmq-logger-route
plugins:
rocketmq-logger:
nameserver_list:
- "rocketmq-namesrv:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
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=rocketmq-logger
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=rocketmq-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: rocketmq-logger-plugin-config
spec:
plugins:
- name: rocketmq-logger
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 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: rocketmq-logger-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: rocketmq-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: rocketmq-logger-route
spec:
ingressClassName: apisix
http:
- name: rocketmq-logger-route
match:
paths:
- /anything*
upstreams:
- name: httpbin-external-domain
plugins:
- name: rocketmq-logger
enable: true
config:
nameserver_list:
- "rocketmq-namesrv.aic.svc:9876"
topic: "TopicTest"
key: "key1"
timeout: 30
meta_format: "default"
batch_max_size: 1
include_req_body: true
include_req_body_expr:
- - "arg_log_body"
- "=="
- "yes"
Apply the configuration:
kubectl apply -f rocketmq-logger-ic.yaml
❶ include_req_body: enable request body logging.
❷ include_req_body_expr: record the body only when the log_body query parameter is yes.
Send a request with log_body=yes:
curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}'
Use the inspection command. The message body should include the request body:
{
...,
"request": {
...,
"method": "POST",
"body": "{\"env\": \"dev\"}",
"size": 183
}
}
Send another request without the query parameter:
curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}'
Use the inspection command again. The new message body should not include the request body.
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.