Prometheus が収集するメトリクス(Exposition formats)を直接パースできた方が何かと便利(データ分析等で)かと思い、試しに実装してみました。
メトリクスのパース
メトリクスをパースする機能は Prometheus に用意されているので、そのライブラリを使って実装すると次のようになりました。
sample1/main.go
package main
import (
"fmt"
"log"
"strings"
"github.com/prometheus/common/expfmt"
)
func main() {
// メトリクス
data := `
sample_total 10
test_metric{label="t1", proc="a1"} 123.4
`
p := expfmt.TextParser{}
// パース処理
mf, err := p.TextToMetricFamilies(strings.NewReader(data))
if err != nil {
log.Fatal(err)
}
for k, v := range mf {
fmt.Printf("key=%s, name=%s, type=%s \n", k, v.GetName(), v.GetType())
// ラベルの取得と出力
for _, l := range v.GetMetric()[0].Label {
fmt.Printf("label name=%s, value=%s \n", l.GetName(), l.GetValue())
}
// 値の取得と出力
fmt.Printf("untyped value=%f \n", v.GetMetric()[0].Untyped.GetValue())
fmt.Println("---")
}
}
実行結果は以下の通りです。
実行結果
$ cd sample1
$ go run main.go
key=sample_total, name=sample_total, type=UNTYPED
untyped value=10.000000
---
key=test_metric, name=test_metric, type=UNTYPED
label name=label, value=t1
label name=proc, value=a1
untyped value=123.400000
---
Histogram の抽出と算出
次に、CoreDNS のメトリクスから次のような Histogram 形式のデータだけを対象にして、増加した分を算出する処理を実装してみました。
Histogram 例
# HELP coredns_dns_request_duration_seconds Histogram of the time (in seconds) each request took per zone.
# TYPE coredns_dns_request_duration_seconds histogram
coredns_dns_request_duration_seconds_bucket{server="dns://:53",view="",zone=".",le="0.00025"} 22
coredns_dns_request_duration_seconds_bucket{server="dns://:53",view="",zone=".",le="0.0005"} 22
...省略
Histogram は GetType
の戻り値が dto.MetricType_HISTOGRAM
かどうかで判定できます。
また、Histogram の場合は _bucket
部分が除外された名称になります。
sample2/main.go
package main
import (
"fmt"
"log"
"os"
"strings"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
// ラベルの連結
func joinLabel(ps []*dto.LabelPair) string {
rs := []string{}
for _, p := range ps {
rs = append(rs, fmt.Sprintf("%s:%s", p.GetName(), p.GetValue()))
}
return strings.Join(rs, ",")
}
func main() {
file := os.Args[1]
f, err := os.Open(file)
if err != nil {
log.Fatal(err)
}
defer f.Close()
p := expfmt.TextParser{}
mf, err := p.TextToMetricFamilies(f)
if err != nil {
log.Fatal(err)
}
for k, v := range mf {
// Histogram の判定
if v.GetType() == dto.MetricType_HISTOGRAM {
for _, m := range v.GetMetric() {
hist := m.GetHistogram()
label := joinLabel(m.GetLabel())
sum := hist.GetSampleSum() //合計の取得
lastCount := uint64(0)
for _, b := range hist.GetBucket() {
// 差分の算出
count := b.GetCumulativeCount() - lastCount
lastCount = b.GetCumulativeCount()
if count > 0 {
fmt.Printf(
"name=%s, label=[%s], sum=%f, le=%f, count=%d \n",
k, label, sum, b.GetUpperBound(), count,
)
}
}
}
}
}
}
実行結果は次のようになりました。
実行結果
$ cd sample2
$ go run main.go coredns_metrics.txt
name=coredns_forward_request_duration_seconds, label=[rcode:NOERROR,to:192.168.5.3:53], sum=3.013131, le=0.001000, count=1
name=coredns_forward_request_duration_seconds, label=[rcode:NOERROR,to:192.168.5.3:53], sum=3.013131, le=0.002000, count=1
name=coredns_forward_request_duration_seconds, label=[rcode:NOERROR,to:192.168.5.3:53], sum=3.013131, le=0.004000, count=2
name=coredns_forward_request_duration_seconds, label=[rcode:NOERROR,to:192.168.5.3:53], sum=3.013131, le=1.024000, count=3
name=coredns_dns_request_duration_seconds, label=[server:dns://:53,view:,zone:.], sum=4.017252, le=0.000250, count=22
name=coredns_dns_request_duration_seconds, label=[server:dns://:53,view:,zone:.], sum=4.017252, le=0.001000, count=2
name=coredns_dns_request_duration_seconds, label=[server:dns://:53,view:,zone:.], sum=4.017252, le=0.004000, count=2
name=coredns_dns_request_duration_seconds, label=[server:dns://:53,view:,zone:.], sum=4.017252, le=0.008000, count=1
name=coredns_dns_request_duration_seconds, label=[server:dns://:53,view:,zone:.], sum=4.017252, le=1.024000, count=4
name=coredns_health_request_duration_seconds, label=[], sum=0.380166, le=0.000250, count=3
name=coredns_health_request_duration_seconds, label=[], sum=0.380166, le=0.002500, count=680
name=coredns_health_request_duration_seconds, label=[], sum=0.380166, le=0.025000, count=1
name=coredns_dns_response_size_bytes, label=[proto:udp,server:dns://:53,view:,zone:.], sum=3501.000000, le=100.000000, count=11
name=coredns_dns_response_size_bytes, label=[proto:udp,server:dns://:53,view:,zone:.], sum=3501.000000, le=200.000000, count=20
name=coredns_dns_request_size_bytes, label=[proto:udp,server:dns://:53,view:,zone:.], sum=1957.000000, le=100.000000, count=31