Go可视化工具 绘制项目代码结构和依赖
比较常见pprof 可以把调用栈可视化成调用图,embedded-struct-visualizer 可以把Go 的项目的代码分层结构和依赖都可视化成流程图。
安装 embedded-struct-visualizer
go install github.com/davidschlachter/embedded-struct-visualizer@latest
查看命令选项参数
embedded-struct-visualizer -h
Usage: [OPTIONS] DirToScan
If the directory to scan is not provided, it defaults to './'
OPTIONS:
-out <file> path to output file (default: write to stdout)
-v verbose logging
Example
以官方给的例子 main.go
package main
import (
"time"
)
type A struct {
B
C map[string]D
}
type B struct {
E, F string
G string
Timer H
}
type D struct {
I uint64
}
type H struct {
Timer time.Ticker
J chan D
}
生成结构关系:
> embedded-struct-visualizer
digraph {
"main.A" -> { "main.B" "main.D" };
"main.B" -> { "main.H" };
"main.H" -> { "time.Ticker" "main.D" };
}
生成结构关系 并 输出到.gv 文件:
>embedded-struct-visualizer -out .\example.gv
> ls
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/11/27 10:54 112 example.gv
-a---- 2022/11/26 22:06 31 go.mod
-a---- 2022/11/26 22:07 208 main.go
项目目录下多出了example.gv ,具体内容如下
cat .\example.gv
digraph {
"main.A" -> { "main.B" "main.D" };
"main.B" -> { "main.H" };
"main.H" -> { "time.Ticker" "main.D" };
安装 graphviz
graphviz 是一种将结构信息表示为抽象图和网络的图的工具。
接着,把生成的gv文件利用graphviz绘制的更美观。
首先下载安装:
https://graphviz.org/download/ 查看是否安装成功(Win):
dot -v
dot - graphviz version 7.0.2 (20221119.0110)
... <为了不篇幅过长,省略一些细节>
利用graphviz绘图
dot -Tformat[:renderer[:formatter]] -o 将输出语言设置为支持的格式之一。默认情况下,生成带属性的点。
接着把上一步生成的gv文件,生成PNG输出
dot -Tpng example.gv -o example.png
在项目目录下多出example.png
接着,实用于一个相对复杂的Gin Engine的gv
-
生成的gv:
embedded-struct-visualizer -out .\gin.gv
gin.gv 详细如下:
digraph {"gin.mockWriter" -> { "http.Header" };"binding.QueryTest" -> { "binding.appkey" };"binding.FooBarStruct" -> { "binding.FooStruct" };"binding.FooBarFileStruct" -> { "binding.FooBarStruct"
"multipart.FileHeader" };"binding.FooBarFileFailStruct" -> { "binding.FooBarStruct"
"multipart.FileHeader" };"binding.FooDefaultBarStruct" -> { "binding.FooStruct" };"binding.FooStructUseNumber" -> { "binding.any" };"binding.FooStructDisallowUnknownFields" -> { "binding.any"
};"binding.FooBarStructForTimeType" -> { "time.Time" };"binding.FooStructForTimeTypeNotUnixFormat" -> { "time.Time"
};"binding.FooStructForTimeTypeNotFormat" -> { "time.Time" };"binding.FooStructForTimeTypeFailFormat" -> { "time.Time" };"binding.FooStructForTimeTypeFailLocation" -> { "time.Time"
};"binding.FooStructForMapType" -> { "binding.any" };"binding.FooStructForIgnoreFormTag" -> { "binding.string" };"binding.defaultValidator" -> { "sync.Once"
"validator.Validate" };"binding.structFull" -> { "binding." "time.Time"
"binding.string" };"binding.S" -> { "binding.S" };"binding.testFile" -> { "binding.byte" };"binding.structNoValidationValues" -> {
"binding.substructNoValidation" "binding.int16"
"binding.uint16" "time.Time" "binding.mapNoValidationSub"
"binding." };"binding.structNoValidationPointer" -> {
"binding.substructNoValidation" "binding.uint32"
"binding.mapNoValidationSub" "binding.int8" "binding.int32"
"binding.uint8" "binding.uint16" "binding.float64"
"binding.map" "binding.int" "binding.int16" "binding.int64"
"binding.float32" "time.Time" "binding.testInterface"
"binding.uint" "binding.uint64" "binding.string" };"gin.Context" -> { "url.Values" "gin.responseWriter"
"gin.HandlersChain" "gin.any" "gin.errorMsgs"
"gin.skippedNode" "sync.RWMutex" "gin.string"
"http.SameSite" "http.Request" "gin.ResponseWriter"
"gin.Params" "gin.Engine" };"gin.interceptedWriter" -> { "gin.ResponseWriter"
"bytes.Buffer" };"gin.Error" -> { "gin.error" "gin.ErrorType" "gin.any" };"gin.onlyFilesFS" -> { "http.FileSystem" };"gin.neuteredReaddirFile" -> { "http.File" };"gin.RouteInfo" -> { "gin.HandlerFunc" };"gin.Engine" -> { "gin.RouterGroup" "gin.string"
"render.HTMLRender" "gin.HandlersChain" "sync.Pool"
"gin.methodTrees" "render.Delims" "template.FuncMap"
"gin.uint16" "net.IPNet" };"gin.LoggerConfig" -> { "gin.LogFormatter" "io.Writer"
"gin.string" };"gin.LogFormatterParams" -> { "http.Request" "time.Time"
"time.Duration" "gin.any" };"render.Data" -> { "render.byte" };"render.HTMLProduction" -> { "template.Template"
"render.Delims" };"render.HTMLDebug" -> { "render.string" "render.Delims"
"template.FuncMap" };"render.HTML" -> { "template.Template" "render.any" };"render.JSON" -> { "render.any" };"render.IndentedJSON" -> { "render.any" };"render.SecureJSON" -> { "render.any" };"render.JsonpJSON" -> { "render.any" };"render.AsciiJSON" -> { "render.any" };"render.PureJSON" -> { "render.any" };"render.MsgPack" -> { "render.any" };"render.ProtoBuf" -> { "render.any" };"render.Reader" -> { "io.Reader" };"render.Redirect" -> { "http.Request" };"render.String" -> { "render.any" };"render.TOML" -> { "render.any" };"render.XML" -> { "render.any" };"render.YAML" -> { "render.any" };"gin.responseWriter" -> { "http.ResponseWriter" };"gin.RouterGroup" -> { "gin.HandlersChain" "gin.Engine" };"protoexample.Test" -> { "protoexample.int32"
"protoexample.int64" "protoexample.TestOptionalGroup"
"protoimpl.MessageState" "protoimpl.SizeCache"
"protoimpl.UnknownFields" "protoexample.string" };"protoexample.Test_OptionalGroup" -> {
"protoimpl.MessageState" "protoimpl.SizeCache"
"protoimpl.UnknownFields" "protoexample.string" };"gin.methodTree" -> { "gin.node" };"gin.node" -> { "gin.node" "gin.HandlersChain"
"gin.nodeType" };"gin.nodeValue" -> { "gin.HandlersChain" "gin.Params" };"gin.skippedNode" -> { "gin.node" "gin.int16" };}
- 绘制png
dot -Tpng gin.gv -o gin.png
<ps: 图片过大可下载预览>
- graphviz guide优化: https://www.graphviz.org/pdf/dotguide.pdf