ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Loki Chunk 文件解剖:用 chunks-inspect 工具解析、校验与调试 Chunk 存储格式

Loki Chunk 文件解剖:用 chunks-inspect 工具解析、校验与调试 Chunk 存储格式 Loki Chunk 文件解剖用 chunks-inspect 工具解析、校验与调试 Chunk 存储格式【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/lokicmd/chunks-inspect是 Loki 仓库中自带的一个开发者调试工具它可以直接读取磁盘上的 Loki chunk 文件逐层解析出 chunk 元数据Metadata、块Block信息、压缩编码与每一行日志原文并自动对元数据与各数据块做 CRC32 校验。本文以该工具为核心完整讲解它的构建、三个命令行参数的实战用法与输出字段含义并结合 loki.go、header.go 等源码剖析其背后 Loki chunk 二进制文件格式的完整布局帮助你在排查存储问题、验证压缩效率或开发底层存储功能时快速定位数据。chunks-inspect 是什么Loki 将日志写入内存中的 chunk再序列化为二进制文件持久化到对象存储。这些文件对普通查询链路是黑盒而 chunks-inspect 的作用就是把它们重新拆开输出人类可读的结构化信息包括chunk 头部元数据用户 ID、时间范围、标签集合、编码方式每个数据块的偏移量、压缩前后大小、压缩比、时间范围与校验结果逐条日志行的原始内容与结构化元数据structured metadata将单个数据块导出为独立文件便于单独分析压缩态与原始态数据。该工具定位是给 Loki 开发者使用的诊断/研究工具不参与正常查询路径因此它完全独立于查询引擎只依赖 pkg/compression 的编解码能力与仓库内的 crc32 表初始化逻辑是一个非常干净、可读性极高的参考实现。构建工具在仓库根目录执行 Makefile 中定义的构建目标即可make chunks-inspect对应的 Makefile 片段Makefile################## # chunks-inspect # ################## .PHONY: cmd/chunks-inspect/chunks-inspect chunks-inspect: cmd/chunks-inspect/chunks-inspect ## build chunks-inspect executable cmd/chunks-inspect/chunks-inspect: CGO_ENABLED0 go build $(GO_FLAGS) -o $ ./cmd/chunks-inspect构建产物为cmd/chunks-inspect/chunks-inspect静态编译CGO_ENABLED0。也可以直接用 go build 手工构建go build -o cmd/chunks-inspect/chunks-inspect ./cmd/chunks-inspect基本用法查看 chunk 文件概览将 chunk 文件名作为命令行参数传给程序即可打印该 chunk 的基础信息$ ./cmd/chunks-inspect/chunks-inspect db61b4eca2a5ad68\:16f89ff4164\:16f8a0cfb41\:1538ace0 Chunks file: db61b4eca2a5ad68:16f89ff4164:16f8a0cfb41:1538ace0 Metadata length: 485 Data length: 264737 UserID: 29 From: 2020-01-09 11:10:04.644000 UTC Through: 2020-01-09 11:25:04.193000 UTC (14m59.549s) Labels: __name__ logs app graphite cluster us-central1 color b container_name graphite filename /var/log/pods/metrictank_graphite-1-large-multitenant-b-5f9db68b5c-jh769_ca9a10b0-0d2d-11ea-b85a-42010a80017a/graphite/0.log hosted_metrics 1 instance graphite-1-large-multitenant-b-5f9db68b5c-jh769 job metrictank/graphite namespace metrictank org 1 plan large pod_template_hash 5f9db68b5c stream stderr Encoding: lz4 Blocks Metadata Checksum: 3444d7a3 OK Found 5 block(s), use -b to show block details Minimum time (from first block): 2020-01-09 11:10:04.644490 UTC Maximum time (from last block): 2020-01-09 11:25:04.192368 UTC Total size of original data: 1257319 file size: 265226 ratio: 4.74输出字段的对应源码实现位于 main.go 的printFile函数各字段含义如下输出字段含义Chunks file传入的 chunk 文件名即序列化后的 fingerprint:from:through:checksum 形式Metadata lengthchunk 头部元数据段的长度含长度字段自身 4 字节见下文格式说明Data length紧随元数据之后的二进制数据段长度UserID该 chunk 所属租户 IDFrom/Through该 chunk 覆盖的时间范围纳秒精度时间戳输出时转为 UTC 并按2006-01-02 15:04:05.000000 MST格式化括号内为跨度时长Labelschunk 的标签集合按键名排序输出Encoding数据块的压缩编码gzip / snappy / lz4 系列 / zstd 等Blocks Metadata Checksum块元数据表的 CRC32-Castagnoli 校验结果OK表示与文件内存储值一致Found N block(s)chunk 内包含的数据块数量未加-b时提示use -b to show block detailsMinimum/Maximum time由第一个块的最小时间与最后一个块的最大时间推导的 chunk 实际数据时间边界Total size of original data / file size / ratio所有块解压后的原始数据总字节数、文件实际字节数、压缩比原始/文件注意示例文件名中的冒号在 shell 中需要用反斜杠转义\:避免被当作历史展开。用 -b 查看数据块细节加-b参数后程序会逐个打印每个 block 的详细信息$ ./cmd/chunks-inspect/chunks-inspect -b db61b4eca2a5ad68\:16f89ff4164\:16f8a0cfb41\:1538ace0 ... chunk file info, see above ... Block 0: position: 6, original length: 273604 (stored: 56220, ratio: 4.87), minT: 2020-01-09 11:10:04.644490 UTC maxT: 2020-01-09 11:12:53.458289 UTC, checksum: 13e73d71 OK Block 0: digest compressed: ae657fdbb2b8be55eebe86b31a21050de2b5e568444507e5958218710ddf02fd, original: 0dad619bf3049a1152cb3153d90c6db6c3f54edbf9977753dde3c4e1b09d07b4 Block 1: position: 56230, original length: 274703 (stored: 60861, ratio: 4.51), minT: 2020-01-09 11:12:53.461855 UTC maxT: 2020-01-09 11:16:35.420787 UTC, checksum: 55269e65 OK Block 1: digest compressed: a7999f471f68cce0458ff9790e7e7501c5bfe14cc28661d8670b9d88aeaee96f, original: a617a9e0b6c33aeaa83833470cf6164c540a7a64258e55eec6fdff483059df6f Block 2: position: 117095, original length: 273592 (stored: 56563, ratio: 4.84), minT: 2020-01-09 11:16:35.423228 UTC maxT: 2020-01-09 11:19:28.680048 UTC, checksum: 781dba21 OK Block 2: digest compressed: 65b59cc61c5eeea8116ce8a8c0b0d98b4d4671e8bc91656979c93717050a18fc, original: 896cc6487365ad0590097794a202aad5c89776d1c626f2cea33c652885939ac6 Block 3: position: 173662, original length: 273745 (stored: 57486, ratio: 4.76), minT: 2020-01-09 11:19:31.062836 UTC maxT: 2020-01-09 11:23:13.562630 UTC, checksum: 2a88a52b OK Block 3: digest compressed: 4f51a64d0397cc806a898cd6662695620083466f234d179fef5c2d02c9766191, original: 15e8a1833ccbba9aa8374029141a054127526382423d3a63f321698ff8e087b5 Block 4: position: 231152, original length: 161675 (stored: 33440, ratio: 4.83), minT: 2020-01-09 11:23:15.416284 UTC maxT: 2020-01-09 11:25:04.192368 UTC, checksum: 6d952296 OK Block 4: digest compressed: 8dd12235f1d619c30a9afb66823a6c827613257773669fda6fbfe014ed623cd1, original: 1f7e8ef8eb937c87ad3ed3e24c321c40d43534cc43662f83ab493fb3391548b2 Total size of original data: 1257319 file size: 265226 ratio: 4.74每个 block 一行主信息实现见 main.go字段含义字段含义position该块压缩数据在 chunk 数据段中的字节偏移original length解压后的原始字节数stored文件中实际存储的压缩字节数ratiooriginal / stored该块的压缩比minT/maxT块内日志时间戳的最小/最大值UTC纳秒精度checksum块的 CRC32-Castagnoli 校验值OK表示与文件中存储值一致BAD (computed: ...)表示数据损坏紧随其后的digest行是对该块压缩态compressed与原始态original字节的 SHA-256 摘要main.go可用于与其他系统比对块内容是否一致。通过这个视图可以直观地看到一个 265 KB 的 chunk 文件被切成 5 个约 15 分钟粒度的 block每个 block 的压缩比都在 4.5~4.9 之间原始数据合计约 1.26 MB——这正是 Loki chunk 在对象存储中的典型形态。用 -l 打印日志行-l参数会解压并逐条打印每个 block 中的日志行输出格式为TS(时间戳) LINE(日志内容) STRUCTURED_METADATA(kv ...)$ ./cmd/chunks-inspect/chunks-inspect -l chunk-file该输出对应 main.go 中对每条 entry 的打印逻辑时间戳使用与概览相同的 UTC 格式日志行会去掉首尾空白strings.TrimSpace随后列出该行携带的结构化元数据键值对V4 及以上格式才有。对已经损坏的 block会输出FAILED to parse, recovered N of M entries提示说明解析在该行中断但仍返回了已成功恢复的条目。用 -s 导出数据块-s参数会把每个 block 的压缩态与原始态分别写成独立文件文件名在输入 chunk 文件名基础上追加块索引chunk-file.block.索引以文件中实际存储的压缩字节原样导出chunk-file.original.索引解压后的原始字节导出。导出逻辑见 main.go 与writeBlockToFilemain.go文件权限为0640导出成功会打印Stored block N to file ...日志。拿到这两个文件后你可以脱离 chunk 容器单独研究某个 block 的压缩效果、与其它工具比对字节内容或者喂给压缩库做二次分析。命令行参数一览工具支持一次传入多个 chunk 文件逐个处理。完整参数对应 main.go 中的 flag 定义与 README 中的帮助输出$ ./cmd/chunks-inspect/chunks-inspect -h Usage of ./cmd/chunks-inspect/chunks-inspect: -b print block details -l print log lines -s store blocks, using input filename, and appending block index to it参数作用-b打印每个 block 的偏移、大小、时间范围、校验和与 SHA-256 摘要-l解压并逐条打印日志行及其结构化元数据-s将各 block 的压缩态与原始态导出为文件名.block.N/文件名.original.N底层原理Loki chunk 二进制文件格式chunks-inspect 的价值在于它完整复刻了 Loki chunk 的序列化/反序列化格式。文件布局的核心注释直接写在 loki.go 中4B magic number 1B version 1B encoding Block 1 ------------------------------------B Block 1 Checksum ... Uvarint # blocks -------------------------- A Block1 Uvarint # entries Block1 Varint64 mint Block1 Varint64 maxt Block1 Varint64 offset -------------------- B Block1 Uvarint uncomp size (V3 chunks and greater only) Block1 Uvarint length Block1 Meta Checksum ... 4B Meta offset ---------------------------- A结合 loki.go 的解析代码整个文件分为三层1. 头部元数据Headerheader.go 中的DecodeHeader按如下顺序读取4 字节大端metadata length紧随其后的length-4字节内容长度字段本身计入总长该内容是snappy 压缩后的 JSON解压后反序列化为ChunkHeader见 header.go包含fingerprint、userID、from、through、metric标签集与encoding4 字节大端data length即二进制数据段的大小。其中encoding字段的注释特别指出Loki 从不使用零值Delta 编码因此该字段缺失时默认按 DoubleDelta 处理。也就是说chunk 头部是一个snappy 压缩的 JSON 长度前缀的自描述结构。2. 数据段Data与块Block头部之后是DataLength字节的二进制数据段。解析时整个数据段被读入内存因为块偏移信息存放在文件末尾然后校验前 4 字节魔数0x012EE56Aloki.go第 4 字节为格式版本chunkFormatV1~V4见 loki.go第 5 字节为压缩编码编号。每个 block 的二进制格式解压后是一条接一条的 entryVarint64 时间戳 Uvarint 行长度 行字节V4 起每条 entry 末尾还追加结构化元数据见下文。块之间以 CRC32Castagnoli 多项式校验值分隔校验表在包初始化时预生成loki.go。3. 块元数据表Block Metadata Table块的目录集中在文件末尾的元数据表中每块记录# entries、minT、maxT、dataOffset、uncomp sizeV3、length六项均为 varint 编码表本身以 Uvarint 块数开头表尾附带整张表的 CRC32 校验即输出中的Blocks Metadata Checksum。元数据表位置的定位逻辑随版本而异loki.goV1~V3单张元数据表位于文件末尾最后 8 字节存表的起始偏移表的长度 len(data) - (8 4) - metasOffset扣除末尾偏移指针与紧邻其前的 4 字节校验和V4 起文件末尾引入多个 section每个 section 的length offset各占 8 字节、共 16 字节按索引逆序存放index 1 为 chunk 元数据表index 2 为结构化元数据符号表通过readSectionLenAndOffset读取loki.go。4. 压缩编码与结构化元数据编码编号到编解码器的映射在 getCompression 中实现V1 固定为 gzipV2 起根据第 5 字节查compression.Codec。Loki 支持的编码全集定义在 pkg/compression/codec.go包括none、gzip、lz4-64k、snappy、lz4-256k、lz4-1M、lz4LZ4_4M、flate、zstd是否支持由IsSupported()判定不认识的编号会报unknown encoding。README 示例中Encoding: lz4对应LZ4_4M。解压统一通过compression.GetReaderPool(codec)获取 reader 池用完归还loki.go。对于V4 及更高版本chunk 末尾还有一个额外的符号表 section结构化元数据的字符串键值被去重压缩存放每条 entry 只记录字符串在符号表中的索引对大幅降低重复键值如trace_id的存储开销。解析流程见 loki.go先解压符号表与 loki.go按索引还原每条 entry 的键值对。源码级验证测试如何保证解析正确性loki_test.go 用真实编码路径验证了解析器的正确性是理解本工具行为边界的绝佳参考TestParseLokiChunkloki_test.go覆盖ChunkFormatV2/V3/V4× 全部受支持 codec 的组合通过 pkg/chunkenc 的NewMemChunk构造 chunk、写入 40 条日志V4 起每条附带trace_id结构化元数据再断言块元数据校验和与计算值一致、每块条目数与元数据一致、首条日志内容精确匹配TestParseLokiChunkBlockParseErrorloki_test.go故意把某一块的行长度字段改成超出块体的大值验证解析器只在该块报错、其余块不受影响且损坏块在报错前已恢复的条目仍会返回TestParseLokiChunkBlockDecompressionErrorloki_test.go对整个块的压缩载荷逐字节取反并修复校验和验证解压失败与校验失败被区分对待解压失败的块parseErr非空、entries为空其余块正常。这三类测试对应的正是-b输出中的checksum ... OK/BAD、FAILED to parse, recovered N of M entries等诊断信息的产生路径chunks-inspect 不仅能读还能替你分辨文件没坏校验通过与内容无法解析entry 流损坏两种不同的损坏形态。典型使用场景小结存储排查当某个 chunk 在查询/检索时行为异常用基本模式确认时间范围、标签与编码是否与预期一致压缩效果评估通过ratio字段对比不同编码gzip vs snappy vs zstd在真实数据上的压缩比数据损坏定位用-b看哪个 block 的 checksum 为BAD或用-l观察FAILED to parse出现在哪个块格式研究用-s导出单个 block 的压缩/原始字节配合 loki.go 中的格式注释逐字节比对是理解 Loki 存储层二进制格式最直接的学习材料。需要说明的是本工具面向 Loki 内部存储格式输入必须是符合上述二进制布局的 chunk 文件通常来自对象存储或本地缓存目录它的输出与查询引擎无关是纯离线的文件级分析工具。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表