Пишем метрики в Prometheus на языке Go
28 января 2019
В рамках поста Устанавливаем связку из Prometheus и Grafana мы познакомились с Prometheus и разобрались с его настройкой. Теперь давайте выясним, как отправить в него каких-нибудь метрик из нашего собственного приложения. Писать будем на языке Go, но я почти уверен, что для других языков существуют аналогичные библиотеки.
Объем демонстрационного кода составил всего лишь 80 строк, так что просто рассмотрим его целиком:
import (
"log"
"flag"
"math/rand"
"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.")
func main() {
flag.Parse()
usersRegistered := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "users_registered",
})
prometheus.MustRegister(usersRegistered)
usersOnline := prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "users_online",
})
prometheus.MustRegister(usersOnline)
requestProcessingTimeSummaryMs := prometheus.NewSummary(
prometheus.SummaryOpts{
Name: "request_processing_time_summary_ms",
Objectives: map[float64]float64{0.5:0.05, 0.9:0.01, 0.99:0.001},
})
prometheus.MustRegister(requestProcessingTimeSummaryMs)
requestProcessingTimeHistogramMs := prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "request_processing_time_histogram_ms",
Buckets: prometheus.LinearBuckets(0, 10, 20),
})
prometheus.MustRegister(requestProcessingTimeHistogramMs)
go func() {
for {
usersRegistered.Inc() // or: Add(5)
time.Sleep(1000 * time.Millisecond)
}
}()
go func() {
for {
for i := 0; i < 10000; i++ {
usersOnline.Set(float64(i)) // or: Inc(), Dec(), Add(5), Dec(5)
time.Sleep(10 * time.Millisecond)
}
}
}()
go func(){
src := rand.NewSource(time.Now().UnixNano())
rnd := rand.New(src)
for {
obs := float64(100 + rnd.Intn(30))
requestProcessingTimeSummaryMs.Observe(obs)
requestProcessingTimeHistogramMs.Observe(obs)
time.Sleep(10 * time.Millisecond)
}
}()
http.Handle("/metrics", promhttp.Handler())
log.Printf("Starting web server at %s\n", *addr)
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Printf("http.ListenAndServer: %v\n", err)
}
}
Библиотека автоматически экспортирует метрики рантайма языка Go, такие, как количество работающих горутин, время сборки мусора, и так далее. То есть, для получения этих метрик особо ничего делать не нужно.
Кроме того, мы можем создавать собственные метрики следующих типов:
- Counter, как несложно угадать по названию, представляет собой простой счетчик. Не самый полезный тип метрики, поскольку счетчик этот является неубывающим. То есть, подходит он для отображения только чего-то вроде суммарного числа учетных записей в системе, да и то лишь при условии, что учетные записи являются неудаляемыми.
- Gauge, здесь используется в значении «мера». Gauge похож на Counter, но в отличие от него может не только возрастать, но и убывать. Этот тип отлично подходит для отображения текущего значения чего-то — температуры, давления, числа пользователей онлайн, и так далее.
- Histogram представляет собой гистограмму. Этот тип метрики хранит число раз, которое измеряемая величина попала в заданный интервал значений (бакет). Гистограммы может быть трудновато использовать, если интервал допустимых значений величины заранее неизвестен. В Grafana по гистограмме можно примерно посчитать процентили, используя функцию
histogram_quantile
. - Summary честно считает заданные процентили. Идеально подходит для измерения времени ответа или чего-то такого. Минус Summary заключается в том, что его дорого считать. Поэтому часто обходятся гистограммами и примерными значениями процентилей.
- Наконец, существуют типы HistogramVec, SummaryVec и так далее. Они представляют собой словарь (map) из описанных выше типов. То есть, это как бы метрики со строковыми метками, или создаваемые на лету метрики. Отлично подходят в случаях, когда вам нужно измерить время ответа сервера в зависимости от запроса, или вроде того. Примеры использования этих типов метрик вы найдете на godoc.org.
Ну вот и все. Остается только дописать адрес нашего приложения в prometheus.yml, как ранее мы это делали для Node Exporter, сказать:
…, и метрики начнут собираться.
Метрики, к сожалению, не являются панацеей от всего. Допустим, у одного конкретного пользователя почему-то тормозит один конкретный запрос. А запрос этот на самом деле распадается на множество подзапросов к разным микросервисам. В такой ситуации метрики не помогут отследить, где и почему тормозит этот один запрос. Тем не менее, метрики довольно полезны, когда требуется оценить состояние системы в целом.
Дополнение: Вас также могут заинтересовать посты про агрегацию логов с Loki и распределенную трассировку при помощи Jaeger.
Метки: Go.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.