thzgajendra/reqflow

GitHub: thzgajendra/reqflow

reqflow 是用于静态追踪 Go 代码库中 HTTP 请求执行路径的工具,通过命令行快速定位路由与层间调用关系。

Stars: 1 | Forks: 0

# reqflow [![Go Reference](https://pkg.go.dev/badge/github.com/thzgajendra/reqflow.svg)](https://pkg.go.dev/github.com/thzgajendra/reqflow) [![Go Report Card](https://goreportcard.com/badge/github.com/thzgajendra/reqflow)](https://goreportcard.com/report/github.com/thzgajendra/reqflow) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) **静态追踪 Go 代码库中任意 HTTP 请求的执行路径。** 一条命令。无需插桩。无需运行时。只需指向你的代码库。 ``` go install github.com/thzgajendra/reqflow/cmd/reqflow@latest ``` ## 为什么选择 reqflow 你刚加入一个团队。`POST /orders` 出现了一个 Bug。你该从哪里开始排查? 你搜索路由,找到处理器,Ctrl/Command 点击进入服务层,再 Ctrl/Command 点击进入存储层,读取结构体标签以确定数据库表名。你每次都要重复这些操作,无论仓库还是 Bug 类型。 **reqflow 只需一条命令,就能完成所有这些步骤。** ## 快速开始 ``` cd your-go-project/ reqflow trace "/orders" ./... ``` 仅此而已。reqflow 会解析你的代码,找到所有注册在 `/orders` 上的路由,让你选择一个,并展示完整的请求路径,包括精确的方法名和文件位置。 ## 命令 ### `reqflow trace` — 追踪请求路径 只需输入路径。reqflow 会查找所有注册在该路径上的 HTTP 方法,让你选择: ``` $ reqflow trace "/orders" ./... Multiple routes match "/orders": 1. GET /orders 2. POST /orders Enter number (1-2): 2 POST /orders ────────────── [H] OrderHandler internal/handler/orders.go:45 CreateOrder() → svc.Create() │ ↓ delegates to │ [S] orderService internal/service/orders.go:89 Create() → store.Insert() │ ↓ queries via │ [D] OrderStore internal/store/orders.go:67 Insert() ``` 其他追踪方式: ``` reqflow trace "POST /orders" ./... # exact route reqflow trace "budget" ./... # substring — shows all matches ``` ### `reqflow routes` — 列出服务中的所有路由 ``` $ reqflow routes ./... GET /orgs/{orgID}/budgets Handler.GetBudgets() handler/handler.go:350 POST /orgs/{orgID}/budgets Handler.CreateBudget() handler/handler.go:398 DELETE /orgs/{orgID}/budgets/{budgetID} Handler.DeleteBudget() handler/handler.go:442 GET /orgs/{orgID}/reports/metrics Handler.GetMetrics() handler/handler.go:700 ... 39 routes across 1 handlers ``` 导出为 JSON: ``` reqflow routes -format json ./... ``` ## 你得到的结果 每次追踪都会显示以下信息: | 部分 | 含义 | |------|---------| | `[H]` `[S]` `[D]` `[C]` `[M]` | 层 — 处理器、服务、存储、客户端、模型 | | `Handler` | 结构体名称 | | `internal/handler/handler.go:700` | 包、文件和 **方法所在行号**(而非结构体) | | `GetMetrics()` | 处理该路由的具体方法 | | `→ svc.GetMetricsByOrgPaginated()` | 该方法调用的下一层方法 | ## 实际示例 ### 处理器 → 服务 → 存储(最常见) ``` $ reqflow trace "GET /orgs/{orgID}/budgets" ./... [H] Handler internal/handler/handler.go:350 GetBudgets() → svc.GetBudgets() [S] service internal/service/service.go:4258 GetBudgets() → store.GetBudgetsByResourceUIDs() → store.GetBudgetsByResourceGroupIDs() → store.GetBudgets() [D] Store internal/store/store.go:32 GetBudgetsByResourceUIDs() GetBudgetsByResourceGroupIDs() GetBudgets() ``` ### 处理器 → 外部客户端(gRPC/HTTP) ``` $ reqflow trace "POST /orgs/{orgID}/actions" ./... [H] Handler internal/handler.go:80 ManualAction() → cloudAccountFetcher.GetPermissionLevel() → resourceFetcher.GetGroupForResource() [C] ConfigGRPCClient internal/config_grpc.go:18 GetPermissionLevel() → client.GetCloudAccountPermission() GetGroupForResource() [C] ConfigServiceClient internal/configclient/config_grpc.pb.go:35 GetCloudAccountPermission() ``` ### 交互式路由选择 ``` $ reqflow trace "budgets" ./... Multiple routes match "budgets": 1. GET /orgs/{orgID}/budgets/summary 2. GET /orgs/{orgID}/budgets 3. POST /orgs/{orgID}/budgets 4. PUT /orgs/{orgID}/budgets/{budgetID} 5. DELETE /orgs/{orgID}/budgets/{budgetID} Enter number (1-5): 3 ``` 选择一个数字,查看追踪结果。一次会话,无需重复运行。 ## 标志 ### `reqflow trace` ``` reqflow trace [flags] [packages] ``` | 标志 | 描述 | |------|-------------| | `-format text` | 终端输出(默认) | | `-format html` | 自包含 HTML 页面 | | `-out ` | 输出到文件而非标准输出 | | `-tablemap` | 显示从结构体标签映射的数据库表 | | `-envmap` | 显示通过 `os.Getenv` / `viper` 读取的环境变量 | ``` reqflow trace -format html -out trace.html "POST /orders" ./... reqflow trace -tablemap "GET /users/{id}" ./... reqflow trace -envmap "POST /orders" ./... ``` ### `reqflow routes` ``` reqflow routes [flags] [packages] ``` | 标志 | 描述 | |------|-------------| | `-format text` | 对齐的表格输出(默认) | | `-format json` | JSON 数组 | | `-out ` | 输出到文件而非标准输出 | ## 工作原理 reqflow 利用 Go 的类型系统进行分析 —— 而非依赖 grep 或正则表达式。 1. **使用 `golang.org/x/tools/go/packages` 加载包并遍历 AST** 2. **根据结构体特征进行分类** — 存储层是包含 `*sql.DB` 字段的结构体;处理器是方法接收 `*gofr.Context` 或 `*gin.Context` 的结构体;HTTP 客户端是名为 `*Client` 的结构体 3. **从 `app.GET("/path", h.Method)` 调用中提取路由** — 包括内联匿名处理器如 `app.GET("/health", func(ctx) { ... })` 4. **构建方法级调用索引** — 精确知道 `Handler.GetMetrics()` 调用了 `svc.GetMetricsByOrgPaginated()`,而不仅是“处理器依赖服务” 5. **追踪精确路径** — 跟踪实际的方法调用,而非结构体依赖关系。仅显示该特定请求所经过的节点和方法 ## 支持的框架 | 框架 | 处理器签名 | |-----------|-------------------| | [GoFr](https://gofr.dev) | `func(ctx *gofr.Context) (any, error)` | | [Gin](https://gin-gonic.com) | `func(c *gin.Context)` | | [Echo](https://echo.labstack.com) | `func(c echo.Context) error` | | [Fiber](https://gofiber.io) | `func(c *fiber.Ctx) error` | | net/http | `func(w http.ResponseWriter, r *http.Request)` | ### 层分类 | 层 | 标记 | 名称模式 | 结构检测 | |-------|-------|--------------|---------------------| | 处理器 | `[H]` | `*Handler`, `*Controller`, `*Endpoint` | 方法接收框架上下文 | | 服务 | `[S]` | `*Service`, `*UseCase`, `*Manager` | — | | 存储 | `[D]` | `*Store`, `*Repository`, `*Repo` | 包含数据库客户端字段(`*sql.DB`、`*gorm.DB` 等) | | 客户端 | `[C]` | `*Client`, `*Caller`, `*Connector` | — | | 模型 | `[M]` | `*Model`, `*Entity`, `*DTO` | 包含数据库结构体标签 | 可通过 `.reqflow.yml` 覆盖配置: ``` layers: service_pattern: ".*Service$|.*Processor$" store_pattern: ".*Store$|.*Repository$" model_pattern: ".*Model$|.*Entity$" ``` ## 配置 项目根目录下的可选配置文件 `.reqflow.yml`: ``` parser: ignore_packages: - vendor - _test - mock layers: service_pattern: ".*Service$" store_pattern: ".*Store$|.*Repository$|.*Repo$" model_pattern: ".*Model$|.*Entity$" ``` ## 作为 Go 库使用 ``` import "github.com/thzgajendra/reqflow" // Parse codebase graph, err := reqflow.Parse(reqflow.ParseOptions{Dir: "."}) // List all routes routes := reqflow.ListRoutes(graph) for _, r := range routes { fmt.Printf("%s %s → %s.%s()\n", r.Method, r.Path, r.HandlerName, r.MethodName) } // Trace a specific route result := reqflow.Trace("POST /orders", graph) fmt.Println(result.Route) for _, node := range result.Chain { fmt.Printf("[%s] %s\n", node.Kind, node.Name) } ``` ## 许可证 Apache 2.0 — 参见 [LICENSE](LICENSE)
标签:API路由, Go, Go代码库, HTTP路由追踪, Ruby工具, SEO, SOC Prime, 二进制发布, 云安全监控, 代码导航, 代码追踪, 后端开发, 开发工具, 开源工具, 性能分析, 数据管道, 日志审计, 源码分析, 请求路径分析, 软件工程, 静态分析