ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Apache APISIX tencent-cloud-cls 插件实战:把网关访问日志结构化写入腾讯云 CLS

Apache APISIX tencent-cloud-cls 插件实战:把网关访问日志结构化写入腾讯云 CLS Apache APISIX tencent-cloud-cls 插件实战把网关访问日志结构化写入腾讯云 CLS【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix导读tencent-cloud-cls是 Apache APISIX 内置的一款日志类插件它把 APISIX 网关处理过的每一条请求按照腾讯云日志服务 CLSCloud Log Service的协议与鉴权规范批量、结构化地上报到你指定的日志主题Topic中。本文以官方文档为主线结合 插件主实现、CLS SDK 实现 与 单元测试完整讲解插件的全部配置属性、Metadata 全局日志格式、启用与下线步骤、批量处理机制以及底层如何完成腾讯云签名、Protobuf 编码和分片上报。读完本文你将能够独立把 APISIX 的请求日志接入腾讯云 CLS并针对采样、请求/响应体采集和日志格式做精细调优。插件概述与适用场景tencent-cloud-cls位于 插件源码目录通过调用腾讯云 CLS 的结构化日志上传接口将 APISIX 日志转发到指定 Topic。从源码可以看出它属于典型的日志上报类插件运行阶段集中在access、body_filter和log三个钩子见 tencent-cloud-cls.luaaccess按sample_ratio决定本次请求是否采样命中则打上ctx.cls_sample标记body_filter当请求命中采样且配置了响应体采集时收集响应体内容log构造日志条目追加global_tag交给批处理器统一上报。在 插件元信息 中插件priority 397说明它会在日志阶段与其他插件按优先级顺序执行。与http-logger、kafka-logger等日志插件类似它同样复用了 APISIX 的批量处理器框架batch processor来聚合日志避免每条请求都触发一次外呼。属性配置详解插件 schema 定义在 tencent-cloud-cls.lua 的 schema 表 中官方文档列出的全部属性如下名称类型必填默认值取值范围说明cls_hoststring是——CLS API 主机名即结构化日志上传接口的域名例如ap-guangzhou.cls.tencentyun.comcls_topicstring是——CLS 日志主题 IDTopic IDsecret_idstring是——腾讯云 API 密钥的 SecretIdsecret_keystring是——腾讯云 API 密钥的 SecretKeysample_rationumber否1[0.00001, 1]请求采样比例1表示采样全部请求include_req_bodyboolean否false[false, true]为true时在日志中包含请求体若请求体过大无法常驻内存受 NGINX 限制将无法记录include_req_body_exprarray否——请求体采集过滤表达式仅当include_req_body为true时生效表达式求值为true才记录请求体语法参考 lua-resty-exprinclude_resp_bodyboolean否false[false, true]为true时在日志中包含响应体include_resp_body_exprarray否——响应体采集过滤表达式语义同include_req_body_exprglobal_tagobject否——JSON 键值对随每条日志一同发送log_formatobject否——以 JSON 键值对声明的日志格式值只支持字符串可用$前缀引用 APISIX 变量或 Nginx 变量其中cls_host、cls_topic、secret_id、secret_key为必填项缺任一字段都会在 Admin API 校验阶段直接报错。这一点在测试 TEST 2: cls config missing 中有明确验证——只提供三个字段时返回property secret_key is required。采样比例 sample_ratiosample_ratio的实现见 access 阶段当配置值为1或math.random() conf.sample_ratio时标记采样命中否则后续log阶段直接跳过上报见 log 阶段 的if not ctx.cls_sample then return。因此设为1所有请求都上报设为0.5约一半请求上报设为0.00001约十万分之一请求上报适合极低比例的抽样观察。注意该判断在每次请求时基于随机数独立进行属于概率采样而非固定路由采样。请求体与响应体采集include_req_body/include_resp_body与对应的*_expr表达式配合使用实际采集逻辑位于 log-util.lua 的 collect_body 与get_log_entrylog-util.luainclude_req_body_expr与include_resp_body_expr在插件校验阶段通过 check_log_schema 用lua-resty-expr预编译校验表达式非法会在配置时即被拒绝采集请求体时受内存限制若请求体过大无法完整保存在内存中将无法记录NGINX 限制响应体采集发生在body_filter阶段tencent-cloud-cls.lua通过core.response.hold_body_chunk暂存响应体分片。测试用例也覆盖了表达式过滤的两种走向请求?barbar命中表达式时错误日志中出现body:body-data请求?foobar不命中时日志中不出现响应体内容见 TEST 17-TEST 20。敏感字段加密官方文档明确指出 schema 中定义了encrypt_fields {secret_key}意味着secret_key会以密文形式存储在 etcd 中参见 插件开发文档中的加密存储字段说明。在 tencent-cloud-cls.lua 可以看到encrypt_fields {secret_key}声明。测试 TEST 12: data encryption for secret_key 完整演示了这一行为开启data_encryption.enable_encrypt_fields后通过 Admin API 读取到的secret_key是明文secret_key而直接从 etcd 读到的值则是密文如oshn8tcqE8cJArmEILVNPQ。这要求部署时在config.yaml中启用数据加密并配置 keyring具体见 数据加密相关配置。日志格式 log_formatlog_format支持在插件配置与 Metadata 两个层级声明值为 JSON 键值对键为日志字段名值仅支持字符串可用$前缀引用 APISIX 内置变量 或 Nginx 变量。其求值逻辑在 log-util.lua 的 get_custom_format_log 分支中完成插件级log_format优先级高于 Metadata 级。默认日志格式示例未配置log_format时插件会使用 APISIX 的完整默认日志结构官方文档给出了一个典型输出{ 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 } }该结构包含请求method、uri、url、querystring、headers、size、响应status、size、headers、路由与服务标识、客户端 IP、上游地址以及start_time、latency、apisix_latency、upstream_latency等时间指标适合直接作为 CLS 的结构化字段用于检索分析。通过 Metadata 配置全局日志格式除插件级配置外还可以通过插件 Metadata 设置日志格式配置项如下名称类型必填默认值说明log_formatobject否—以 JSON 键值对声明的日志格式值只支持字符串可用$前缀引用 APISIX 变量或 Nginx 变量:::info 重要提示 Metadata 的配置是全局生效的一旦设置将作用于所有使用tencent-cloud-cls插件的 Route 与 Service。 :::使用 Admin API 配置 Metadata 的完整示例如下。首先从config.yaml中取出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 } }配置生效后上报到 CLS 的每条日志将只包含自定义字段形如{host:localhost,timestamp:2020-09-23T19:05:05-04:00,client_ip:127.0.0.1,route_id:1} {host:localhost,timestamp:2020-09-23T19:05:05-04:00,client_ip:127.0.0.1,route_id:1}注意示例输出中的route_id字段是由插件自动附加的见 log 阶段对 global_tag 与默认字段的处理可据此定位日志对应的路由。Metadata 生效逻辑在 get_log_entry 中插件会读取plugin.plugin_metadata(plugin_name)当 Metadata 中存在非空log_format且插件配置未显式声明log_format时使用 Metadata 中的格式。测试 TEST 9/TEST 10 验证了 Metadata 配置后日志中确实出现host、timestamp、client_ip三个字段。在 Route 上启用插件以下示例为路由1配置tencent-cloud-cls插件将/hello的请求日志上报到广州地域的 CLScurl 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_host必须与你的 CLS 地域对应使用内网域名如ap-guangzhou.cls.tencentyun.com或公网域名均可按部署网络环境选择cls_topic填写腾讯云控制台中的日志主题 IDglobal_tag中的键值对会被追加到每条日志条目顶层实现见 log 阶段适合打上模块名、网关实例名等静态标签便于在 CLS 中区分来源同时开启include_req_body与include_resp_body后日志会携带请求与响应体请结合上文提到的内存限制与表达式过滤按需使用。插件 schema 校验与生效可参考测试 TEST 1: schema check 与 TEST 5: add plugin。验证日志上报配置完成后发起一次请求即可触发上报curl -i http://127.0.0.1:9080/hello随后登录腾讯云 CLS 控制台在对应 Topic 的检索页面即可看到该请求的结构化日志。若本地不方便连接真实 CLS仓库测试通过一个模拟的/structuredlog接口验证了完整链路测试 TEST 5/TEST 6 将cls_host指向本地127.0.0.1:10420请求经过网关后错误日志中会出现Batch Processor[tencent-cloud-cls] successfully processed the entries说明日志已成功上报。批量处理机制插件默认使用批处理器聚合日志避免每条请求都发起一次外呼。官方文档说明批处理器每5秒提交一次或当队列中的数据达到1000条时提交。详细配置项见 批处理器文档名称类型必填默认值说明namestring否插件名批处理器唯一标识batch_max_sizeinteger否1000每批最多发送的日志条数达到上限自动推送inactive_timeoutinteger否5缓冲区最大刷新时间秒到期后无论数量是否达标都推送buffer_durationinteger否60批次中最早一条日志的最大存活时长秒超时强制处理max_retry_countinteger否0处理失败时的最大重试次数retry_delayinteger否1处理失败后延迟重试的秒数这些参数会通过batch_processor_manager:wrap_schema(schema)合入插件 schematencent-cloud-cls.lua因此可以在插件配置中直接覆盖默认值例如在测试中常用的batch_max_size: 1, max_retry_count: 1, retry_delay: 2, buffer_duration: 2, inactive_timeout: 2batch_max_size设为1表示每条日志立即上报便于调试生产环境建议保持较大批次以降低调用频率。批处理器的重试与丢弃逻辑见 batch-processor.lua 的 execute_func失败时按retry_delay延时重试超过max_retry_count后丢弃并打印exceeded the max_retry_count... dropping the entries测试 TEST 4: incorrect server 演示了上报失败时的日志Batch Processor[tencent-cloud-cls] failed to process entries [1/1]: got wrong status: 500。底层实现CLS SDK 的上报原理日志实际发送由 cls-sdk.lua 完成该模块封装了腾讯云结构化日志上传接口的完整调用流程可以从以下三个层面理解其原理1. 腾讯云签名鉴权CLS 接口要求每次请求携带基于 HMAC-SHA1 的签名。签名逻辑见 sign 函数构造http_request_info请求方法、路径/structuredlog、空参数与空头列表生成签名时间窗q-sign-time当前时间 60 秒有效期见auth_expire_time 60依次计算sign_key HMAC-SHA1(secret_key, sign_time)与最终签名拼接为q-sign-algorithmsha1q-ak...q-sign-time...q-key-time...q-signature...形式的 Authorization 值。2. Protobuf 编码与字段规范化CLS 的结构化日志上传采用 Protobuf 协议。SDK 在 init_pb_state 中内嵌加载了cls.proto包含Log、LogTag、LogGroup、LogGroupList消息定义随后在 send_cls_request 中通过pb.encode(cls.LogGroupList, pb_obj)编码请求体并以Content-Type: application/x-protobuf发送。在编码前normalize_log 会把日志条目转换为 CLS 的键值字段字符串直接取值数字转字符串嵌套 table 用 JSON 序列化同时限制单个字段值不超过 1MBMAX_SINGLE_VALUE_SIZE超过则截断并告警。3. 分批发送与重试语义send_to_cls 负责把批处理器攒下的一批日志拆分为多个 LogGroup 上报所有字段累计大小不得超过 5MBMAX_LOG_GROUP_VALUE_SIZE超过则拆分为多次请求单条日志超过 5MB 会被直接丢弃并打印错误每个 LogGroup 会附带source字段本机 IP通过 DNS 解析主机名获得解析失败时上报错误日志测试 TEST 16 覆盖该场景发送结果非 200 时视为失败其中 413、404、401、403 属于不可重试错误见 send_cls_request其余错误交由批处理器按max_retry_count重试。网络超时方面SDK 设置了连接超时 1000ms、发送超时 10000ms、读取超时 10000mscls-sdk.lua。代码中还留有-- TODO: support lz4/zstd compress注释cls-sdk.lua说明当前版本暂未启用请求体压缩。关闭插件要停用该插件只需删除 Route 配置中对应的 JSON 配置块。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 } } }将plugins置为空对象后/hello路由不再上报任何 CLS 日志。注意事项与最佳实践密钥安全secret_key在 etcd 中以加密形式存储务必在config.yaml中开启deployment.data_encryptionkeyring 配置参考 conf/config.yaml.example并妥善管理 keyring 密钥地域匹配cls_host需与 CLS 日志主题所在地域一致避免跨地域网络延迟或鉴权失败控制日志体积请求体/响应体采集会显著增加日志量并受内存限制建议结合include_req_body_expr/include_resp_body_expr只采集关键请求如仅当特定参数存在时或通过sample_ratio降低采样比例批量参数调优高吞吐场景可适当调大batch_max_size与buffer_duration减少请求次数调试阶段可临时将batch_max_size设为 1 观察实时上报并通过inactive_timeout控制刷新频率日志格式统一优先通过 Plugin Metadata 定义全局log_format避免在每个 Route 上重复声明插件级log_format会覆盖 Metadata 级配置请注意两者优先级排查链路上报失败时在 error.log 中关注Batch Processor[tencent-cloud-cls] failed to process entries、resolve ip failed、size of log is over 5MB, dropped等关键词可结合 测试用例 中的模拟服务快速复现。延伸阅读插件完整实现CLS SDK 与签名/编码实现插件测试用例批量处理器配置说明APISIX 变量参考插件开发指南含加密存储字段说明【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表