尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Grafana Tempo 贡献者指南:从 PR 提交流程到 Go 编码规范与工具链实战

Grafana Tempo 贡献者指南:从 PR 提交流程到 Go 编码规范与工具链实战 Grafana Tempo 贡献者指南从 PR 提交流程到 Go 编码规范与工具链实战【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempoGrafana Tempo 是一款高吞吐、低依赖的分布式链路追踪后端distributed tracing backend仓库采用 Go 编写围绕cmd/、modules/、pkg/、tempodb/等核心目录组织。本文基于仓库根目录的 CONTRIBUTING.md 编写完整梳理向 Tempo 提交代码与文档的规范流程AI 辅助贡献的披露与责任边界、Go 依赖管理go get与make vendor-check、项目目录结构、编码与可观测性规范、测试与 lint 工具链、PR 的签名提交与 changelog 条目要求、文档贡献流程以及基于 docker-compose 的本地调试方法。读完本文你将掌握一份可复制的开源贡献 S.O.P.能够规范、高效地向 Tempo 提交第一个 PR。贡献前的总体原则Tempo 使用 GitHub 管理 PR 的评审贡献入口非常简单如果是一个琐碎的修复或改进trivial fix直接创建 pull request 即可如果计划做更复杂的改动请先在相关的 GitHub issue 上讨论你的想法避免方向性返工。在提交 PR 之前必须完整阅读本指南——PR 检查清单也要求贡献者先通读全文。这保证了所有提交者无论人类还是 AI 辅助都遵循同一套质量与流程标准。AI 辅助贡献接受但责任完全在提交者Tempo 接受开发过程中使用了 AI 工具如 GitHub Copilot、ChatGPT、Claude 等的贡献但明确声明使用 AI 并不会降低门槛而是把责任完全转移给了贡献者本人。披露要求如果 AI 工具生成了你所提交代码或文档的实质性部分必须在 PR 描述中说明并指出使用了哪些工具、各用于什么目的。琐碎用途自动补全、语法检查无需披露除此之外的实质使用都需要披露。每一行都由你负责通过提交 PR你即认证了 Developer Certificate of OriginDCO——你有权在项目许可证下提交这些代码。AI 工具无法认证 DCO无论代码如何生成你都是记录在案author of record的作者。这意味着提交前要读懂每一行代码如果无法解释它就不要提交不允许由自主 Agent 在无人审查输出的情况下直接提交 PR不要用 AI 来写 PR 描述或 issue 评论——这些内容必须真实反映你对改动的理解。质量与正确性自查清单AI 生成的代码往往在特定方面出错提交前需要逐项检查幻觉 APIHallucinated APIs调用了所使用库中并不存在的函数或方法伪造依赖Fake dependencies包名听起来合理但实际不存在或未被代码库其他位置引入——必须核实每个新依赖真实存在且处于活跃维护状态错误的边界情况处理AI 常产出看起来正确但错误路径或边界条件处理有误的代码风格漂移Style driftAI 输出通常不符合项目约定——提交前务必运行make fmt和make lint并修复所有问题。所有常规要求依然适用测试、文档、changelog 条目、CI 通过。未经审查、浪费维护者时间的 AI 输出不算贡献。许可与版权AI 工具可能从训练数据中复现片段。如果使用的工具能标记与公开仓库的相似性如 GitHub Copilot 的 code referencing请启用该功能。若生成的片段疑似来自许可证不兼容的源码请手动重写。依赖管理Go Modules 与 vendor-checkTempo 使用 Go modules 声明go 1.26.5以及 git。新增或更新依赖使用go get命令# 选取最新的 tagged release go get example.com/some/module/pkg # 选取特定版本 go get example.com/some/module/pkgvX.Y.Z提交前务必运行以下命令验证所有依赖与 proto 定义的一致性make vendor-checkTempo 维护了仓库内的vendor/目录可在仓库根目录看到因此依赖变更后 vendor 一致性检查是 CI 与本地提交的关键一环。项目结构先看懂再动手CONTRIBUTING.md 给出了 Tempo 的顶层目录结构结合当前仓库可以这样理解cmd/ tempo/ - 主 tempo 二进制 tempo-cli/ - 用于直接检查后端 block 的 CLI 工具 tempo-vulture/ - bird-themed 一致性检查器可选 tempo-query/ - jaeger-query GRPC 插件Apache2 许可 docs/ example/ - 开始运行 Tempo 的最佳起点 docker-compose/ tk/ integration/ - e2e 测试 modules/ - Tempo 顶层组件 backend-worker/ backend-scheduler/ distributor/ overrides/ querier/ frontend/ storage/ opentelemetry-proto/ - git 子模块proto vendoring 必需 operations/ - Tempo 部署与监控资源Apache2 许可 jsonnet/ tempo-mixin/ pkg/ tempopb/ - 与各 Tempo 服务交互的 protoApache2 许可 tempodb/ - 对象存储 key/value 数据库 vendor/对照当前仓库各目录均有大量实现cmd/tempo/main.go、cmd/tempo-cli/main.go、cmd/tempo-vulture/main.go、cmd/tempo-query/main.go四个二进制入口齐全modules/下除了文档列出的组件还有distributor/、generator/、livestore/、blockbuilder/等活跃模块docs/目录包含design-proposals/设计提案、internal/内部流程内容与图表与sources/全部产品文档。新贡献者可以据此快速定位我改的东西属于哪一层。编码规范Go 代码的标准姿势Go imports 分组imports 必须遵循标准库、外部库、本地包三段式格式import ( context fmt github.com/gogo/protobuf/proto github.com/opentracing/opentracing-go github.com/grafana/tempo/modules/overrides github.com/grafana/tempo/pkg/validation )错误处理遵循标准 Go 错误处理模式错误沿调用栈向上返回不要吞掉除真正不可恢复的场景外避免panic。立即处理错误并提前返回——happy path 保持在正常缩进层级不要嵌进else// good err : doSomething() if err ! nil { level.Error(logger).Log(msg, failed to do something, err, err) return err } // happy path continues here at normal indentation // avoid err : doSomething() if err nil { // happy path buried inside else } else { return err }用%w包裹错误并附上上下文return fmt.Errorf(failed to create tempodb: %w, err)始终附加一段简短上下文描述当前函数正在做什么从而构建可读的错误链同时保留原始错误供后续检查。在包级别定义哨兵错误sentinel errorsvar ( ErrDoesNotExist errors.New(does not exist) ErrEmptyTenantID errors.New(empty tenant id) )使用errors.New创建哨兵值当包外调用方需要检查时以Err*形式导出。用errors.Is和errors.As检查错误// 检查哨兵错误 if errors.Is(err, backend.ErrDoesNotExist) { ... } // 检查 context 取消 if errors.Is(err, context.Canceled) { ... } // 提取自定义错误类型 var parseErr *ParseError if errors.As(err, parseErr) { ... }优先使用errors.Is/errors.As而非直接相等比较或字符串匹配——它们会遍历由%w包装形成的错误链。为结构化错误数据定义自定义错误类型当调用方需要检查错误字段而不仅是身份时定义实现error接口的结构体若类型包装了另一个错误添加Unwrap()方法type ParseError struct { msg string line int col int } func (e *ParseError) Error() string { return fmt.Sprintf(parse error at line %d, col %d: %s, e.line, e.col, e.msg) }使用结构化 key-value 对记录错误level.Error(logger).Log(msg, failed to flush block, tenant, tenantID, err, err) level.Warn(logger).Log(msg, skipped span processing, err, err)用level.Error表示意外失败level.Warn表示预期或可恢复情况始终以err, err作为最后一对 key-value。对高频触发的错误使用限速日志器log.NewRateLimitedLogger。defer 的使用用defer将清理与获取配对——清理语句应紧跟在其所对应的调用之后而不是放在函数末尾。打开迭代器/读取器后立即关闭iter, err : block.Iterator() if err ! nil { return err } defer iter.Close()加锁后立即解锁mu.Lock() defer mu.Unlock()创建 context 后立即取消ctx, cancel : context.WithTimeout(ctx, 30*time.Second) defer cancel()启动 span 后立即结束ctx, span : tracer.Start(ctx, operationName) defer span.End()创建 ticker/timer 后立即停止ticker : time.NewTicker(interval) defer ticker.Stop()用匿名defer函数处理条件化或依赖错误的清理。当清理逻辑依赖函数返回值或需要检查错误时使用匿名函数。常见模式是仅在失败时记录 span 错误var err error defer func() { if err ! nil { span.RecordError(err) } }()另一个常见用途是在长运行 goroutine 中从 panic 恢复defer func() { if r : recover(); r ! nil { level.Error(logger).Log(msg, recovered from panic, err, r, stack, string(debug.Stack())) err errors.New(recovered from panic) } }()以及优雅关停——仅在启动中途失败时停止子服务defer func() { if err ! nil w.subservices ! nil { if stopErr : services.StopManagerAndAwaitStopped(context.Background(), w.subservices); stopErr ! nil { level.Error(logger).Log(msg, failed to stop dependencies, err, stopErr) } } }()接口实现断言在文件顶部做编译期接口实现校验var _ SomeInterface (*ConcreteType)(nil)可观测性Instrumentation每个非平凡组件都应输出 metrics、logs 和 traces。新增功能时应从一开始就纳入可观测性而不是事后补加MetricsTempo 使用 Prometheus metrics当前仓库operations/tempo-mixin/下含 dashboards、alerts.jsonnet、rules.libsonnet 等监控资源。LogsTempo 使用 go-kit log以keyvaluelogfmt格式输出结构化日志。使用github.com/go-kit/log/level下的 level 函数例如level.Info(logger).Log(msg, started, tenant, tenantID)。高频事件使用限速日志。TracesTempo 使用 OpenTelemetry 做追踪埋点。测试单元、本地与集成三级体系Tempo 力求大部分功能都有充分测试单元测试在隔离环境中测试代码功能。*_test.go文件与被测代码放在一起多输入场景优先使用t.Run()子测试的表驱动测试table-driven tests。本地测试使用 examples provided 中的docker-compose、tanka或helm部署本地环境验证新功能。集成测试端到端测试摄取与查询路径。这些测试位于 integration 目录——当前仓库下按领域拆分为integration/api、integration/limits、integration/metrics-generator、integration/operations、integration/storage、integration/util等子包对应 Makefile 中的make test-integration-*系列目标。断言使用 testify 库assert用于非致命检查require用于致命检查。提交前运行测试make test查看覆盖率make test-with-coverCI 会在每个 PR 上运行这些测试。格式化与 Lint提交 PR 前运行 lintmake lint只检查相对基分支的改动大 PR 更快make lint basemain修复格式问题make fmt这要求gofumpt和goimports位于$PATH中。本项目使用gofumptgofmt的更严格超集做格式化。可以按 gofumpt 文档 配置编辑器或在提交前运行make fmt。如果改动涉及 jsonnet 或 libsonnet 文件还要运行make jsonnetfmt这要求jsonnetfmt二进制位于$PATH。编译 jsonnet编译 jsonnet 文件运行make jsonnet这要求jsonnet、jsonnet-bundler和tanka二进制位于$PATH。Tempo 的部署资源operations/下的 jsonnet 与 libsonnet依赖这套工具链。代码生成proto 与 TraceQL 语法如果改动任何.proto文件重新生成 Go 代码make gen-proto从 Makefile 的gen-proto目标可以看到它先删除并重建opentelemetry-proto子模块经 buf 中间目录修补后使用buf/下的buf.gen.*.yaml模板分别生成 OpenTelemetry proto、Tempo protopkg/tempopb/tempo.proto、backendwork.proto、backend prototempodb/backend/v1/v1.proto与 frontend proto。如果改动 TraceQL 语法.y文件重新生成解析器make gen-traceql对应目标使用goyacc -l -o pkg/traceql/expr.y.go pkg/traceql/expr.y生成解析器。生成文件*.pb.go、*.y.go、*.gen.go不参与格式化与 lint——不要手工编辑它们。Pull Request 全流程签名提交是硬性要求自 2026 年 6 月 22 日起所有 Grafana Labs 仓库包括 Tempo要求签名提交。可参考提交签名验证说明以及检查签名状态。注意未签名的提交和 PR 会被拒绝并关闭包括由 Agent 发起的 PR。PR 描述每个 PR 必须有清晰的描述覆盖这个 PR 做了什么总结改动内容及其动机修复了哪个些issue使用Fixes #issue number以便合并时自动关闭 issue。PR 检查清单在标记 PR 可评审ready for review之前确认为改动行为更新或新增了测试新增或更新了文档见文档一节在.chloggen/下添加了 changelog 条目见Changelog 条目一节提交消息语义化前缀提交消息使用语义化前缀type: short description常见类型fix:— 缺陷修复feat:— 新特性enhancement:— 对现有功能的改进chore:— 维护、依赖更新、构建变更refactor:— 不改变行为地重构docs:— 仅文档主题行保持简洁前缀之后小写。示例fix: use counter instead of gauge for compactor deduped spans metric enhancement: deduplicate spans within traces during block builder chore(deps): update module google.golang.org/api to v0.267.0Changelog 条目.chloggen所有改变行为的 PR特性、增强、缺陷修复、破坏性变更都必须包含 changelog 条目。仅依赖更新、纯文档变更、纯内部重构不需要这类 PR 打Skip Changelog、dependencies或type/docs标签或标题加chore:前缀。Tempo 使用 chloggen 管理CHANGELOG.md不要直接编辑CHANGELOG.md而是在.chloggen/下添加 YAML 文件避免共享 changelog 上的合并冲突。当前仓库.chloggen/下已有大量条目文件与config.yaml、TEMPLATE.yaml、summary.tmpl等支撑文件。创建条目make chlog-new # 以当前分支名命名文件 make chlog-new FILENAMEmy-change # 可选显式指定文件名文件名默认为当前分支名在main/master或 detached HEAD 上必须传FILENAME覆盖。编辑生成的.chloggen/name.yamlchange_type: enhancement # breaking | change | feature | enhancement | bug_fix | security component: metrics-generator # 必须位于 .chloggen/config.yaml 的 components 白名单 note: Short description of the change. issues: [] # 可选PR 编号留空则发布时自动填充 subtext: # 可选的补充细节 user: your-github-handle # 渲染为 (your-github-handle)change_type关键字对应渲染章节breaking→ Breaking changes、change→ Changes、feature→ Features、enhancement→ Enhancements、bug_fix→ Bug fixes、security→ Security具体章节标题可见.chloggen/config.yaml中的 change_types 定义。issues可选留空时chlog-update会在发布时从添加该条目文件的提交中解析 PR 编号回填详见.chloggen/README.md。component必须在.chloggen/config.yaml的允许列表中否则chlog-validate拒绝引入新组件时要在同一 PR 中同步加入该列表。当前白名单覆盖distributor、querier、query-frontend、compactor、metrics-generator、block-builder、live-store、backend-scheduler、backend-worker、overrides、cache、storage、traceql、api、tempo、tempo-cli、tempo-query、tempo-vulture、operations、docs、deps。写 note 时保持简短至多一两句聚焦用户影响实现细节放subtext。例如对于 parquet 迭代器谓词下推的改动✅Improve read performance by pushing down predicates to the parquet iterators.❌Add support for pushdown predicates in the parquet iterators.推送前校验与预览make chlog-validate make chlog-preview发布时维护者操作make chlog-update VERSIONvX.Y.Z # 将条目汇总进 CHANGELOG.md 并删除条目文件保持 PR 同步PR 与main失步时应rebase不要 mergemain进分支。一旦 PR 收到评审意见避免 force push包括git push --force-with-lease——重写历史会破坏 GitHub 的自上次评审以来的变更视图迫使评审者重读整个 PR。应通过追加新提交回应评审意见。若确需 rebasemain解决冲突请单独推送 rebase不夹杂其他改动并在 PR 评论中说明。这一点对 AI 编码 Agent 加倍适用——它们倾向于默认使用--force-with-lease。文档贡献任何人都可以参与 Tempo 文档写新内容、更新现有内容或创建 issue。当前文档项目在 GitHub issues 中跟踪。目录结构Tempo 文档位于docs目录包含三个子目录design-proposals项目和功能提案不随产品文档发布当前仓库下有 2022-04 Parquet.md、2022-04 TraceQL Concepts.md、2023-11 TraceQL Metrics.md 等提案internal内部流程相关内容包括图表sources全部产品文档所在地helm-charts文件夹包含tempo-distributedHelm chart 的文档tempo文件夹包含产品文档。文档贡献方式写作前可参考 Grafana Writers Toolkit 获取高质量文档的编写指南与文档模板。创建文档 PR 时添加type/doc标签标识其为文档贡献。若内容需要合入之前的版本为对应版本添加backport标签——PR 合并后该标签会触发自动流程创建额外 PR 将内容合入该版本分支。检查该 PR 中是否有不适用于该版本的内容例如把 TraceQL 信息 backport 到 Tempo 1.5。本地预览文档在仓库根目录运行make docs该命令使用grafana/docs镜像内部用 Hugo 生成静态站点站点运行于localhost:3002/docs/。make docs-test则执行文档生成测试。注意make docs非常吃内存。若崩溃请增加分配给 Docker 的内存后重试。发布流程Tempo 使用 CI action 将文档同步到 Grafana 网站CI 在每次合入main的docs子目录改动时触发。helm-charts文件夹从 next 分支发布Tempo 文档从latest分支发布。调试使用调试器有助于定位 Tempo 代码中的问题。仓库提供了 docker-compose 调试示例——该目录包含docker-compose.yaml、tempo.yaml配置与readme.md演示如何在 docker-compose 内调试 Tempo如配合 GoLand 远程调试。结语从签名提交、AI 辅助贡献披露、make vendor-check依赖校验到 import 三段式、%w错误链、defer配对清理、RED 指标与 logfmt 日志再到.chloggen/的 YAML 条目与make docs预览——Tempo 的贡献规范把高质量开源协作落实成了一个个可执行的命令与可勾选的清单。对照 CONTRIBUTING.md 与本文逐项执行你就能以维护者期望的方式安全地把自己的第一个改动合入这个高吞吐分布式追踪后端。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表