Apache APISIX loki-logger 插件详解将网关请求日志批量推送至 Grafana Loki【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix导读loki-logger是 Apache APISIX 官方提供的日志插件之一用于将网关处理的请求上下文信息序列化为 JSON 日志条目并通过批处理队列高效地推送到 Grafana Loki 进行集中存储与分析。本篇文章将围绕该插件的完整配置属性、元数据定制、启用/删除流程、批处理机制原理以及高频问题排查展开帮助你在真实网关环境中快速落地APISIX 日志 → Loki的可观测性链路。描述插件解决了什么问题在微服务与 API 网关架构中请求日志是排障、审计与监控的重要数据源。loki-logger插件的工作方式如下请求进入 APISIX 并被路由命中后插件在日志阶段logphase收集请求上下文信息上下文信息被序列化为符合 Loki Push API 格式的日志条目JSON日志条目被提交到批处理队列当队列数据量超过最大批处理大小时或到达刷新周期统一推送至 Grafana Loki从而避免每条请求都发起一次 HTTP 调用。从源码实现看插件核心位于 apisix/plugins/loki-logger.lua它复用了apisix.utils.log-util与批处理器batch processor两套基础设施log_util.get_log_entry负责组装日志条目batch_processor_manager:add_entry负责缓冲与批量发送。属性Attributesloki-logger插件的核心属性如下表所示其中endpoint_addrs为必填项| 名称 | 类型 | 必选项 | 默认值 | 描述 | |--|---|---|---|---| | endpoint_addrs | array[string] | True | | Loki API 基础 URL格式如http://127.0.0.1:3100支持 HTTPS 和域名。如果配置了多个端点将随机选择一个进行写入 | | endpoint_uri | string | False |/loki/api/v1/push| 如果您正在使用与 Loki Push API 兼容的日志收集服务可以使用此配置项自定义 API 路径 | | tenant_id | string | False |fake| Loki 租户 ID。根据 Loki 的多租户文档在单租户模式下默认值为fake| | log_labels | object | False |{job apisix}| Loki 日志标签。可以使用 APISIX 变量 和 Nginx 变量只需在字符串前加$符号可单独使用或组合使用如$host或$remote_addr:$remote_port| | ssl_verify | boolean | False | true | 当设置为true时将验证 SSL 证书 | | timeout | integer | False | 3000ms | Loki 服务 HTTP 调用的超时时间范围 1 到 60,000 毫秒 | | keepalive | boolean | False | true | 当设置为true时会保持连接以供多个请求复用 | | keepalive_timeout | integer | False | 60000ms | 连接空闲后的关闭时间范围大于等于 1000 毫秒 | | keepalive_pool | integer | False | 5 | 连接池限制范围大于等于 1 | | log_format | object | False | | 以 JSON 格式声明的键值对形式的日志格式。值仅支持字符串类型可通过$前缀使用 APISIX 变量 和 Nginx 变量 | | include_req_body | boolean | False | false | 当设置为true时日志中将包含请求体。如果请求体太大无法在内存中保存则受 Nginx 限制无法记录请求体 | | include_req_body_expr | array | False | | 当include_req_body为true时的过滤器。只有当此处设置的表达式求值为true时才会记录请求体表达式语法参见 lua-resty-expr | | include_resp_body | boolean | False | false | 当设置为true时日志中将包含响应体 | | include_resp_body_expr | array | False | | 当include_resp_body为true时的过滤器。只有当此处设置的表达式求值为true时才会记录响应体表达式语法参见 lua-resty-expr |从源码看属性校验规则在 apisix/plugins/loki-logger.lua 中schema 对上述属性做了严格约束endpoint_addrs必须是数组array且minItems 1不允许为空数组或字符串单元测试 t/plugin/loki-logger.t 中对endpoint_addrs http://127.0.0.1:8199、空数组、缺失等错误形态均验证了对应的校验报错endpoint_uri为字符串且minLength 1默认/loki/api/v1/pushtimeout取值范围 160000毫秒默认 3000keepalive_timeout最小 1000毫秒默认 60000keepalive_pool最小 1默认 5log_labels的每个 value 必须是长度 ≥ 1 的字符串include_req_body_expr/include_resp_body_expr均为数组且minItems 1其合法性由log_util.check_log_schema见 apisix/utils/log-util.lua在check_schema阶段通过expr.new预编译验证。此外插件还通过core.utils.check_https与core.utils.check_tls_bool对endpoint_addrsHTTPS 端点和ssl_verify配置做了联动校验见 apisix/plugins/loki-logger.lua。批处理相关配置该插件支持使用批处理器对日志条目进行批量聚合与处理避免频繁提交数据。批处理器默认每5秒刷新一次缓冲区或当队列中的数据达到1000条时提交。您可以在插件配置中直接附加批处理参数进行自定义详见批处理器文档。批处理器支持的可配置项包括名称类型默认值描述namestringloki logger用于标识批处理器的唯一名称batch_max_sizeinteger1000每批最大日志条数达到后立即推送设为 1 时每条日志即时发送inactive_timeoutinteger5缓冲区最大刷新间隔秒无论条数是否达标都会推送buffer_durationinteger60批次中最旧条目必须被处理的最大时限秒max_retry_countinteger0失败时的最大重试次数retry_delayinteger1失败重试的延迟秒数从 apisix/utils/batch-processor.lua 的push实现可以看到当batch_max_size 1时条目被立即调度发送否则条目进入entry_buffer当缓冲区条数达到batch_max_size或计时器到期inactive_timeout/buffer_duration时触发process_buffer。官方建议保持inactive_timeout小于buffer_duration以获得最佳批量效果。默认日志格式示例未配置log_format时插件输出如下结构的完整日志字段与log_util.get_full_log的实现对应见 apisix/utils/log-util.lua{ request: { headers: { connection: close, host: localhost, test-header: only-for-test#1 }, method: GET, uri: /hello, url: http://localhost:1984/hello, size: 89, querystring: {} }, client_ip: 127.0.0.1, start_time: 1704525701293, apisix_latency: 100.99994659424, response: { headers: { content-type: text/plain, server: APISIX/3.7.0, content-length: 12, connection: close }, status: 200, size: 118 }, route_id: 1, loki_log_time: 1704525701293000000, upstream_latency: 5, latency: 105.99994659424, upstream: 127.0.0.1:1980, server: { hostname: localhost, version: 3.7.0 }, service_id: }其中loki_log_time是插件内部追加的纳秒级时间戳字段由ngx.req.start_time() * 1000拼接000000得到见 apisix/plugins/loki-logger.lua用于满足 Loki 对日志时间戳的纳秒精度要求该字段在真正组包发送前会被清理entry.loki_log_time nil不会污染写入 Loki 的日志正文。元数据Metadata您还可以通过配置插件元数据plugin_metadata来全局设置日志格式可选配置项如下| 名称 | 类型 | 必选项 | 默认值 | 描述 | |------|------|----------|--|-------------| | log_format | object | False | | 日志格式以 JSON 键值对声明值只支持字符串类型可通过$前缀使用 APISIX 变量 和 Nginx 变量 |:::info 重要提示配置插件元数据具有全局范围意味着它将对所有使用了loki-logger插件的路由和服务生效。而单个插件配置路由/服务级plugins.loki-logger.log_format优先级高于元数据配置。:::以下示例展示了如何通过 Admin API 配置元数据。:::note您可以这样从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/loki-logger -H X-API-KEY: $admin_key -X PUT -d { log_format: { host: $host, timestamp: $time_iso8601, client_ip: $remote_addr } }使用这个配置后日志将被格式化为以下形式每行一条{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}自定义格式的底层实现自定义log_format的解析逻辑位于 apisix/utils/log-util.luagen_log_format会遍历每个 value若以$开头则标记为取ctx.var变量否则视为静态字符串get_custom_format_log据此逐键生成日志条目并自动附加route_id与service_id字段。格式解析结果还会通过 LRU 缓存lru_log_format提升高频路径性能。启用插件以下示例展示了如何在路由1上启用loki-logger插件curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { plugins: { loki-logger: { endpoint_addrs : [http://127.0.0.1:3100] } }, upstream: { nodes: { 127.0.0.1:1980: 1 }, type: roundrobin }, uri: /hello }多个端点的随机写入从源码看插件在发送时会从endpoint_addrs中随机选取一个端点并拼接endpoint_uri组成完整 URL见 apisix/plugins/loki-logger.lualocal endpoint_url conf.endpoint_addrs[math_random(#conf.endpoint_addrs)] .. conf.endpoint_uri因此当配置多个端点时可以实现简单的负载分散如果存在不可用端点受timeout与max_retry_count批处理重试共同约束失败批次会按重试策略在retry_delay后重新尝试见 apisix/utils/batch-processor.lua。发送请求的组成插件通过resty.http发送 POST 请求请求头固定包含Content-Type: application/jsonX-Scope-OrgID: tenant_id用于 Loki 多租户路由见 apisix/plugins/loki-logger.lua请求体结构符合 Loki Push API 规范将批内所有条目组装为一个streamsstream 为log_labelsvalues 为[纳秒时间戳, JSON 日志]数组见 apisix/plugins/loki-logger.lua。示例用法插件启用后向 APISIX 发起请求即可在 Loki 服务器中查询到对应日志curl -i http://127.0.0.1:9080/hello测试用例 t/plugin/loki-logger.t 完整覆盖了这条链路TEST 2/3 创建带插件的路由并请求/helloTEST 4 通过lib.grafana_loki.fetch_logs_from_loki从 Loki 拉取时间窗口内的日志并断言请求头test-header已作为标签request_headers_test_header被写入TEST 57 验证自定义log_labels含静态值与$remote_addr变量的写入效果TEST 811 验证tenant_id多租户隔离下日志正确路由到对应租户。删除插件当您需要删除loki-logger插件时使用以下命令移除对应的 JSON 配置即可APISIX 会自动重新加载相关配置无需重启服务curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -d { methods: [GET], uri: /hello, plugins: {}, upstream: { type: roundrobin, nodes: { 127.0.0.1:1980: 1 } } }FAQ日志未正确推送请查看error.log文件寻找此类日志2023/04/30 13:45:46 [error] 19381#19381: *1075673 [lua] batch-processor.lua:95: Batch Processor[loki logger] failed to process entries: loki server returned status: 401, body: no org id, context: ngx.timer, client: 127.0.0.1, server: 0.0.0.0:9081可以根据错误failed to process entries: loki server returned status: 401, body: no org id以及 Loki 服务器返回的响应正文来诊断错误。这类错误通常与租户tenant配置相关Loki 开启多租户后要求请求必须携带X-Scope-OrgID头请核对tenant_id是否与 Loki 侧租户配置一致。该错误信息由 apisix/plugins/loki-logger.lua 在响应状态码 ≥ 300 时生成。当请求每秒RPS较高时出现错误请确保keepalive相关配置已正确设置详见上文属性章节高并发下复用连接可以显著降低建连开销与文件描述符压力。请检查error.log中的日志查找此类记录2023/04/30 13:49:34 [error] 19381#19381: *1082680 [lua] batch-processor.lua:95: Batch Processor[loki logger] failed to process entries: loki server returned status: 429, body: Ingestion rate limit exceeded for user tenant_1 (limit: 4194304 bytes/sec) while attempting to ingest 1000 lines totaling 616307 bytes, reduce log volume or contact your Loki administrator to see if the limit can be increased, context: ngx.timer, client: 127.0.0.1, server: 0.0.0.0:9081通常与高 QPS 相关的日志如上所示。错误信息Ingestion rate limit exceeded for user tenant_1 (limit: 4194304 bytes/sec) while attempting to ingest 1000 lines totaling 616307 bytes, reduce log volume or contact your Loki administrator to see if the limit can be increased表示 Loki 侧的摄入速率被限流说明单批推送的数据量超过了 Loki 配置的每秒摄入限制。请参考 Loki 官方配置文档为 Loki 添加默认摄入量与突发摄入量限制例如ingestion_rate_mb和ingestion_burst_size_mb。在开发过程中进行测试时将ingestion_burst_size_mb设置为 100可以确保 APISIX 以至少 10000 RPS 的速率正确推送日志。小结loki-logger插件以极低的接入成本打通了 APISIX 与 Grafana Loki 的日志链路通过批处理器聚合写入显著降低了高频请求场景下的推送压力通过log_labels与log_format支持变量插值可以灵活定制写入 Loki 的标签与日志正文配合多端点随机写入、keepalive 连接池与失败重试机制能够满足生产环境对吞吐与可靠性的基本要求。若需进一步了解批处理的详细配置如batch_max_size、inactive_timeout、max_retry_count的调优可继续阅读批处理器文档插件的全部实现细节可参考 apisix/plugins/loki-logger.lua 及其单元测试。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考