news 2026/9/21 19:22:42

Apache APISIX tencent-cloud-cls 插件实战:将网关访问日志实时上报腾讯云 CLS

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Apache APISIX tencent-cloud-cls 插件实战:将网关访问日志实时上报腾讯云 CLS
  • API网关
  • 后端
  • 云原生
  • 微服务

【免费下载链接】apisix

The Cloud-Native API Gateway and AI Gateway

项目地址:https://gitcode.com/gh_mirrors/api/apisix
点击查看免费下载

本文是 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_hoststringCLS API 主机地址(Host),如ap-guangzhou.cls.tencentyun.com,具体参见腾讯云「上传结构化日志」接口文档。
cls_topicstring目标 CLS 的 Topic ID。
schemestringhttps["http", "https"]连接 CLS 时使用的协议,默认https保证链路安全。
secret_idstringAPI 密钥的 SecretId。
secret_keystringAPI 密钥的 SecretKey,属于加密存储字段。
sample_rationumber1[0.00001, 1]请求采样比例,1表示采集全部请求。
include_req_bodybooleanfalse[false, true]true时在日志中附带请求体;若请求体过大无法驻留内存,则受 NGINX 限制无法记录。
include_req_body_exprarrayinclude_req_body配合使用的过滤表达式,仅当表达式求值为true时才记录请求体,语法参考 lua-resty-expr。
max_req_body_bytesinteger524288>=1允许记录的最大请求体字节数,超过该值会截断后再记录。
include_resp_bodybooleanfalse[false, true]true时在日志中附带响应体。
include_resp_body_exprarrayinclude_resp_body配合使用的过滤表达式,仅当求值为true时记录响应体。
max_resp_body_bytesinteger524288>=1允许记录的最大响应体字节数,超过该值会截断后再记录。
global_tagobjectJSON 形式的键值对,随每条日志一起发送。
log_formatobject以 JSON 键值对声明的自定义日志格式。值支持字符串和嵌套对象(最多嵌套五层,更深字段会被截断);字符串内可用$前缀引用 APISIX 变量 或 NGINX 变量。
log_format_extraobject默认日志条目之上追加的额外日志字段,保留全部默认字段而非替换(与log_format不同)。取值语法与log_format相同;设置了log_format时该项被忽略。

必填项与加密存储

cls_hostcls_topicsecret_idsecret_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_sizebuffer_durationmax_retry_countretry_delayinactive_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) end

sample_ratio1时全量采集;否则按随机概率决定本次请求是否进入日志,并在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实现可以看出,日志条目的生成遵循以下优先级:

  1. 若插件配置或插件元数据中设置了log_format,则生成自定义格式日志;
  2. 否则使用get_full_log生成上述完整默认日志,并将log_format_extra声明的额外字段追加到默认字段之上(绝不覆盖已有默认字段);
  3. global_tag中配置的键值对最后合并进条目。

通过插件元数据定制日志格式

除了在 Route 上配置log_format,你还可以通过插件元数据(Plugin Metadata)全局设置日志格式,对所有使用该插件的 Route 和 Service 同时生效

可用元数据如下:

名称类型必填默认值描述
log_formatobject以 JSON 键值对声明的日志格式,值支持字符串与嵌套对象(最多五层),字符串内可用$引用 APISIX/NGINX 变量。
log_format_extraobject在默认日志条目之上追加的额外字段,语法同log_format,设置log_format时被忽略。
max_pending_entriesinteger8192等待处理的最大条目数。积压超过该值时新条目将被丢弃,避免日志服务器变慢或不可达时无限制增长 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=sha1q-akq-sign-timeq-key-timeq-signature等。

2. protobuf 序列化

CLS 结构化日志接口要求以application/x-protobuf内容类型提交LogGroupList消息。SDK 在运行时通过 lua-protobuf 动态加载内嵌的cls.proto定义(包含LogLogTagLogGroupLogGroupList四个消息),将日志条目编码为二进制后再通过resty.http以 POST 方式发送到:

{scheme}://{cls_host}/structuredlog?topic_id={cls_topic}

