- API网关
- 后端
- 云原生
- 微服务
【免费下载链接】apisix
The Cloud-Native API Gateway and AI Gateway
本文是 Apache APISIX 官方tencent-cloud-cls日志插件的完整技术指南。该插件通过腾讯云 CLS(Cloud Log Service)提供的结构化日志上传 API,把 APISIX 网关处理过的请求日志批量转发到你指定的 CLS Topic,供检索、告警与离线分析使用。读完本文,你将掌握该插件的全部配置属性、日志格式定制方式、启用与下线方法,并能结合源码理解其采样、批量上报与签名认证的实现原理。
插件概述
tencent-cloud-cls是一个典型的日志类插件:在 APISIX 的日志阶段(logphase)收集请求上下文,经过批量处理器(Batch Processor)聚合后,通过 CLS 的structuredlog上传接口写入指定的日志主题。
插件主文件位于 apisix/plugins/tencent-cloud-cls.lua,从源码可以确认:
- 插件优先级(
priority)为397,版本号为0.1; - 插件 Schema 通过
batch_processor_manager:wrap_schema(schema)包装,因此自动继承了批量处理器的全部配置项; - 底层上报逻辑封装在独立的 CLS SDK 模块 apisix/plugins/tencent-cloud-cls/cls-sdk.lua 中,负责腾讯云签名、protobuf 序列化与 HTTP 发送。
属性配置详解
启用插件时,可以在 Route、Service、Consumer 或 Plugin Config 上按以下属性进行配置:
| 名称 | 类型 | 必填 | 默认值 | 合法取值 | 描述 |
|---|---|---|---|---|---|
| cls_host | string | 是 | CLS API 主机地址(Host),如ap-guangzhou.cls.tencentyun.com,具体参见腾讯云「上传结构化日志」接口文档。 | ||
| cls_topic | string | 是 | 目标 CLS 的 Topic ID。 | ||
| scheme | string | 否 | https | ["http", "https"] | 连接 CLS 时使用的协议,默认https保证链路安全。 |
| secret_id | string | 是 | API 密钥的 SecretId。 | ||
| secret_key | string | 是 | API 密钥的 SecretKey,属于加密存储字段。 | ||
| sample_ratio | number | 否 | 1 | [0.00001, 1] | 请求采样比例,1表示采集全部请求。 |
| include_req_body | boolean | 否 | false | [false, true] | 为true时在日志中附带请求体;若请求体过大无法驻留内存,则受 NGINX 限制无法记录。 |
| include_req_body_expr | array | 否 | 与include_req_body配合使用的过滤表达式,仅当表达式求值为true时才记录请求体,语法参考 lua-resty-expr。 | ||
| max_req_body_bytes | integer | 否 | 524288 | >=1 | 允许记录的最大请求体字节数,超过该值会截断后再记录。 |
| include_resp_body | boolean | 否 | false | [false, true] | 为true时在日志中附带响应体。 |
| include_resp_body_expr | array | 否 | 与include_resp_body配合使用的过滤表达式,仅当求值为true时记录响应体。 | ||
| max_resp_body_bytes | integer | 否 | 524288 | >=1 | 允许记录的最大响应体字节数,超过该值会截断后再记录。 |
| global_tag | object | 否 | JSON 形式的键值对,随每条日志一起发送。 | ||
| log_format | object | 否 | 以 JSON 键值对声明的自定义日志格式。值支持字符串和嵌套对象(最多嵌套五层,更深字段会被截断);字符串内可用$前缀引用 APISIX 变量 或 NGINX 变量。 | ||
| log_format_extra | object | 否 | 在默认日志条目之上追加的额外日志字段,保留全部默认字段而非替换(与log_format不同)。取值语法与log_format相同;设置了log_format时该项被忽略。 |
必填项与加密存储
cls_host、cls_topic、secret_id、secret_key四项为必填。源码中的required声明如下(apisix/plugins/tencent-cloud-cls.lua):
encrypt_fields = {"secret_key"}, required = { "cls_host", "cls_topic", "secret_id", "secret_key" }其中encrypt_fields = {"secret_key"}意味着secret_key会以加密形式存储在 etcd 中,属于加密存储字段机制,避免密钥明文落盘。
值得留意的是,源码 Schema 中还定义了文档属性表未列出的ssl_verify字段(type = "boolean", default = true),用于控制上报请求是否校验 CLS 服务端证书,默认开启。
批量处理能力
该插件支持使用批量处理器聚合日志,避免频繁提交数据。默认情况下,批量处理器每 5 秒提交一次数据,或当队列中数据达到1000 条时立即提交。你可以通过插件的batch_max_size、buffer_duration、max_retry_count、retry_delay、inactive_timeout等参数覆盖默认行为,详细说明见批量处理器配置。
采样与请求体读取的源码实现
从源码可以看到采样逻辑实现在access阶段(apisix/plugins/tencent-cloud-cls.lua):
function _M.access(conf, ctx) ctx.cls_sample = false if conf.sample_ratio == 1 or math.random() < conf.sample_ratio then core.log.debug("cls sampled") ctx.cls_sample = true else return end log_util.check_and_read_req_body(conf, ctx) endsample_ratio为1时全量采集;否则按随机概率决定本次请求是否进入日志,并在body_filter阶段调用log_util.collect_body按需收集响应体。请求体/响应体的表达式过滤(include_req_body_expr/include_resp_body_expr)与体积截断(max_req_body_bytes/max_resp_body_bytes)逻辑统一实现在 apisix/utils/log-util.lua,其中响应体会优先尝试按Content-Encoding解压后再记录。
默认日志格式示例
未设置log_format时,每条日志的默认结构如下(字段含义:client_ip客户端 IP、route_id路由 ID、service_id服务 ID、latency总延迟、apisix_latencyAPISIX 内部延迟、upstream_latency上游延迟、start_time请求起始时间戳(毫秒)等):
{ "response": { "headers": { "content-type": "text/plain", "connection": "close", "server": "APISIX/3.7.0", "transfer-encoding": "chunked" }, "size": 136, "status": 200 }, "route_id": "1", "upstream": "127.0.0.1:1982", "client_ip": "127.0.0.1", "apisix_latency": 100.99985313416, "service_id": "", "latency": 103.99985313416, "start_time": 1704525145772, "server": { "version": "3.7.0", "hostname": "localhost" }, "upstream_latency": 3, "request": { "headers": { "connection": "close", "host": "localhost" }, "url": "http://localhost:1984/opentracing", "querystring": {}, "method": "GET", "size": 65, "uri": "/opentracing" } }从 apisix/utils/log-util.lua 的get_log_entry实现可以看出,日志条目的生成遵循以下优先级:
- 若插件配置或插件元数据中设置了
log_format,则生成自定义格式日志; - 否则使用
get_full_log生成上述完整默认日志,并将log_format_extra声明的额外字段追加到默认字段之上(绝不覆盖已有默认字段); global_tag中配置的键值对最后合并进条目。
通过插件元数据定制日志格式
除了在 Route 上配置log_format,你还可以通过插件元数据(Plugin Metadata)全局设置日志格式,对所有使用该插件的 Route 和 Service 同时生效。
可用元数据如下:
| 名称 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
| log_format | object | 否 | 以 JSON 键值对声明的日志格式,值支持字符串与嵌套对象(最多五层),字符串内可用$引用 APISIX/NGINX 变量。 | |
| log_format_extra | object | 否 | 在默认日志条目之上追加的额外字段,语法同log_format,设置log_format时被忽略。 | |
| max_pending_entries | integer | 否 | 8192 | 等待处理的最大条目数。积压超过该值时新条目将被丢弃,避免日志服务器变慢或不可达时无限制增长 worker 内存,相关内存开销见批量处理器积压限制。 |
注意:插件元数据的配置是全局作用域的,会影响所有使用
tencent-cloud-cls插件的 Route 与 Service。
通过 Admin API 配置元数据的示例(先获取admin_key):
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/tencent-cloud-cls \ -H "X-API-KEY: $admin_key" -X PUT -d ' { "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "request": { "method": "$request_method", "uri": "$request_uri" }, "response": { "status": "$status" } } }'配置生效后,日志将按如下紧凑格式输出(可见自定义格式会自动附带route_id字段):
{"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","request":{"method":"GET","uri":"/hello"},"response":{"status":200},"route_id":"1"} {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","request":{"method":"GET","uri":"/hello"},"response":{"status":200},"route_id":"1"}启用插件
以下示例在/hello路由上启用tencent-cloud-cls插件,同时开启请求体与响应体采集,并为每条日志附加global_tag标记:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: $admin_key" -X PUT -d ' { "plugins": { "tencent-cloud-cls": { "cls_host": "ap-guangzhou.cls.tencentyun.com", "cls_topic": "${your CLS topic name}", "global_tag": { "module": "cls-logger", "server_name": "YourApiGateWay" }, "include_req_body": true, "include_resp_body": true, "secret_id": "${your secret id}", "secret_key": "${your secret key}" } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } }, "uri": "/hello" }'启用后向网关发起请求,即可在 CLS Topic 中查看到对应日志:
curl -i http://127.0.0.1:9080/hello禁用插件
需要下线该插件时,将路由配置中plugins下的tencent-cloud-cls配置删除即可。APISIX 会自动热加载生效,无需重启:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: $admin_key" -X PUT -d ' { "uri": "/hello", "plugins": {}, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }'底层上报原理:签名、序列化与批量发送
插件的日志上报由 apisix/plugins/tencent-cloud-cls/cls-sdk.lua 完成,从源码可以梳理出完整链路:
1. 腾讯云签名认证
SDK 内实现了腾讯云 CLS 的 SHA1 签名算法(对应官方「请求签名」规范):以POST方法、/structuredlog路径、空参数与空请求头构造http_request_info,再依次生成q-sign-time(有效期 60 秒)、string_to_sign与 HMAC-SHA1 签名,最终拼装出Authorization请求头。签名参数包括q-sign-algorithm=sha1、q-ak、q-sign-time、q-key-time、q-signature等。
2. protobuf 序列化
CLS 结构化日志接口要求以application/x-protobuf内容类型提交LogGroupList消息。SDK 在运行时通过 lua-protobuf 动态加载内嵌的cls.proto定义(包含Log、LogTag、LogGroup、LogGroupList四个消息),将日志条目编码为二进制后再通过resty.http以 POST 方式发送到:
{scheme}://{cls_host}/structuredlog?topic_id={cls_topic}3. 大小限制与分批发送
- 单条日志的单个字段值最大1 MB(
MAX_SINGLE_VALUE_SIZE),超出会被截断并记录警告; - 单条日志总体积与单个
LogGroup累计体积上限均为5 MB(MAX_LOG_GROUP_VALUE_SIZE),超限日志会被丢弃,且发送时会按 5 MB 边界自动拆分为多个LogGroupList分批上传; - 每条日志会带上本机 IP 作为
source字段(首次通过 DNS 解析主机名得到,结果全局缓存); - 连接超时 1000 ms、发送与读取超时各 10000 ms。
4. 失败处理与重试语义
send_cls_request中,HTTP 状态码为413、404、401、403时视为不可重试错误直接放弃;其余错误(如500)会返回失败,由批量处理器依据max_retry_count、retry_delay等配置进行重试。测试用例(见下文)中模拟的 500 响应即验证了该重试路径。
测试用例验证
插件行为在测试文件 t/plugin/tencent-cloud-cls.t 中有完整覆盖,可作为配置与行为的权威参考:
- Schema 校验(TEST 1/2):验证合法配置通过校验、缺少
secret_key时报property "secret_key" is required; - 批量上报失败与成功(TEST 3-6):分别向返回 500 的模拟服务器与正常服务器上报,断言错误日志
Batch Processor[tencent-cloud-cls] failed to process entries [1/1]: got wrong status: 500与成功日志successfully processed the entries; - 请求结构验证(TEST 7/8):mock
send_to_cls与send_cls_request,断言LogGroupList、LogGroup、Log、contents的层级结构正确; - 元数据日志格式(TEST 9/10):通过元数据设置
log_format后,验证上报日志包含host、@timestamp、client_ip等自定义字段; - 密钥加密存储(TEST 12):开启
data_encryption后,通过 Admin API 读取到的是解密后的明文secret_key,而从 etcd 直接读取到的是密文(如oshn8tcqE8cJArmEILVNPQ==),印证了encrypt_fields机制的实际效果。
使用建议
- 生产环境务必使用
https(默认值),并保持ssl_verify为true,避免凭证与日志内容在传输中被窃取; - 请求/响应体采集会带来内存与性能开销,建议仅在排障场景开启,并结合
include_req_body_expr/include_resp_body_expr精确限定采集范围,同时用max_req_body_bytes/max_resp_body_bytes控制单条日志体积; - 高流量场景下优先依赖批量处理器聚合上报,并通过
global_tag附加业务维度标签,便于在 CLS 中按模块、网关实例等维度过滤检索; - 若日志字段较多,推荐通过元数据配置
log_format精简字段,既降低存储成本,也让 CLS 检索索引更聚焦。
- API网关
- 后端
- 云原生
- 微服务
【免费下载链接】apisix
The Cloud-Native API Gateway and AI Gateway
相关推荐
Apache APISIX tencent-cloud-cls 插件实战:把网关访问日志结构化写入腾讯云 CLS
Apache APISIX tencent cloud cls 插件实战:把网关访问日志结构化写入腾讯云 CLS 导读 tencent cloud cls 是
后端微服务云原生Apache APISIX tencent-cloud-cls 插件实战:将网关访问日志批量推送至腾讯云日志服务
Apache APISIX tencent cloud cls 插件实战:将网关访问日志批量推送至腾讯云日志服务 tencent cloud cls 是 Apa
后端微服务云原生3分钟快速上手:Windows上最轻量级安卓应用安装器完全指南
3分钟快速上手:Windows上最轻量级安卓应用安装器完全指南 你是否曾想过在Windows电脑上直接运行安卓应用,而不需要臃肿的安卓模拟器?APK Insta
API网关后端云原生微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考