rocketmq-logger
rocketmq-logger 插件将请求和响应日志作为 JSON 对象以批处理的方式推送到 RocketMQ 集群,并支持自定义日志格式。
示例
以下示例展示了如何在不同场景下配置 rocketmq-logger 插件。
要按照示例操作,请使用以下 Docker compose 文件启动一个 RocketMQ 集群示例:
- Docker
- Kubernetes
version: "3"
services:
rocketmq_namesrv:
image: apacherocketmq/rocketmq:4.6.0
container_name: rmqnamesrv
restart: unless-stopped
ports:
- "9876:9876"
command: sh mqnamesrv
networks:
rocketmq_net:
rocketmq_broker:
image: apacherocketmq/rocketmq:4.6.0
container_name: rmqbroker
restart: unless-stopped
ports:
- "10909:10909"
- "10911:10911"
- "10912:10912"
depends_on:
- rocketmq_namesrv
command: sh mqbroker -n rmqnamesrv:9876 -c ../conf/broker.conf
networks:
rocketmq_net:
networks:
rocketmq_net:
启动容器:
docker compose up -d
几秒钟后,名称服务器和代理应该会启动。
创建 TopicTest 主题:
docker exec -i rmqnamesrv rm /home/rocketmq/rocketmq-4.6.0/conf/tools.yml
docker exec -i rmqnamesrv /home/rocketmq/rocketmq-4.6.0/bin/mqadmin updateTopic -n rmqnamesrv:9876 -t TopicTest -c DefaultCluster
为 RocketMQ NameServer 和 Broker Deployment 创建 Kubernetes 清单文件:
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: apacherocketmq/rocketmq:4.6.0
command: ["sh", "mqnamesrv"]
ports:
- containerPort: 9876
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: rocketmq-namesrv
spec:
selector:
app: rocketmq-namesrv
ports:
- port: 9876
targetPort: 9876
type: ClusterIP
---
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: apacherocketmq/rocketmq:4.6.0
command: ["sh", "mqbroker", "-n", "rocketmq-namesrv:9876", "-c", "../conf/broker.conf"]
ports:
- containerPort: 10909
- containerPort: 10911
- containerPort: 10912
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: rocketmq-broker
spec:
selector:
app: rocketmq-broker
ports:
- name: fastlisten
port: 10909
targetPort: 10909
- name: listen
port: 10911
targetPort: 10911
- name: haservice
port: 10912
targetPort: 10912
type: ClusterIP
在配置的 RocketMQ 主题中等待消息:
kubectl apply -f rocketmq-deployment.yaml
Pod 运行后,创建 TopicTest 主题:
kubectl exec -n aic deploy/rocketmq-namesrv -- sh -c \
"rm -f /home/rocketmq/rocketmq-4.6.0/conf/tools.yml && \
/home/rocketmq/rocketmq-4.6.0/bin/mqadmin updateTopic \
-n rocketmq-namesrv:9876 -t TopicTest -c DefaultCluster"
等待已配置的 RocketMQ 主题接收消息:
- Docker
- Kubernetes
docker run -it --name rockemq_consumer -e NAMESRV_ADDR=localhost:9876 --net host apacherocketmq/rocketmq:4.6.0 sh tools.sh org.apache.rocketmq.example.quickstart.Consumer
几秒钟后,消费者应该会启动并监听来自 APISIX 的消息:
01:32:17.823 [main] DEBUG i.n.u.i.l.InternalLoggerFactory - Using SLF4J as the default logging framework
Consumer Started.
打开一个新的终端会话,用于以下与 APISIX 交互的步骤。
在以下示例中向 APISIX 发送请求后,运行以下命令以输出 TopicTest 主题中的消息:
kubectl exec -n aic deploy/rocketmq-namesrv -- sh -c \
"/home/rocketmq/rocketmq-4.6.0/bin/mqadmin printMsg \
-n rocketmq-namesrv:9876 -t TopicTest"
使用不同的元日志格式记录日志
以下示例展示了如何在路由上启用 rocketmq-logger 插件,该插件记录对路由的客户端请求并将日志推送到 RocketMQ。你还将了解 default 和 origin 元日志格式之间的区别。
创建一个启用 rocketmq-logger 的路由,如下所示:
- 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": "rocketmq-logger-route",
"uri": "/anything",
"plugins": {
"rocketmq-logger": {
"nameserver_list": [ "127.0.0.1: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:
- "127.0.0.1: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 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: 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
应用配置:
kubectl apply -f rocketmq-logger-ic.yaml
❶ meta_format: 设置为 default 日志格式。
❷ batch_max_size: 设置为 1 以立即发送日志条目。
向路由发送请求以生成日志条目:
curl -i "http://127.0.0.1:9080/anything"
你应该在运行消费者的另一个终端中看到一个日志条目:
{
"client_ip": "127.0.0.1",
"upstream": "34.197.122.172:80",
"start_time": 1744727400000,
"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": "Tue, 15 Apr 2025 14:30:00 GMT",
"server": "APISIX/3.15.0",
"content-type": "application/json"
},
"status": 200
},
"server": {
"hostname": "apisix",
"version": "3.15.0"
},
"service_id": ""
}
将 rocketmq-logger 的元日志格式更新为 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"
}
}
}'
更新 adc.yaml,将 meta_format 修改为 origin:
services:
- name: httpbin
routes:
- uris:
- /anything
name: rocketmq-logger-route
plugins:
rocketmq-logger:
nameserver_list:
- "127.0.0.1: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 sync -f adc.yaml
- Gateway API
- APISIX CRD
更新 rocketmq-logger-ic.yaml,将 PluginConfig 中的 meta_format 更改为 origin:
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
应用更新后的配置:
kubectl apply -f rocketmq-logger-ic.yaml
更新 rocketmq-logger-ic.yaml,将 ApisixRoute 中的 meta_format 更改为 origin:
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
应用更新后的配置:
kubectl apply -f rocketmq-logger-ic.yaml
再次向路由发送请求以生成新的日志条目:
curl -i "http://127.0.0.1:9080/anything"
你应该在运行消费者的另一个终端中看到一个日志条目:
GET /anything HTTP/1.1
host: 127.0.0.1:9080
user-agent: curl/8.6.0
accept: */*
使用插件元数据记录请求和响应头
以下示例展示了如何使用插件元数据和内置变量自定义日志格式,以记录请求和响应中的特定请求头和响应头。
在 APISIX 中,插件元数据用于配置同一插件的所有插件实例的通用元数据字段。当插件在多个资源中启用并且需要对其元数据字段进行统一更新时,这非常有用。
首先,创建一个启用 rocketmq-logger 的路由,如下所示:
- 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": "rocketmq-logger-route",
"uri": "/anything",
"plugins": {
"rocketmq-logger": {
"nameserver_list": [ "127.0.0.1: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:
- "127.0.0.1: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 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: 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
应用配置:
kubectl apply -f rocketmq-logger-ic.yaml
❶ meta_format: 设置为 default 日志格式。需要注意的是,如果你想使用插件元数据自定义日志格式,这是强制性的。如果 meta_format 设置为 origin,日志条目将保持 origin 格式。
❷ batch_max_size: 设置为 1 以立即发送日志条目。
接下来,配置 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"
}
}'
plugin_metadata:
- name: rocketmq-logger
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
client_ip: "$remote_addr"
env: "$http_env"
resp_content_type: "$sent_http_Content_Type"
将配置同步到网关:
adc sync -f adc.yaml
apiVersion: apisix.apache.org/v1alpha1
kind: GatewayProxy
metadata:
namespace: aic
name: apisix-config
spec:
provider:
type: ControlPlane
controlPlane:
# ...
# 控制面连接配置
pluginMetadata:
rocketmq-logger:
log_format:
host: "$host"
"@timestamp": "$time_iso8601"
client_ip: "$remote_addr"
env: "$http_env"
resp_content_type: "$sent_http_Content_Type"
应用配置:
kubectl apply -f gatewayproxy.yaml
❶ 记录自定义请求头 env。
❷ 记录响应头 Content-Type。
向路由发送带有 env 头的请求:
curl -i "http://127.0.0.1:9080/anything" -H "env: dev"
你应该在运行消费者的另一个终端中看到一个日志条目:
{
"host": "127.0.0.1",
"client_ip": "127.0.0.1",
"resp_content_type": "application/json",
"route_id": "rocketmq-logger-route",
"env": "dev",
"@timestamp": "2025-04-15T14:30:00+00:00"
}
有条件地记录请求体
以下示例展示了如何有条件地记录请求体。
创建一个启用 rocketmq-logger 的路由,如下所示:
- 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 '{
"plugins": {
"rocketmq-logger": {
"nameserver_list": [ "127.0.0.1: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",
"id": "rocketmq-logger-route"
}'
services:
- name: httpbin
routes:
- uris:
- /anything
name: rocketmq-logger-route
plugins:
rocketmq-logger:
nameserver_list:
- "127.0.0.1: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 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: 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"
应用配置:
kubectl apply -f rocketmq-logger-ic.yaml
❶ include_req_body: 设置为 true 以包含请求体。
❷ include_req_body_expr: 仅当 URL 查询字符串 log_body 为 yes 时包含请求体。
向路由发送带有满足条件的 URL 查询字符串的请求:
curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}'
结果将是:
{
...,
"method": "POST",
"body": "{\"env\": \"dev\"}",
"size": 183
}
}
向路由发送不带任何 URL 查询字符串的请求:
curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}'
你不应该在日志中观察到请求体。
除了将 include_req_body 或 include_resp_body 设置为 true 之外,如果你自定义了 log_format,插件将不会在日志中包含 body。
作为一种解决方法,你也许能够在日志格式中使用 NGINX 变量 $request_body,例如:
{
"rocketmq-logger": {
...,
"log_format": {"body": "$request_body"}
}
}