Help us understand the problem. What is going on with this article?

go言語でprometheusのラベル付きexporterのテンプレ

はじめに

この記事ではgo言語を使ってprometheus用のexporterのテンプレを紹介します.
最終的なコードはここ

参考

https://github.com/prometheus/client_golang/blob/master/examples/random/main.go

環境

OS: Ubuntu 18.04.3
Go: 1.10.4 linux/amd64

下準備

$ go get github.com/prometheus/client_golang/prometheus

コード全容

package main

import (
    "flag"
    "log"
    "net/http"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
)

const (
    namespace = "testExporter"
)

var (
    testMetric = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "testMetric",
            Help:      "This is testMetric.",
        },
        []string{"testFlag"},
    )
)

func init() {
    prometheus.MustRegister(testMetric)
}

func main() {
    flag.Parse()
    go func() {
        for {
            testMetric.WithLabelValues("HogeHoge").Set(99.0)
            time.Sleep(60 * time.Second)
        }
    }()
    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(*addr, nil))
}

コード解説

パッケージインポートまで

package main

import (
    "flag"
    "log"
    "net/http"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

必要なパッケージ群をインポートしておく.

リッスンするポートの設定

var (
    addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
)

flagを使ってコマンドライン引数からポートを指定している.

実行時に,

$ go run main.go -listen-address :8888

のように-listen-address :<ポート>とすることで任意のポートを指定できる.
何も指定しなければ,flag.Stringの第2引数に指定したポートをリッスンする.

namespace定義

const (
    namespace = "testExporter"
)

exporterの名前を定義する.
この名前はmetric名の一部になる.この例では,

#<namespace>_<metric_name(後述)>{フラグ(後述)} 値
testExporter_testMetric{testFlag="HogeHoge"} 99

のようになる.

metric定義

var (
        testMetric = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "testMetric",
            Help:      "This is testMetric.",
        },
        []string{"testFlag"},
    )

/*      <metric_name> = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Namespace: <どのnamecpaceに属するか>,
            Name:      "<metric_name>",
            Help:      "<metricの説明>",
        },
        []string{"<flag名>"},
    )
*/
)

ここで取得したいmetricの定義を行う.
今回はGauge型のmetricを定義している.
定義の仕方はコメントの通り.
metricにflagをつけない場合はprometheus.NewGaugeだが,
metricにflagをつける場合はprometheus.NewGaugeVecを用いる.

metricの登録

func init() {
    prometheus.MustRegister(testMetric)
    //prometheus.MustRegister(<metric_name>)
}

先程定義したmetricを登録する.
複数metricある場合はすべて登録する.
登録しないと:8080/metricsにアクセスしても表示されない.

main関数

func main() {
    //コマンドライン引数をパース
    flag.Parse()
    go func() {
        for {
            testMetric.WithLabelValues("HogeHoge").Set(99.0)
            time.Sleep(60 * time.Second)
        }
    }()
    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(*addr, nil))
}

先程定義したmetricに実際に値を入れる部分はこの行.

//<metric_name>.WithLabelValues("<セットしたいフラグ>").Set(値)
testMetric.WithLabelValues("HogeHoge").Set(99.0)

実際は目的の値をセットすることとなる.

最後の2行で指定されたポートでリッスンしている.

実際の出力

では,実際に実行してアクセスしてみる.

$ go run main.go &
$ curl localhost:8080/metrics
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
<一部省略>
# HELP testExporter_testMetric This is testMetric.
# TYPE testExporter_testMetric gauge
testExporter_testMetric{testFlag="HogeHoge"} 99

無事にさっき設定したラベルと値が出力されている.

ラベルの異なる値を追加してみる.

main関数の中の以下の部分を書き換える.

 go func() {
        for {
            testMetric.WithLabelValues("HogeHoge").Set(99.0)
            testMetric.WithLabelValues("FugaFuga").Set(33.4)

            time.Sleep(60 * time.Second)
        }
    }()

その後,再度実行してアクセスしてみる.

<前略>
# HELP testExporter_testMetric This is testMetric.
# TYPE testExporter_testMetric gauge
testExporter_testMetric{testFlag="FugaFuga"} 33.4
testExporter_testMetric{testFlag="HogeHoge"} 99

ちゃんとラベルの異なる2つのmetricが確認できる.

ラベルの数を増やしてみる.

metric定義の部分でラベルを増やす.

var (
    testMetric = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "testMetric",
            Help:      "This is testMetric.",
        },
        []string{"testFlag", "testFlag2"},
    )
)

main関数も変更

go func() {
        for {
            testMetric.WithLabelValues("HogeHoge", "AAAA").Set(99.0)
            testMetric.WithLabelValues("FugaFuga", "BBBB").Set(33.4)
            time.Sleep(60 * time.Second)
        }
    }()

アクセスしてみる.

# HELP testExporter_testMetric This is testMetric.
# TYPE testExporter_testMetric gauge
testExporter_testMetric{testFlag="FugaFuga",testFlag2="BBBB"} 33.4
testExporter_testMetric{testFlag="HogeHoge",testFlag2="AAAA"} 99

ラベルが追加できている.

最後に

ラベル付きでexporterを記述する記事があまりなかったのですが,やってみるとかなりかんたんなことが分かりますね.

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away