프로메테우스 Client Library : Instrumentation for Golang Application

프로메테우스 가이드북
이민석's avatar
Jun 09, 2024
프로메테우스 Client Library : Instrumentation for Golang Application

프로메테우스 가이드북은 A to Z Metnros (Udemy) — Prometheus | The Complete Hands-On for Monitoring & Alerting를 듣고 작성한 가이드북입니다.

가이드북의 전체 목차 및 인덱싱은 프로메테우스 가이드북 — 소개 페이지를 참고해주세요.

사전 지식

앞서 Metric Type, Metric Name Convention 그리고 Python Application 예제를 학습하였습니다. 이번에는 같은 내용을 Golang Application에 대해서 진행할 것입니다.

예제

  1. Golang Application

  2. Prometheus Client - add new COUNTER

  3. Prometheus Server

  4. Prometheus Client - add new GAUGE

  5. Prometheus Client - add new SUMMARY

Golang Application

다음의 코드를 counter.go로 작성해주세요.

package main

imoprt (
   "net/http"
   "fmt"
   "log"
   "github.com/gorilla/mux"
)

func main() {
  startMyApp()
}

func startMyApp() {

   router := mux.NewRouter()
   router.HandleFunc("/birthday/{name}", func(rw http.ResponseWriter, r *http.Request) {
         vars := mux.Vars(r)
         name := vars("name")
         greetings := fmt.Sprintf("Happy Birthday %S :)", name)
         rw.Write([]byte(greetings))
   }).Methods("GET")

   log.Println("Starting the application servers...")
   http.ListenAndServe(":8000", router)

}

위 서버를 실행하기 위해서 아래 명령어를 사용할 수 있습니다.

go run counter.go

Prometheus Client - add new COUNTER

작성한 Golang Application에 Prometheus Client 관련 코드를 추가하였습니다.

추가한 REQUEST_COUNT는 [GET] /birthday/{name}의 요청 숫자를 수집하는 기능을 담당합니다. 또한 매트릭을 수집하기 위한 promhttp.Handler()도 등록되어 있습니다.

import (
   # ...
   "github.com/prometheus/client_golang/prometheus",          # ⛳️
   "github.com/prometheus/client_golang/prometheus/promhttp", # ⛳️
   "github.com/prometheus/client_golang/prometheus/promauto", # ⛳️
)

var REQUEST_COUNT = promauto.NewCounter(prometheus.CounterOpts{
      Name: "go_app_requests_count",
      Help: "Total App HTTP Request Count"
   }) # ⛳️

func main() {} # ...

func startMyApp() {

   router := mux.NewRouter()
   router.HandleFunc("/birthday/{name}", func(rw http.ResponseWriter, r *http.Request) {

   # ...
   router.HandleFunc("/birthday/{name}", func(rw http.ResponseWriter, r *http.Request) {
         # ...
         REQUEST_COUNT.Inc() # ⛳️
   }).Method("GET")

   router.Path('/metrics').Handler(promhttp.Handler()) # ⛳️

   # ...
}

Python Application에서는 localhost:8000에 App Server가 localhost:8001 에 Metric Server가 실행되었습니다.

하지만 Golang Application에서는 localhost:8000에서 App/Metric Server가 실행됩니다. 다른 점은 localhost:8000/metrics 쪽으로 promhttp.Handler()가 등록이 된다는 점입니다.

Prometheus Server

Golang Application에 Prometheus Client를 등록하였으니, Promethes Server의 scrap_configs 에 새로운 대상으로 등록해야 합니다.

# ...
scrp_configs:
   # ...
   - job_name: "prom_go_app"
     static_configs:
     - targets: ["localhost:8000"]

Prometheus Client - add new GAUGE

다음과 같이 Gauge Metric을 수집하도록 구성된 Golang Application이 있습니다.

# ...

var REQUEST_INPROGRESS = promauto.NewGauge(promtheus.GuageOpts{
      Name: "go_app_requests_inprogress",
      Help: "Number of application requests in progress"
   })

func main() {} # ...

func startMyApp() {

   router := mux.NewRouter()
   router.HandleFunc("/birthday/{name}", func(rw http.ResponseWriter, r *http.Request) {

   # ...
   router.HandleFunc("/birthday/{name}", func(rw http.ResponseWriter, r *http.Request) {

         REQUEST_INPROGRESS.Inc()    # ⛳️

         # ...

         time.Sleep(S * time.Second) # ⛳️
         REQUEST_INPROGRESS.Dec()    # ⛳️

   }).Method("GET")

   router.Path('/metrics').Handler(promhttp.Handler()) # ⛳️

}

Prometheus Client - add new SUMMARY

다음과 같이 Summry Metric을 수집하도록 구성된 Golang Application이 있습니다.

# ...

var REQUEST_RESPOND_TIME = promauto.NewSummaryVec(prometheus.SummaryOpts{
      Name: "go_app_response_latency_seconds",
      Help: "Response latency in seconds"
   })

func routeMiddleware(next http.Handler) http.Handler {
   return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
      start_time := time.Now()
      route := mux.CurrentRoute(r)
      path, _ := route.GetPathTemplate()

      next.ServeHTTP(w, r)
      time_taken := time.Since(start_time)
      REQUEST_RESPOND_TIME.WithLabelValues(path).Observe(time_taken.Seconds()) # ⛳️
   } 
}

func main() {
   startMyApp()
}

func startMyApp() {
   router := mux.NewRouter()
   router.HandleFunc("/birthday/{name}", func (rw http.ResponseWriter, r *http.Request) {
      vars := mux.Vars(r)
      name := vars["name"]
      
      time.Sleep(5 * time.Second)
      rs.Wirte([]byte(greetings))
   }).Methods("GET")

   router.Use(routeMiddleware) # ⛳️

   log.Println("Starting the application server...")
   router.Path("/metrics").Handler(promhttp.Handler())
   http.ListenAndServe(":8000", router)
}
Share article

Unchaptered