概要
Pushgateway にプログラムからメトリクスをpushする方法を紹介します。
push可能なメトリクスのフォーマットはText
とProtocol buffer
の2種類あります。
本記事ではProtocol buffer
フォーマットでpushする方法を紹介します。
Text
フォーマットでのpushについては以下記事をご参照ください。
Prometheus Pushgateway に プログラムからメトリクスをpushする [Text format編]
各言語向けライブラリ
「Prometheus」のクライアントライブラリに「Pushgateway」へのメトリクスpushメソッドがしれっと実装されていますので若干盲点ですが、各言語向けのライブラリがあります。
通常は以下のライブラリを使うのが良いと思います。
なお、全てのライブラリを確認したわけではありませんがProtocol buffer
フォーマットでpushする処理は見当たりませんでした。
また、ネット上を見渡してもProtocol buffer
のバイト列をPOSTする処理や、後述するデリミタ情報を付与することにより、Pushgatewayにメトリクスをpushできることを示しているコードは見当たりませんでした。このあたりの実装を見かけた方はぜひコメント欄で教えて下さい。
ということで今回自作してみました。まずは今回参照したドキュメントを紹介します。
Prometheus Client Data の仕様
以下ドキュメントにPrometheus Client Dataのフォーマットが記載されています。
Protocol buffer
フォーマットでpushする場合は以下に気をつけると良さそうです。
-
32-Bit Varint-Encoded Record Length-Delimited
をほどこす必要がある- Protocol buffer のバイト列全体のLengthをバイト列先頭に付与すると良さそうです。
- protoファイル(
metrics.proto
)は以下リポジトリで公開されている- prometheus/client_model: Data model artifacts for Prometheus.
- ちなみにGo言語用の構造体定義(
metrics.pb.go
)も用意されているのでGo言語で実装する場合はgo getして入手するだけで良いです。 - 念の為お使いのバージョンのPushGatewayの依存モジュールのclient_modelと同じバージョンのものを使うと良いかと思います。
-
Content-Type
ヘッダの値はapplication/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited
とする
続いて実装例を紹介します。
実装例
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/matttproud/golang_protobuf_extensions/pbutil"
dto "github.com/prometheus/client_model/go"
)
func main() {
// metrics
m := &dto.MetricFamily{
Name: proto.String("some_metric"),
Help: proto.String("Just an example."),
Type: dto.MetricType_COUNTER.Enum(),
Metric: []*dto.Metric{
{
Label: []*dto.LabelPair{
{
Name: proto.String("label1"),
Value: proto.String("val1"),
},
{
Name: proto.String("label2"),
Value: proto.String("val2"),
},
},
Counter: &dto.Counter{
Value: proto.Float64(123.456),
},
},
},
}
fmt.Println("metric:", m)
// byte列を見てみよう
dump(m)
// push
push(m)
}
func dump(m proto.Message) {
// byte列に変換
marshaled, _ := proto.Marshal(m)
fmt.Println("metric marshaled: ", marshaled)
// 逆変換の例
unmarshaled := &dto.MetricFamily{}
_ = proto.Unmarshal(marshaled, unmarshaled)
fmt.Println("metric unmarshaled: ", unmarshaled)
}
func push(m proto.Message) {
// lenghをデリミタとしてbyte列の先頭に付与する
buf := &bytes.Buffer{}
_, err := pbutil.WriteDelimited(buf, m)
fmt.Println("metric delimited: ", buf.Bytes())
// post
err = post(buf.Bytes())
if err != nil {
fmt.Println("err: ", err)
}
}
func post(in []byte) error {
endpoint := "http://localhost:9091/metrics/job/some_job/instance/some_instance"
contentType := "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"
res, err := http.Post(endpoint, contentType, bytes.NewBuffer(in))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode >= 400 {
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return errors.New(res.Status)
}
return fmt.Errorf("%s: %s", res.Status, b)
}
return err
}
dump(m)
はメトリクスのpush処理には不要な処理で、バイト列の比較用のために書いたコードです。
実行結果
実行すると以下のようなログを出力します。
metric: name:"some_metric" help:"Just an example." type:COUNTER metric:<label:<name:"label1" value:"val1" > label:<name:"label2" value:"val2" > counter:<value:123.456 > >
metric marshaled: [10 11 115 111 109 101 95 109 101 116 114 105 99 18 16 74 117 115 116 32 97 110 32 101 120 97 109 112 108 101 46 24 0 34 43 10 14 10 6 108 97 98 101 108 49 18 4 118 97 108 49 10 14 10 6 108 97 98 101 108 50 18 4 118 97 108 50 26 9 9 119 190 159 26 47 221 94 64]
metric unmarshaled: name:"some_metric" help:"Just an example." type:COUNTER metric:<label:<name:"label1" value:"val1" > label:<name:"label2" value:"val2" > counter:<value:123.456 > >
metric delimited: [78 10 11 115 111 109 101 95 109 101 116 114 105 99 18 16 74 117 115 116 32 97 110 32 101 120 97 109 112 108 101 46 24 0 34 43 10 14 10 6 108 97 98 101 108 49 18 4 118 97 108 49 10 14 10 6 108 97 98 101 108 50 18 4 118 97 108 50 26 9 9 119 190 159 26 47 221 94 64]
metric marshaled
とmetric delimited
のバイト列を比較すると、delimitedの方は先頭にlength値を表す値が追加されていることが分かります。
このlength値がないとPushgateway側でunmarshalに失敗しレスポンスコード400が返ってきます。
なお、pushが成功するとPushgatewayのWebUIには以下のように表示されます。