其实前三篇根本算不上源码分析,只是对项目的简单了解和环境的搭建。而从这一篇开始,我们才真正能接触到scope项目的源码。
在go语言中,我们可以用var来定义一组变量,比如:
var(
name string
age int
)
我们先看main.go中的main函数,一开头用var定义了一组变量。
var (
flags = flags{}
mode string
debug bool
weaveEnabled bool
weaveHostname string
dryRun bool
containerLabelFilterFlags = containerLabelFiltersFlag{exclude: false, filterIDPrefix: "containerLabelFilterExclude"}
containerLabelFilterFlagsExclude = containerLabelFiltersFlag{exclude: true, filterIDPrefix: "containerLabelFilter"}
)
在go语言中,flag包用于解析命令行参数。其使用方法主要包括三个步骤。
第一步:定义flag参数(有三种形式)
形式1:通过flag.String(),flag.Bool(),flag.Int()等flag.Xxx()方法,该方法返回一个相应的指针。
var help=flag.String("name","jack","main.exe --name \"jack\"")
形式2:通过flag.XxxVar()方法将flag参数绑定到一个变量。
flag.IntVar(&age,"age",18,"--age 17")
在scope中,用到探针和app的flags变量就是采用的这种方法。
// Flags that apply to both probe and app
flag.StringVar(&mode, "mode", "help", "For internal use.")
flag.BoolVar(&debug, "debug", false, "Force debug logging.")
flag.BoolVar(&dryRun, "dry-run", false, "Don't start scope, just parse the arguments. For internal use only.")
flag.BoolVar(&weaveEnabled, "weave", true, "Enable Weave Net integrations.")
flag.StringVar(&weaveHostname, "weave.hostname", app.DefaultHostname, "Hostname to advertise/lookup in WeaveDNS")
形式3:通过flag.Var()方法绑定自定义类型,自定义类型需要实现Value接口。
在scope中,也有用到这种方法。例如:
flag.Var(&containerLabelFilterFlags, "app.container-label-filter", "Add container label-based view filter, specified as title:label. Multiple flags are accepted. Example: --app.container-label-filter='Database Containers:role=db'")
flag.Var(&containerLabelFilterFlagsExclude, "app.container-label-filter-exclude", "Add container label-based view filter that excludes containers with the given label, specified as title:label. Multiple flags are accepted. Example: --app.container-label-filter-exclude='Database Containers:role=db'")
而containerLabelFilterFlags和containerLabelFilterFlagsExclude都是containerLabelFiltersFlag变量,containerLabelFiltersFlag则实现了Value接口,例如:
type containerLabelFiltersFlag struct {
apiTopologyOptions []app.APITopologyOption
filterNumber int
filterIDPrefix string
exclude bool
}
func (c *containerLabelFiltersFlag) String() string {
return fmt.Sprint(c.apiTopologyOptions)
}
func (c *containerLabelFiltersFlag) Set(flagValue string) error {
filterID := fmt.Sprintf(c.filterIDPrefix+"%d", c.filterNumber)
newAPITopologyOption, err := c.toAPITopologyOption(flagValue, filterID)
if err != nil {
return err
}
c.filterNumber++
c.apiTopologyOptions = append(c.apiTopologyOptions, newAPITopologyOption)
return nil
}
func (c *containerLabelFiltersFlag) toAPITopologyOption(flagValue string, filterID string) (app.APITopologyOption, error) {
indexRanges := colonFinder.FindAllStringIndex(flagValue, -1)
if len(indexRanges) != 1 {
if len(indexRanges) == 0 {
return app.APITopologyOption{}, fmt.Errorf("No unescaped colon found. This is needed to separate the title from the label")
}
return app.APITopologyOption{}, fmt.Errorf("Multiple unescaped colons. Escape colons that are part of the title and label")
}
splitIndices := indexRanges[0]
titleStringEscaped := flagValue[:splitIndices[0]+1]
labelStringEscaped := flagValue[splitIndices[1]:]
containerFilterTitle := unescapeBackslashes.ReplaceAllString(titleStringEscaped, `$1`)
containerFilterLabel := unescapeBackslashes.ReplaceAllString(labelStringEscaped, `$1`)
labelKeyValuePair := strings.Split(containerFilterLabel, "=")
if len(labelKeyValuePair) != 2 {
return app.APITopologyOption{}, fmt.Errorf("Docker label isn't in the correct key=value format")
}
filterFunction := render.HasLabel
if c.exclude {
filterFunction = render.DoesNotHaveLabel
}
return app.MakeAPITopologyOption(filterID, containerFilterTitle, filterFunction(labelKeyValuePair[0], labelKeyValuePair[1]), false), nil
}
第二步:调用flag.Parse()解析命令行参数到定义的flag。
flag.Parse()
第三步:直接使用flag本身或者其绑定的变量。
在scope中的main.go代码的最下面,我们看到经过一系列的命令行参数解析后最终会调用一下方法。
switch mode {
case "app":
appMain(flags.app)
case "probe":
probeMain(flags.probe, targets)
case "version":
fmt.Println("Weave Scope version", version)
case "help":
flag.PrintDefaults()
default:
fmt.Printf("command '%s' not recognized", mode)
os.Exit(1)
}
其中appMain和probeMain是我们应该重点关注的两个方法。接下来我们先跟踪下probeMain这个方法。毕竟它的意思是探针。对于监控数据的收集应该是在这个探针里面。