Grafana Loki 日志条目删除(Log Entry Deletion)完整指南:配置、API 与底层原理
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
Grafana Loki 支持按流(stream)、按时间窗口、按可选行过滤器删除日志条目,该能力由 compactor 组件统一承载。本文基于当前仓库的官方文档与源码实现,系统讲解删除功能的前置条件、compactor 配置项、三种deletion_mode语义、删除请求的创建/查询/取消 API 参数,以及删除请求在后端的存储、分片与执行机制,帮助你安全、正确地落地日志删除与合规需求。
功能概述:Loki 能删除什么
Grafana Loki 的日志条目删除功能允许你从**指定流(stream)**中删除日志:满足以下条件的日志条目会被删除:
- 落在指定的时间窗口内;
- 匹配一个可选的 LogQL 行过滤器(line filter)。
删除请求通过 compactor 组件暴露的 REST 端点提交(端点列表见 reference/loki-http-api 中的 compactor 部分),由请求指定流与时间窗口;日志条目的实际删除动作,则在可配置的**取消期(cancellation period)**过后才会发生。
需要特别注意的是索引存储类型的约束:日志条目删除在TSDB 索引作为索引存储时受支持;过时的 BoltDB Shipper 索引也支持该功能,但 BoltDB Shipper 将在 Loki 4.0 中被移除,因此新部署应使用 TSDB。
日志条目删除依赖自定义日志保留(custom logs retention)工作流——即 compactor 的 retention 机制。compactor 会检查所有未处理且已超过取消期的删除请求,据此判断某个 chunk 是否应该被删除。
开启删除功能的前置条件
日志删除不是开箱即用的,需要同时满足三个条件:
- 启用 retention:在 compactor 配置中设置
retention_enabled: true(命令行对应-compactor.retention-enabled)。 - 配置
delete_request_store:指定存储删除请求的对象存储桶;启用 retention 后必须配置,否则 compactor 启动校验会直接失败(见 pkg/compactor/config.go 中Validate()对DeleteRequestStore == ""的报错)。 - 租户的
deletion_mode非disabled:默认值为filter-and-delete,因此在未显式修改的情况下,一旦启用 retention 并配置好delete_request_store,删除功能对所有租户即默认生效。
⚠️ 启用 retention 的安全警告
启用 retention 必须非常谨慎。强烈建议同时在对象存储上开启版本控制(versioning),以便在 retention 配置误操作时能够恢复数据。如果你只想启用删除能力、并不想强制执行 retention,请将retention_period设置为0s。
从源码看,compactor 的Validate()还做了一些自动化的配置协调(pkg/compactor/config.go):当retention_enabled为 true 且apply_retention_interval为 0 时,它会将apply_retention_interval对齐为compaction_interval的值,并额外增加最多 10 分钟(不超过其一半)的抖动(jitter),以避免 retention 与 compaction 在同一时刻运行。
配置详解:compactor 与 limits_config
deletion_mode:三种模式
deletion_mode是limits_config中的全局与按租户设置,其类型定义在 pkg/validation/limits.go:
limits_config: # 全局默认,可选值:disabled | filter-only | filter-and-delete deletion_mode: filter-and-delete在全局配置文件中设置即为全局默认值;也可以按租户在**运行时配置文件(runtime configuration file)**中覆盖(参见 configure 文档的 runtime-configuration-file 部分)。
deletion_mode支持三种取值(实现见 pkg/compactor/deletionmode/mode.go):
| 取值 | 语义 | 存储行为 |
|---|---|---|
disabled | 不允许删除,对删除 API 端点的请求返回403 Forbidden | 不删除 |
filter-only | 查询 Loki 时过滤掉匹配删除请求的日志行 | 不从存储中移除 |
filter-and-delete | 查询时过滤,且同时从存储中移除 | 从存储删除(默认值) |
源码中Mode.DeleteEnabled()仅对FilterOnly与FilterAndDelete返回 true(mode.go),而删除 API 的请求处理会先调用validDeletionLimit检查租户模式是否允许删除(pkg/compactor/deletion/util.go)。注意ParseMode对未知值会返回ErrUnknownMode,因此配置拼写错误会直接导致请求校验失败。
端点注册与 retention 的强绑定
删除 API 端点仅在compactor.retention_enabled为 true 时注册。当 retention 未启用时,无论deletion_mode取什么值,所有租户都无法访问删除端点(此时请求处理器的 handler 为 nil,会直接返回400 Retention is not enabled,见 pkg/compactor/deletion/request_handler.go)。
当 retention 启用后,再通过deletion_mode的按租户覆盖(override)来控制哪些租户可以使用删除 API。
其他关键 compactor 配置项
以下是当前仓库中与删除相关的完整配置项(默认值与含义来自 pkg/compactor/config.go):
compactor: # 启用按流/按租户的自定义 retention(删除功能的前提) retention_enabled: true # 存储删除请求的对象存储桶,启用 retention 后必填 delete_request_store: loki-delete-requests # 删除请求在桶中的路径前缀,默认 "index/" delete_request_store_key_prefix: index/ # 存储删除请求所用的数据库类型:boltdb(默认)或 sqlite delete_request_store_db_type: boltdb # 迁移数据库类型时的备份库类型,例如 boltdb backup_delete_request_store_db_type: "" # 允许在创建后多长时间内取消删除请求,默认 24h delete_request_cancel_period: 24h # 带行过滤器的删除请求最大分片跨度,默认 24h delete_max_interval: 24h # 每个压缩周期最多处理的删除请求数,默认 70 delete_batch_size: 70 # retention 生效周期:0 表示与 compaction 周期一致(源码会自动加抖动) apply_retention_interval: 0s # 删除请求开始真正删除数据前的延迟,默认 2h retention_delete_delay: 2h # 删除 chunk 的工作协程数,默认 150 retention_delete_worker_count: 150对应的命令行 Flag(前缀均为-compactor.):
| Flag | 默认值 |
|---|---|
-compactor.retention-enabled | false |
-compactor.delete-request-store | 空 |
-compactor.delete-request-store.key-prefix | index/ |
-compactor.delete-request-store.db-type | boltdb |
-compactor.delete-request-store.backup-db-type | 空 |
-compactor.delete-request-cancel-period | 24h |
-compactor.delete-max-interval | 24h |
-compactor.delete-batch-size | 70 |
-compactor.retention-delete-delay | 2h |
-compactor.retention-delete-worker-count | 150 |
源码中delete_request_cancel_period的 Flag 注释建议至少设为 24h(config.go);retention_delete_delay则是在取消期之后、chunk 真正被删除之前的额外缓冲。
一个最小可用配置示例
limits_config: retention_period: 744h # 如不想强制 retention,可设为 0s deletion_mode: filter-and-delete compactor: working_directory: /var/loki/compactor retention_enabled: true delete_request_store: gcs://bucket_for_delete_requests # 按你的对象存储类型填写 delete_request_store_db_type: boltdb delete_request_cancel_period: 24h delete_max_interval: 24h删除请求的生命周期与 HTTP API 使用
提交删除请求(Add)
通过 compactor 的删除端点提交删除请求。核心参数在 request_handler.go 中解析:
query(必填):LogQL 流选择器表达式,如{cluster="prod"},可带行过滤器如{cluster="prod"} |= "ERROR"。源码中parseDeletionQuery会先syntax.ParseLogSelector再构建 Pipeline,非法表达式(如错误的 regex 或ip()模式)会在提交时就返回 400,而不是等到执行期失败(pkg/compactor/deletion/util.go)。start(必填):起始时间,支持Unix 秒或RFC3339格式。end(必填):结束时间,同样支持两种格式。校验规则包括:不允许删除未来时间的数据(deletes in the future are not allowed),且 start 必须小于 end(request_handler.go)。max_interval(可选):单请求的分片跨度,不能大于delete_max_interval,也不能大于待删除时间窗口本身;最小 1 秒,合法单位为s、m、h(request_handler.go)。
curl -X POST -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?query=%7Bcluster%3D%22prod%22%7D&start=1704067200&end=1704153600"成功时返回204 No Content,响应头X-Delete-Request-ID携带删除请求 ID。请求只影响查询层时(filter-only),删除记录同样会持久化,查询时由查询路径按需过滤。
分片(sharding)机制
删除请求如果带行过滤器,会被拆分成多个较小的子请求,每个子请求覆盖不超过delete_max_interval(默认 24h)的时间跨度。单个请求可以用max_interval参数请求更小的分片,但不能大于delete_max_interval。不带行过滤器的删除请求不会被拆分(request_handler.go 中仅当parsedExpr.HasFilter()时才计算分片间隔)。
源码中的buildRequests展示了分片的实现细节(request_handler.go):分片时子请求之间刻意保留少量时间重叠(而不是精确衔接),以避免因边界 1ms 的间隙漏删日志。
查询删除请求(Get)
curl -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete"该接口按创建时间排序返回该租户的全部删除请求(JSON 数组),并隐藏内部的UserID与SequenceNum字段。它还支持两个可选参数:
for_querytime_filtering=true:仅返回与查询时过滤相关的删除请求;start+end:可选的时间范围重叠过滤,只返回与给定范围有交集的删除请求(request_handler.go)。
被拆分出的同一请求的多个子请求,在查询时会被mergeDeletes合并展示为一条,状态根据已完成子请求的比例计算为Received、Processed或N% Complete(request_handler.go)。
取消删除请求(Cancel)
删除请求在可配置的取消期内可以取消:
curl -X PUT -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?request_id=<REQUEST_ID>"- 取消期的长度由
delete_request_cancel_period决定,默认24h; - 一旦请求进入处理中或已完成(状态为
Processed),默认不允许取消(返回 400); - 对已开始处理或已超过创建后取消期的请求,仍可传入
force=true查询参数强制取消(request_handler.go):curl -X PUT -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?request_id=<REQUEST_ID>&force=true"
缓存失效辅助端点
compactor 还暴露了缓存代数(cache generation number)相关端点:GET /loki/api/v1/cache_generation_number用于获取某租户的缓存代数;POST(更新)端点用于在回放历史数据等场景下手动递增缓存代数,使该租户的查询结果缓存失效(request_handler.go)。删除处理完成后缓存代数会自动更新,从而保证查询不会命中已删除数据的缓存。
删除请求的存储:boltdb 与 sqlite,以及迁移
删除请求本身使用delete_request_store_db_type指定的数据库类型存储,默认为boltdb,也可改用sqlite。
- 从一种数据库类型迁移到另一种时,可以设置
backup_delete_request_store_db_type: boltdb,使删除请求同时写入备份数据库,迁移期间不丢请求(当前仓库中备份库仅支持 boltdb,见 config.go)。 - 底层实现分别位于 pkg/compactor/deletion/delete_requests_db_boltdb.go 与 pkg/compactor/deletion/delete_requests_db_sqlite.go,并配套完整的单元测试(
delete_requests_db_boltdb_test.go、delete_requests_db_sqlite_test.go),可作为实现参考。
注意:delete_request_store与delete_request_store_db_type是两个不同维度的配置——前者是对象存储位置(存放删除请求数据文件),后者是本地数据库引擎。二者的组合决定了删除请求的持久化方式。
删除的实际执行机制
删除不是提交请求后立即发生的,其执行链路如下:
- compactor 周期性扫描:compactor 在每个 retention 周期(
apply_retention_interval,默认与 compaction 周期一致并带抖动)检查未处理的删除请求。 - 只处理已过取消期的请求:只有创建时间超过
delete_request_cancel_period(默认 24h)的请求才会进入实际删除阶段,这是给用户留出取消窗口。 - 批量执行:每个周期最多处理
delete_batch_size(默认 70)个删除请求。 - 真正的数据删除:对于
filter-and-delete模式,涉及删除的 chunk 需要按删除请求重建(去除被删除的行),再写回对象存储并更新索引;删除动作还会受到retention_delete_delay(默认 2h)的进一步延迟缓冲。
从源码看,delete_requests_manager.go 中的DeleteRequestsManager负责加载待处理的删除请求(loadDeleteRequestsToProcess),并以chunksSelectedTotal、deletedLinesTotal、deleteRequestsProcessedTotal、deletionFailures等指标持续上报进度(metrics.go)。带行过滤器的删除请求重建 chunk 的过程由 deletion_manifest_builder.go 生成删除清单(deletion manifest)驱动。
⚠️ 性能注意事项
带行过滤器的日志删除是 compactor 最消耗资源的操作之一。因为带过滤器的删除需要把每个相关 chunk 读出来、剔除匹配行、再重写并写回对象存储,属于 CPU 与 IO 密集任务。如果需要在多个租户/流上删除大批量带行过滤器的数据,请参考 Horizontal scaling of Compactor,将删除工作分布到多个 compactor 实例上执行。
排查与运维建议
- 对象存储务必开启版本控制,防止误删后无法恢复;
- 先以
filter-only模式观察删除查询的实际命中范围,再切换到filter-and-delete落地物理删除; - 提交带行过滤器的删除请求时,合理利用
max_interval参数控制单次分片规模,避免单个请求跨度过大导致执行时间过长; - 监控 compactor 的删除相关指标(
loki_compactor_deletion_*),观察请求积压与执行失败情况,必要时通过横向扩展 compactor 分散负载。
总结
Loki 的日志条目删除能力以 compactor 的 retention 工作流为底座,通过retention_enabled+delete_request_store+deletion_mode三个要素即可开启;删除请求经 REST API 提交后,先经历可取消的保护期,再由 compactor 周期性地完成过滤、chunk 重建与物理删除。理解filter-only与filter-and-delete的差异、分片与取消机制、以及 boltdb/sqlite 两种请求存储的迁移方式,是安全使用这一强操作能力的关键。
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考