3. 大小限制与分批发送

  • 单条日志的单个字段值最大1 MBMAX_SINGLE_VALUE_SIZE),超出会被截断并记录警告;
  • 单条日志总体积与单个LogGroup累计体积上限均为5 MBMAX_LOG_GROUP_VALUE_SIZE),超限日志会被丢弃,且发送时会按 5 MB 边界自动拆分为多个LogGroupList分批上传;
  • 每条日志会带上本机 IP 作为source字段(首次通过 DNS 解析主机名得到,结果全局缓存);
  • 连接超时 1000 ms、发送与读取超时各 10000 ms。

4. 失败处理与重试语义

send_cls_request中,HTTP 状态码为413404401403时视为不可重试错误直接放弃;其余错误(如500)会返回失败,由批量处理器依据max_retry_countretry_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):mocksend_to_clssend_cls_request,断言LogGroupListLogGroupLogcontents的层级结构正确;
  • 元数据日志格式(TEST 9/10):通过元数据设置log_format后,验证上报日志包含host@timestampclient_ip等自定义字段;
  • 密钥加密存储(TEST 12):开启data_encryption后,通过 Admin API 读取到的是解密后的明文secret_key,而从 etcd 直接读取到的是密文(如oshn8tcqE8cJArmEILVNPQ==),印证了encrypt_fields机制的实际效果。

使用建议

  • 生产环境务必使用https(默认值),并保持ssl_verifytrue,避免凭证与日志内容在传输中被窃取;
  • 请求/响应体采集会带来内存与性能开销,建议仅在排障场景开启,并结合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

项目地址:https://gitcode.com/gh_mirrors/api/apisix
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 19:22:12

5个高频坑:搞懂“表示的拼音”在编码中的避坑指南

5个高频坑:搞懂“表示的拼音”在编码中的避坑指南 看了一堆教程还是不会写项目?别急,问题往往出在那些你觉得“太简单”的基础概念上。比如,当面试官问你“表示的拼音”在底层系统或国际化项目中是如何处理时,很多候选人卡壳了。这不仅仅是一个语言学问题,更是 编码规范、内存管理和跨平台兼容性…

作者头像 李华
网站建设 2026/9/21 19:21:36

洛克王国化蝶3个坑点,面试必问的底层逻辑拆解

洛克王国化蝶3个坑点,面试必问的底层逻辑拆解 屏幕前正对着满屏红色报错发呆的朋友,听我说句掏心窝子的话: 报错一堆看不懂 StackTrace,其实是因为你只看了表象,没看底层机制。 别慌,这不仅是新手村的通关密码,更是各大厂 Java…

作者头像 李华
网站建设 2026/9/21 19:21:31

3招搞定问卷星怎么导出数据,从入门到精通避坑指南

3招搞定问卷星怎么导出数据,从入门到精通避坑指南 配置环境就卡半天,这大概是很多刚接触自动化办公或数据处理的开发者最真实的写照。你明明只是想从问卷星里拉取几百条用户反馈,结果在 Python 环境配置、Selenium…

作者头像 李华
网站建设 2026/9/21 19:21:26

clientX 坐标错乱全解析:前端老手避坑完整示例

clientX 坐标错乱全解析:前端老手避坑完整示例 刚接手前端项目,最让人头大的往往不是复杂的业务逻辑,而是那些看似简单却总在细节上坑人的原生 API。很多新手照着教程敲代码, clientX 一写上去,鼠标点哪它就在哪,感觉挺顺。但一旦项目跑起来,涉及到滚动、缩放或者复杂布局,坐标直接飞了。…

作者头像 李华
网站建设 2026/9/21 19:21:13

2026最新eovideo实战:5个致命坑与修复方案

2026最新eovideo实战:5个致命坑与修复方案 盯着满屏红色的StackTrace,脑子直接宕机。刚在掘金技术社区看到2026最新的项目案例,发现eovideo底层机制变了,老代码全报错。别慌,这五个坑我全踩过,今天一次性讲透。 坑一:环境版本不匹配导致启动崩溃 现象 :运行 eovideo…

作者头像 李华
网站建设 2026/9/21 19:21:11

什么是直播源码解析

3步吃透直播底层:一文搞懂从协议到代码 别再对着文档发呆了。如果你也是那种看了一堆教程,感觉每个概念都懂,但真上手写项目时脑子一片空白,代码敲出来全是Bug,那这篇内容就是为你准备的。…

作者头像 李华