0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Prometheus Pushgateway に プログラムからメトリクスをpushする [Protocol buffer編]

Last updated at Posted at 2021-02-21

概要

Pushgateway にプログラムからメトリクスをpushする方法を紹介します。

push可能なメトリクスのフォーマットはTextProtocol 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)は以下リポジトリで公開されている
  • 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 marshaledmetric delimitedのバイト列を比較すると、delimitedの方は先頭にlength値を表す値が追加されていることが分かります。
このlength値がないとPushgateway側でunmarshalに失敗しレスポンスコード400が返ってきます。

なお、pushが成功するとPushgatewayのWebUIには以下のように表示されます。

push結果

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?