経験は何よりも饒舌

10年後に真価を発揮するかもしれないブログ 

mackerel-plugin-accesslogを中心にMackerelのコードを読んだ

f:id:wafuwafu13:20210606132640p:plain


グラフのラベル名をいい感じにする簡単なPRを投げた時のメモ。

[plugin-aws-cloudfront]Replace label name of graph by wafuwafu13 · Pull Request #766 · mackerelio/mackerel-agent-plugins · GitHub


公式ブログを読めばなんとなく機能的な部分はわかった。
mackerel.io
mackerel.io

mackerel-plugin-accesslog/main.gompaccesslog.Do()呼び出されているmp.NewMackerelPlugin(...).Run()は、go-mackerel-plugin/mackerel-plugin.goに定義されている。

mackerel-agent-plugins/accesslog.go at df9c8643da21869d54cf8625dd7523d88b1c4e20 · mackerelio/mackerel-agent-plugins · GitHub

mp.NewMackerelPlugin(&AccesslogPlugin{
	prefix:    *optPrefix,
	file:      flag.Args()[0],
	posFile:   *optPosFile,
	noPosFile: *optNoPosFile,
	parser:    parser,
}).Run()

go-mackerel-plugin/mackerel-plugin.go at 42f2323dad976a17b36639d1ddbefa85ad8e771e · mackerelio/go-mackerel-plugin · GitHub

// Run the plugin
func (mp *MackerelPlugin) Run() {
	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		mp.OutputDefinitions()
	} else {
		mp.OutputValues()
	}
}

このgo-mackerel-pluginmp.OutputValues()mackerel-plugin-accesslogFetchMetricsが呼び出されている。

go-mackerel-plugin/mackerel-plugin.go at 42f2323dad976a17b36639d1ddbefa85ad8e771e · mackerelio/go-mackerel-plugin · GitHub

// OutputValues output the metrics
func (mp *MackerelPlugin) OutputValues() {
	now := time.Now()
	stat, err := mp.FetchMetrics()


mackerel-plugin-accesslogFetchMetricsでは、まずgetReadCloser()p.fileつまり flag.Args()[0]で指定されたアクセスログのファイルが開かれる。

mackerel-agent-plugins/accesslog.go at df9c8643da21869d54cf8625dd7523d88b1c4e20 · mackerelio/mackerel-agent-plugins · GitHub

// FetchMetrics interface for mackerelplugin
func (p *AccesslogPlugin) FetchMetrics() (map[string]float64, error) {
	rc, takeMetrics, err := p.getReadCloser()

mackerel-agent-plugins/accesslog.go at df9c8643da21869d54cf8625dd7523d88b1c4e20 · mackerelio/mackerel-agent-plugins · GitHub

func (p *AccesslogPlugin) getReadCloser() (io.ReadCloser, bool, error) {
	if p.noPosFile {
		rc, err := os.Open(p.file)
		return rc, true, err
	}

それをbufio.NewReader, Reader.Readlineで読み込んでいくという流れ。

mackerel-agent-plugins/accesslog.go at df9c8643da21869d54cf8625dd7523d88b1c4e20 · mackerelio/mackerel-agent-plugins · GitHub

r := bufio.NewReader(rc)
...
buf, isPrefix, err := r.ReadLine()
...

ちなみにアクセスログのパーサーはoptFormat = flag.String("format", "", "Access Log format ('ltsv' or 'apache')")ltsvapacheが指定されてパースされている。

mackerel-agent-plugins/accesslog.go at df9c8643da21869d54cf8625dd7523d88b1c4e20 · mackerelio/mackerel-agent-plugins · GitHub

switch *optFormat {
case "":
	parser = nil // guess format by log (default)
case "ltsv":
	parser = parsers.LTSV
case "apache":
	parser = parsers.Apache
if p.parser == nil {
	p.parser, l, err = parsers.GuessParser(line)
} else {
	l, err = p.parser.Parse(line)
}

パーサーの実態はGitHub - Songmu/axslogparser: Fairly accurate access Log Parserで、ltsvapacheも指定されていなかった場合は推測してくれるらしい。

あとはret := make(map[string]float64)のマップに結果を集計していってそれを返しておしまい。

mackerel-agent-plugins/accesslog.go at df9c8643da21869d54cf8625dd7523d88b1c4e20 · mackerelio/mackerel-agent-plugins · GitHub

ret[string(fmt.Sprintf("%d", l.Status)[0])+"xx_count"]++
ret["total_count"]++
...
ret[v+"_percentage"] = ret[v+"_count"] * 100 / ret["total_count"]