ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Apache APISIX clickhouse-logger 插件:将网关访问日志批量写入 ClickHouse 的完整指南

2026/9/14 19:53:09 拓冰建站 浏览量
Apache APISIX clickhouse-logger 插件:将网关访问日志批量写入 ClickHouse 的完整指南 Apache APISIX clickhouse-logger 插件将网关访问日志批量写入 ClickHouse 的完整指南【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix导读本文围绕 Apache APISIX 的clickhouse-logger插件展开系统讲解如何将 API 网关产生的访问日志以批量聚合的方式写入 ClickHouse 列式数据库。读完本文你将掌握插件的全部配置参数、自定义日志格式含 Metadata 全局配置、请求/响应体采集过滤以及通过 Admin API 完成插件的启用、验证与移除的完整实操流程。插件概述clickhouse-logger是 Apache APISIX 官方提供的一个日志类插件用于将请求访问日志推送到 ClickHouse 数据库。ClickHouse 作为一款高性能的列式分析数据库非常适合承载网关的海量访问日志便于后续进行流量分析、安全审计与监控告警。从源码结构看该插件位于 apisix/plugins/clickhouse-logger.lua其运行优先级为398属于在请求生命周期末尾执行的日志类插件local _M { version 0.1, priority 398, name plugin_name, schema batch_processor_manager:wrap_schema(schema), metadata_schema metadata_schema, }插件通过log钩子收集请求上下文借助Batch Processor批处理器将日志聚合后批量提交避免每条日志都触发一次网络请求从而大幅降低对 ClickHouse 的连接压力。属性Attributes详解插件的 Schema 定义在 apisix/plugins/clickhouse-logger.lua各属性如下名称类型必填默认值有效值说明endpoint_addr已弃用是请改用endpoint_addrs。ClickHouse 端点地址。endpoint_addrsarray是ClickHouse 端点地址数组配置多个时会随机选择写入。databasestring是存放日志的数据库名称。logtablestring是存放日志的数据表名。userstring是ClickHouse 用户名。passwordstring是ClickHouse 密码。timeoutinteger否3[1,...]发送请求后保持连接的时间秒。namestring否clickhouse logger日志器的唯一标识。若用 Prometheus 监控 APISIX 指标该名称会导出在apisix_batch_process_entries中。ssl_verifyboolean否true[true,false]设为true时校验 SSL。log_formatobject否以 JSON 键值对声明的日志格式值仅支持字符串。可以用$前缀引用 APISIX 变量 或 Nginx 变量。include_req_bodyboolean否false[false, true]设为true时在日志中包含请求体。若请求体过大无法保存在内存中受 Nginx 限制将无法记录。include_req_body_exprarray否当include_req_body为true时的过滤条件仅当此处表达式求值为真时才记录请求体。include_resp_bodyboolean否false[false, true]设为true时在日志中包含响应体。include_resp_body_exprarray否当include_resp_body为true时的过滤条件仅当此处表达式求值为真时才记录响应体。必须字段与校验逻辑源码中通过oneOf约束了两种必填组合两种组合都需要user、password、database、logtable区别仅在端点字段oneOf { {required {endpoint_addr, user, password, database, logtable}}, {required {endpoint_addrs, user, password, database, logtable}} },endpoint_addr为历史遗留字段已弃用推荐统一使用数组形式的endpoint_addrs。此外check_schema还会对 HTTPS 端点和ssl_verify做一致性校验见 clickhouse-logger.luafunction _M.check_schema(conf, schema_type) if schema_type core.schema.TYPE_METADATA then return core.schema.check(metadata_schema, conf) end local check {endpoint_addrs} core.utils.check_https(check, conf, plugin_name) core.utils.check_tls_bool({ssl_verify}, conf, plugin_name) return core.schema.check(schema, conf) end密码加密存储插件的 Schema 中声明了encrypt_fields {password}这意味着password字段在写入 etcd 时会以加密形式存储。详见 plugin-develop.md 的 encrypted storage fields 章节该能力要求 APISIX 3.1.0。启用方式是在 conf/config.yaml 中开启数据加密apisix: data_encryption: enable: true keyring: - edd1c9f0985e76a2 - qeddd145sfvddff4开启后通过 Admin API 新增/更新资源时encrypt_fields声明的参数会被自动加密后存入 etcd通过 Admin API 读取以及插件运行时APISIX 会自动解密。解密时按keyring中的 key 顺序尝试全部失败则使用原始数据。批处理器Batch Processor该插件基于批处理器聚合日志避免频繁提交数据。批处理器每5秒inactive_timeout或队列中的数据达到1000条batch_max_size时提交一次。完整的批处理配置说明见 batch-processor.md其可配置项为名称类型必填默认值说明namestring否日志器名称批处理器唯一标识默认取调用它的插件名。batch_max_sizeinteger否1000每个批次最多发送的日志条数达到上限立即推送。inactive_timeoutinteger否5缓冲刷新最大等待时间秒到期后无论是否达到条数上限都会推送。buffer_durationinteger否60批次中最旧一条日志的最大存活时间秒。max_retry_countinteger否0处理出错时在从管线移除该条目前的最大重试次数。retry_delayinteger否1执行失败后延迟重试的秒数。注意应保证批次最大条数在函数执行能力范围内用于刷新的定时器基于inactive_timeout为达到最佳效果建议让inactive_timeout小于buffer_duration。插件的log阶段会调用 batch-processor-manager.lua 将日志条目推入缓冲区add_entry若处理器尚未创建则通过add_entry_to_new_processor新建并将插件 Schema 中的批处理参数透传给底层 batch-processor.lua。默认日志格式示例未配置自定义log_format时插件输出的默认日志结构如下示例数据{ response: { status: 200, size: 118, headers: { content-type: text/plain, connection: close, server: APISIX/3.7.0, content-length: 12 } }, client_ip: 127.0.0.1, upstream_latency: 3, apisix_latency: 98.999998092651, upstream: 127.0.0.1:1982, latency: 101.99999809265, server: { version: 3.7.0, hostname: localhost }, route_id: 1, start_time: 1704507612177, service_id: , request: { method: POST, querystring: { foo: unknown }, headers: { host: localhost, connection: close, content-length: 18 }, size: 110, uri: /hello?foounknown, url: http://localhost:1984/hello?foounknown } }该结构的生成逻辑位于 apisix/utils/log-util.luaget_full_log包含请求信息method、uri、url、querystring、headers、size、响应信息status、headers、size、服务端信息version、hostname、上游地址、路由/服务 ID、消费者信息、客户端 IP、开始时间以及 latency / upstream_latency / apisix_latency 三类耗时单位毫秒。当配置了include_req_body或include_resp_body时还会分别向request.body与response.body字段写入请求体/响应体。自定义日志格式Metadata 全局配置除在 Route 级配置log_format外还可以通过插件 Metadata全局设置日志格式其 Schema 仅含一个log_format字段见 clickhouse-logger.lualocal metadata_schema { type object, properties { log_format { type object } }, }重要Metadata 配置是全局生效的会作用于所有使用了clickhouse-logger插件的 Route 和 Service。通过 Admin API 配置 Metadata 的示例curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/clickhouse-logger -H X-API-KEY: $admin_key -X PUT -d { log_format: { host: $host, timestamp: $time_iso8601, client_ip: $remote_addr } }其中admin_key可以从 conf/config.yaml 中读取并写入环境变量admin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g)从源码看自定义格式的求值逻辑在 log-util.lua 的 get_custom_format_log值以$开头的会被当作变量从ctx.var中取值否则作为字面量写入同时会自动补充route_id与service_id。优先级方面get_log_entry会优先采用插件级conf.log_format其次才是 Metadata 中的log_format见 log-util.lua。准备 ClickHouse 环境启动 ClickHouse 容器使用官方 Docker 镜像启动一个 ClickHouse 服务docker run -d -p 8123:8123 -p 9000:9000 -p 9009:9009 --name some-clickhouse-server --ulimit nofile262144:262144 clickhouse/clickhouse-server其中8123为 HTTP 接口端口插件走 HTTP 协议写入9000为原生 TCP 接口9009为集群内部通信端口。创建日志表在 ClickHouse 中创建一张用于存放日志的表字段需与将要写入的日志 JSON 键一一对应curl -X POST http://localhost:8123/ \ --data-binary CREATE TABLE default.test (host String, client_ip String, route_id String, service_id String, timestamp String, PRIMARY KEY(timestamp)) ENGINE MergeTree() --user default:若使用自定义log_format如上面的host、timestamp、client_ip则表结构只需包含这些字段即可若使用默认日志格式则需要为默认 JSON 中的每个键建立对应列。启用插件在 Route 上启用clickhouse-logger。当配置了多个端点时日志会被随机写入其中某一个端点由math.random随机选择见 clickhouse-logger.lua。curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { plugins: { clickhouse-logger: { user: default, password: , database: default, logtable: test, endpoint_addrs: [http://127.0.0.1:8123] } }, upstream: { type: roundrobin, nodes: { 127.0.0.1:1980: 1 } }, uri: /hello }写入原理INSERT ... FORMAT JSONEachRow插件通过resty.http向 ClickHouse 发送 HTTP 请求完成写入请求体为INSERT INTO logtable FORMAT JSONEachRow 日志并通过请求头携带认证信息见 clickhouse-logger.lualocal httpc_res, httpc_err httpc:request({ method POST, path url_decoded.path, query url_decoded.query, body INSERT INTO .. conf.logtable .. FORMAT JSONEachRow .. log_message, headers { [Host] url_decoded.host, [Content-Type] application/json, [X-ClickHouse-User] conf.user, [X-ClickHouse-Key] conf.password, [X-ClickHouse-Database] conf.database } })当端点未显式指定端口时源码会自动推断https方案默认443其余默认80https端点还会按ssl_verify决定是否校验证书。服务端返回 400状态码时视为写入失败并记录错误信息。日志条数大于 1 时多条 JSON 会用空格拼接为{} {}形式一次性提交见 clickhouse-logger.lua这正是FORMAT JSONEachRow所需的行式 JSON 输入。示例验证启用插件后向 APISIX 发起一次请求curl -i http://127.0.0.1:9080/hello随后查询 ClickHouse 中的表即可看到对应日志行各列以制表符分隔对应建表时的字段顺序curl http://localhost:8123/?queryselect%20*%20from%20default.test 127.0.0.1 127.0.0.1 1 2023-05-08T19:15:5305:30从仓库的测试用例 t/plugin/clickhouse-logger.t 可以看到完整的端到端验证路径TEST 1/2 校验 Schema 的完整/基础配置合法性TEST 3 验证缺少端点字段时返回value should match only one schema, but matches noneTEST 4/5 通过 Admin API 在 Route 上启用插件分别使用单端点endpoint_addr与多端点endpoint_addrsTEST 7 查询default.test表断言日志已落库TEST 8 连续请求 12 次并断言日志被随机分发到8123与8124两个端点。请求体与响应体采集include_req_body/include_resp_body及其对应的_expr过滤条件可以用于审计类场景include_req_body为true时get_full_log会通过get_request_body读取请求体并写入log.request.body。请求体过大时受限于 Nginx 内存机制可能无法记录同时MAX_REQ_BODY默认上限为 512 KiB见 log-util.lua。include_resp_body为true时插件通过body_filter钩子调用log_util.collect_body采集响应体写入log.response.body若响应带 Content-Encoding如 gzip还会先解码再记录见 log-util.lua。include_req_body_expr / include_resp_body_expr基于 lua-resty-expr 表达式做条件过滤表达式求值为真时才记录对应 body。例如只记录带?foobar参数的请求{ clickhouse-logger: { user: default, password: , database: default, logtable: test, endpoint_addrs: [http://127.0.0.1:8123], include_req_body: true, include_req_body_expr: [ [arg_foo, , bar] ] } }上述能力均有对应测试覆盖见 t/plugin/clickhouse-logger2.t 的 TEST 1-8其中 TEST 4/TEST 8 分别验证了不满足表达式条件时响应体/请求体不会被记录no_error_log断言。移除插件要移除clickhouse-logger插件只需将 Route 配置中plugins下的对应 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 } } }小结clickhouse-logger将 APISIX 网关的访问日志以 JSONEachRow 格式批量写入 ClickHouse具有以下关键特性批量聚合写入基于 Batch Processor 每 5 秒或满 1000 条提交一次支持batch_max_size、inactive_timeout、max_retry_count、retry_delay等自定义调优灵活格式定制支持 Route 级与 Metadata 级全局log_format可自由引用 APISIX/Nginx 变量多端点随机写入配置endpoint_addrs数组后随机选择目标端点可在不引入负载均衡组件的情况下做简单分摊敏感信息保护password通过encrypt_fields加密存储于 etcd请求/响应体审计可按需采集请求体与响应体并支持 lua-resty-expr 条件过滤。如需深入源码可继续阅读 clickhouse-logger.lua、log-util.lua、batch-processor-manager.lua 以及对应的测试文件 clickhouse-logger.t 与 clickhouse-logger2.t。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考