mackerel-plugin-accesslogを中心にMackerelのコードを読んだ
グラフのラベル名をいい感じにする簡単なPRを投げた時のメモ。
公式ブログを読めばなんとなく機能的な部分はわかった。
mackerel.io
mackerel.io
mackerel-plugin-accesslog/main.go
のmpaccesslog.Do()
呼び出されているmp.NewMackerelPlugin(...).Run()
は、go-mackerel-plugin/mackerel-plugin.go
に定義されている。
mp.NewMackerelPlugin(&AccesslogPlugin{
prefix: *optPrefix,
file: flag.Args()[0],
posFile: *optPosFile,
noPosFile: *optNoPosFile,
parser: parser,
}).Run()
// Run the plugin func (mp *MackerelPlugin) Run() { if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" { mp.OutputDefinitions() } else { mp.OutputValues() } }
このgo-mackerel-plugin
のmp.OutputValues()
でmackerel-plugin-accesslog
のFetchMetrics
が呼び出されている。
// OutputValues output the metrics func (mp *MackerelPlugin) OutputValues() { now := time.Now() stat, err := mp.FetchMetrics()
mackerel-plugin-accesslog
のFetchMetrics
では、まずgetReadCloser()
でp.file
つまり flag.Args()[0]
で指定されたアクセスログのファイルが開かれる。
// FetchMetrics interface for mackerelplugin func (p *AccesslogPlugin) FetchMetrics() (map[string]float64, error) { rc, takeMetrics, err := p.getReadCloser()
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で読み込んでいくという流れ。
r := bufio.NewReader(rc) ... buf, isPrefix, err := r.ReadLine() ...
ちなみにアクセスログのパーサーはoptFormat = flag.String("format", "", "Access Log format ('ltsv' or 'apache')")
でltsv
かapache
が指定されてパースされている。
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で、ltsv
もapache
も指定されていなかった場合は推測してくれるらしい。
あとはret := make(map[string]float64)
のマップに結果を集計していってそれを返しておしまい。
ret[string(fmt.Sprintf("%d", l.Status)[0])+"xx_count"]++ ret["total_count"]++ ... ret[v+"_percentage"] = ret[v+"_count"] * 100 / ret["total_count"]