Prometheus를 사용하여 Golang 애플리케이션에서 지표 수집
위키백과에 따르면
Prometheus is a free software application used for event monitoring and alerting. It records real-time metrics in a time series database built using a HTTP pull model, with flexible queries and real-time alerting.
우리는 Grafana를 다음과 같이 설명할 수 있습니다.
Grafana is a multi-platform open source analytics and interactive visualization software available since 2014. It provides charts, graphs, and alerts for the web when connected to supported data sources.
요컨대 프로메테우스가 데이터를 수집한 것은 그라파나 덕분에 우리는 정보의 시각화를 편리하게 하기 위해 아름다운 도형과 계기판을 만들 수 있었다.
용례 레이어 생성하기
이 기능을 이용하기 위해서는 프로메테우스가 수집하고 처리할 데이터를 제공할 수 있도록 코드를 수정해야 한다.우리가 깨끗한 구조를 사용할 때, 우리가 해야 할 첫 번째 단계는 예시층으로 새로운 가방을 만드는 것이다.저장소 기반: https://github.com/eminetto/clean-architecture-go, 다음과 같은 내용으로
pkg/metric/interface file .go
만들기 시작합니다.package metric
import "time"
//CLI define a CLI app
type CLI struct {
Name string
StartedAt time.Time
FinishedAt time.Time
Duration float64
}
// NewCLI create a new CLI app
func NewCLI(name string) *CLI {
return &CLI{
Name: name,
}
}
//Started start monitoring the app
func (c *CLI) Started() {
c.StartedAt = time.Now()
}
// Finished app finished
func (c *CLI) Finished() {
c.FinishedAt = time.Now()
c.Duration = time.Since(c.StartedAt).Seconds()
}
//HTTP application
type HTTP struct {
Handler string
Method string
StatusCode string
StartedAt time.Time
FinishedAt time.Time
Duration float64
}
//NewHTTP create a new HTTP app
func NewHTTP(handler string, method string) *HTTP {
return &HTTP{
Handler: handler,
Method: method,
}
}
//Started start monitoring the app
func (h *HTTP) Started() {
h.StartedAt = time.Now()
}
// Finished app finished
func (h *HTTP) Finished() {
h.FinishedAt = time.Now()
h.Duration = time.Since(h.StartedAt).Seconds()
}
//UseCase definition
type UseCase interface {
SaveCLI(c *CLI) error
SaveHTTP(h *HTTP)
}
이 파일에서 우리는 명령행 응용 프로그램과 API에서 수집하고자 하는 두 가지 중요한 구조를 정의했다.우리는 또한 CLI
인터페이스 (잠시 후 실현될 것) 와 구조를 초기화하는 함수 HTTP
와 UseCase
를 정의했다.앞에서 언급한 바와 같이, 이러한 깨끗한 구조 전략은 우리가 도량 집합의 세부 사항을 응용 프로그램의 다른 층으로 추상화할 수 있도록 한다.만약 우리가 언제든지 프로메테우스의 도량 집합 해결 방안을 다른 해결 방안으로 변경한다면, 우리는 아무런 문제가 없을 것이다. 왜냐하면 다른 층은 실현 NewCLI
인터페이스를 받기를 원하기 때문이다.이제 우리는 인터페이스를 실현하여 파일을 만들 것이다
NewHTTP
:package metric
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/push"
"github.com/eminetto/clean-architecture-go/config"
)
//Service implements UseCase interface
type Service struct {
pHistogram *prometheus.HistogramVec
httpRequestHistogram *prometheus.HistogramVec
}
//NewPrometheusService create a new prometheus service
func NewPrometheusService() (*Service, error) {
cli := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "pushgateway",
Name: "cmd_duration_seconds",
Help: "CLI application execution in seconds",
Buckets: prometheus.DefBuckets,
}, []string{"name"})
http := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "http",
Name: "request_duration_seconds",
Help: "The latency of the HTTP requests.",
Buckets: prometheus.DefBuckets,
}, []string{"handler", "method", "code"})
s := &Service{
pHistogram: cli,
httpRequestHistogram: http,
}
err := prometheus.Register(s.pHistogram)
if err != nil && err.Error() != "duplicate metrics collector registration attempted" {
return nil, err
}
err = prometheus.Register(s.httpRequestHistogram)
if err != nil && err.Error() != "duplicate metrics collector registration attempted" {
return nil, err
}
return s, nil
}
//SaveCLI send metrics to server
func (s *Service) SaveCLI(c *CLI) error {
gatewayURL := config.PROMETHEUS_PUSHGATEWAY
s.pHistogram.WithLabelValues(c.Name).Observe(c.Duration)
return push.New(gatewayURL, "cmd_job").Collector(s.pHistogram).Push()
}
//SaveHTTP send metrics to server
func (s *Service) SaveHTTP(h *HTTP) {
s.httpRequestHistogram.WithLabelValues(h.Handler, h.Method, h.StatusCode).Observe(h.Duration)
}
이 파일에서, 우리는 UseCase
함수를 사용하여 pkg/metric/prometheus.go
인터페이스를 실현했고, 우리는 다음 단계에서 그것을 사용할 것이다.각 함수에 대한 자세한 내용은 공식 Go 클라이언트 documentation 에서 확인할 수 있습니다.이 파일의 또 다른 중요한 점은 함수
NewPrometheusService
내부의 행 UseCase
이다.프로메테우스는 도량 수집기이기 때문에 수집이 끝날 때까지 데이터를 메모리에 저장하는 방법이 필요하다.우리가 지속적으로 실행되는 응용 프로그램에 대해 이야기할 때, 예를 들어 API와 같은 데이터는 메모리에 남아 있다.그러나 실행 후 종료된 CLI 애플리케이션의 경우 이러한 데이터를 어디에 저장해야 합니다.프로메테우스 프로젝트에는 Push Gateway의 해결 방안이 있습니다.이것은 프로메테우스가 데이터를 수집할 때까지 어떤 서버에서 실행해야 하는 작은 응용 프로그램입니다.응용 프로그램의 gatewayURL: = config.PROMETHEUS_PUSHGATEWAY
를 구성할 때 PushGateway에 대해 다시 한 번 논의하겠습니다.이 구성에는 PushGateway 주소가 있습니다.나는 파일에 이 변수를 포함했다. SaveCLI
, docker-compose.yml
, config / config_testing.go
, config / config_staging.go
.post to understand 파일이 존재하는 이유를 확인하십시오.예를 들어, 파일config / config_prod.go
에는 다음이 포함됩니다.// +build dev
package config
const (
MONGODB_HOST = "mongodb://127.0.0.1:27017"
MONGODB_DATABASE = "bookmark"
MONGODB_CONNECTION_POOL = 5
API_PORT = 8080
PROMETHEUS_PUSHGATEWAY = "http://localhost:9091/"
)
CLI 애플리케이션에서 지표 수집
이제 CLI 애플리케이션에서 지표를 수집하기 위해 이 서비스를 시작합니다.이것은
config / config_dev.go
파일의 새 코드입니다.package main
import (
"errors"
"fmt"
"github.com/eminetto/clean-architecture-go/pkg/metric"
"log"
"os"
"github.com/eminetto/clean-architecture-go/config"
"github.com/eminetto/clean-architecture-go/pkg/bookmark"
"github.com/eminetto/clean-architecture-go/pkg/entity"
"github.com/juju/mgosession"
mgo "gopkg.in/mgo.v2"
)
func handleParams() (string, error) {
if len(os.Args) < 2 {
return "", errors.New("Invalid query")
}
return os.Args[1], nil
}
func main() {
metricService, err := metric.NewPrometheusService()
if err != nil {
log.Fatal(err.Error())
}
appMetric := metric.NewCLI("search")
appMetric.Started()
query, err := handleParams()
if err != nil {
log.Fatal(err.Error())
}
session, err := mgo.Dial(config.MONGODB_HOST)
if err != nil {
log.Fatal(err.Error())
}
defer session.Close()
mPool := mgosession.NewPool(nil, session, config.MONGODB_CONNECTION_POOL)
defer mPool.Close()
bookmarkRepo := bookmark.NewMongoRepository(mPool, config.MONGODB_DATABASE)
bookmarkService := bookmark.NewService(bookmarkRepo)
all, err := bookmarkService.Search(query)
if err != nil {
log.Fatal(err)
}
if len(all) == 0 {
log.Fatal(entity.ErrNotFound.Error())
}
for _, j := range all {
fmt.Printf("%s %s %v \n", j.Name, j.Link, j.Tags)
}
appMetric.Finished()
err = metricService.SaveCLI(appMetric)
if err != nil {
log.Fatal(err)
}
}
config / config_dev.go
함수의 시작 부분에서 우리는 프로메테우스의 실현을 이용하여 서비스를 만들었다.metricService, err := metric.NewPrometheusService()
if err != nil {
log.Fatal(err.Error())
}
그런 다음 데이터 수집을 시작하여 어플리케이션 이름을 지정합니다. Grafana의 시각화에서 이 어플리케이션을 사용합니다.appMetric := metric.NewCLI("search")
appMetric.Started()
파일 끝에 수집 및 전송을 완료했습니다cmd/main.go
.appMetric.Finished()
err = metricService.SaveCLI(appMetric)
if err != nil {
log.Fatal(err)
}
API 지표 수집
이제 API 지표를 수집합니다.우리가 모든 단점에서 지표를 수집하고 싶을 때, 우리는 middlewares의 개념을 이용할 수 있다.따라서 파일main
을 만듭니다.
package middleware
import (
"net/http"
"strconv"
"github.com/eminetto/clean-architecture-go/pkg/metric"
"github.com/codegangsta/negroni"
)
//Metrics to prometheus
func Metrics(mService metric.UseCase) negroni.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
appMetric := metric.NewHTTP(r.URL.Path, r.Method)
appMetric.Started()
next(w, r)
res := w.(negroni.ResponseWriter)
appMetric.Finished()
appMetric.StatusCode = strconv.Itoa(res.Status())
mService.SaveHTTP(appMetric)
}
}
이 중간부품은 PushGateway
인터페이스의 실현을 수신하고 요청 상세 정보(실행 시간과 상태 코드)를 수집하기 시작하며 향후 수집을 위해 데이터를 저장합니다.우리가 API를 이야기할 때, 그것은 프로메테우스가 그것을 수집하고 처리할 때까지 메모리에 저장된다.
새로운 중간부품을 활용하기 위해 API의 pkg/middleware/metrics.go
를 변경하고 지표를 수집하는 데 사용할 Prometheus 단점을 만들어야 합니다.파일metric. UseCase
을 다음과 같이 변경합니다.
package main
import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/codegangsta/negroni"
"github.com/eminetto/clean-architecture-go/api/handler"
"github.com/eminetto/clean-architecture-go/config"
"github.com/eminetto/clean-architecture-go/pkg/bookmark"
"github.com/eminetto/clean-architecture-go/pkg/middleware"
"github.com/eminetto/clean-architecture-go/pkg/metric"
"github.com/gorilla/context"
"github.com/gorilla/mux"
"github.com/juju/mgosession"
mgo "gopkg.in/mgo.v2"
)
func main() {
session, err := mgo.Dial(config.MONGODB_HOST)
if err != nil {
log.Fatal(err.Error())
}
defer session.Close()
r := mux.NewRouter()
mPool := mgosession.NewPool(nil, session, config.MONGODB_CONNECTION_POOL)
defer mPool.Close()
bookmarkRepo := bookmark.NewMongoRepository(mPool, config.MONGODB_DATABASE)
bookmarkService := bookmark.NewService(bookmarkRepo)
metricService, err := metric.NewPrometheusService()
if err != nil {
log.Fatal(err.Error())
}
//handlers
n := negroni.New(
negroni.HandlerFunc(middleware.Cors),
negroni.HandlerFunc(middleware.Metrics(metricService)),
negroni.NewLogger(),
)
//bookmark
handler.MakeBookmarkHandlers(r, *n, bookmarkService)
http.Handle("/", r)
http.Handle("/metrics", promhttp.Handler())
r.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
logger := log.New(os.Stderr, "logger: ", log.Lshortfile)
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
Addr: ":" + strconv.Itoa(config.API_PORT),
Handler: context.ClearHandler(http.DefaultServeMux),
ErrorLog: logger,
}
err = srv.ListenAndServe()
if err != nil {
log.Fatal(err.Error())
}
}
CLI에서 설명한 것처럼 가져오기 및 서비스 시작 외에도 첫 번째 중요한 변화는 다음과 같은 새로운 미들웨어를 실행 스택에 포함시키는 것입니다.
n := negroni.New(
negroni.HandlerFunc(middleware.Cors),
negroni.HandlerFunc(middleware.Metrics(metricService)),
negroni.NewLogger(),
)
두 번째 변화는 하나의 단점을 만드는 것이다. 프로메테우스는 이 단점을 이용하여 데이터를 수집할 것이다.
http.Handle("/metrics", promhttp.Handler())
프로메테우스가 사용할 데이터를 만들기 위해 응용 프로그램에서 필요한 모든 변경 사항입니다.우리는 지금 테스트를 촉진하기 위해 현지 환경을 세울 것이다.
Grafana 구성
이제 Grafana를 사용하여 프로메테우스가 수집한 데이터의 시각화를 만들 것입니다.
액세스 링크main.go
, 사용자 api/main.go
및 암호 http://localhost:3000/login
로 로그인하고 인터페이스 요구에 따라 새 암호를 생성합니다.
로그인하면 인터페이스의 옵션을 사용하여 새 admin
를 생성해야 합니다.admin
옵션을 선택하려면 다음 정보를 입력해야 합니다.
옵션data source
에서 표준 대시보드를 가져와야 합니다.
이제 첫 번째 대시보드를 만듭니다.
옵션 Prometheus
을 선택하면 데이터를 채울 수 있습니다.
검색 필드에서 다음을 사용합니다.
http\u 요청\u 지속 시간\u 초수 {job="bookmark"}>0Dashboards
필드에 표시할 정보를 입력했습니다.
{{handler}}-{{method}}-{{code}}
이런 방식을 통해 접근한 URL 외에 방법과 상태 코드가 무엇인지 볼 수 있다.
일반 옵션에서는 시각적 이름을 지정합니다.
경고를 만들지 않으므로 업데이트된 대시보드를 보려면 복귀 (페이지 상단에 화살표가 있는 버튼) 를 클릭합니다.
이제 CLI에 대한 정보가 포함된 새 패널을 추가합니다.
새 질의를 작성합니다.
질의에서 다음 값을 입력합니다.
pushgateway_cmd_duration_seconds_sum
전설로서 우리는 다음과 같이 사용한다.
{{name}}
일반 옵션에서 새 패널의 이름을 지정하고 대시보드로 돌아갈 수 있습니다.
응용 프로그램이 지표를 수집할 때, 대시보드의 데이터를 업데이트합니다.고급 질의가 있는 다른 패널을 추가할 수 있습니다.프로메테우스와 그라파나 문헌에는 더 높은 예가 있다.
결론
이 글에서 제 목표는 Go 응용 프로그램에 도량 기능을 추가하는 것이 얼마나 간단한지 보여주는 것입니다.또 하나는 우리가 깨끗한 체계 구조를 사용하고 있다는 것이다. 이것은 우리가 창설 Add query
의 새로운 실현을 통해 프로메테우스에서 다른 해결 방안으로 옮기고 몇 개의 설정 줄만 바꾸면 된다는 것이다.이러한 지표는 우리가 응용 프로그램의 행위를 더욱 잘 이해하고 의사결정과 개선을 추진하는 데 도움을 준다.나는 이 문장이 유용해서 더 많은 항목에도 이런 좋은 점이 있기를 바란다.
본고에서 제공한 모든 코드는 저장소에 있다https://github.com/eminetto/clean-architecture-go
Reference
이 문제에 관하여(Prometheus를 사용하여 Golang 애플리케이션에서 지표 수집), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/eminetto/using-prometheus-to-collect-metrics-from-golang-applications-35gc
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
package middleware
import (
"net/http"
"strconv"
"github.com/eminetto/clean-architecture-go/pkg/metric"
"github.com/codegangsta/negroni"
)
//Metrics to prometheus
func Metrics(mService metric.UseCase) negroni.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
appMetric := metric.NewHTTP(r.URL.Path, r.Method)
appMetric.Started()
next(w, r)
res := w.(negroni.ResponseWriter)
appMetric.Finished()
appMetric.StatusCode = strconv.Itoa(res.Status())
mService.SaveHTTP(appMetric)
}
}
package main
import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/codegangsta/negroni"
"github.com/eminetto/clean-architecture-go/api/handler"
"github.com/eminetto/clean-architecture-go/config"
"github.com/eminetto/clean-architecture-go/pkg/bookmark"
"github.com/eminetto/clean-architecture-go/pkg/middleware"
"github.com/eminetto/clean-architecture-go/pkg/metric"
"github.com/gorilla/context"
"github.com/gorilla/mux"
"github.com/juju/mgosession"
mgo "gopkg.in/mgo.v2"
)
func main() {
session, err := mgo.Dial(config.MONGODB_HOST)
if err != nil {
log.Fatal(err.Error())
}
defer session.Close()
r := mux.NewRouter()
mPool := mgosession.NewPool(nil, session, config.MONGODB_CONNECTION_POOL)
defer mPool.Close()
bookmarkRepo := bookmark.NewMongoRepository(mPool, config.MONGODB_DATABASE)
bookmarkService := bookmark.NewService(bookmarkRepo)
metricService, err := metric.NewPrometheusService()
if err != nil {
log.Fatal(err.Error())
}
//handlers
n := negroni.New(
negroni.HandlerFunc(middleware.Cors),
negroni.HandlerFunc(middleware.Metrics(metricService)),
negroni.NewLogger(),
)
//bookmark
handler.MakeBookmarkHandlers(r, *n, bookmarkService)
http.Handle("/", r)
http.Handle("/metrics", promhttp.Handler())
r.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
logger := log.New(os.Stderr, "logger: ", log.Lshortfile)
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
Addr: ":" + strconv.Itoa(config.API_PORT),
Handler: context.ClearHandler(http.DefaultServeMux),
ErrorLog: logger,
}
err = srv.ListenAndServe()
if err != nil {
log.Fatal(err.Error())
}
}
n := negroni.New(
negroni.HandlerFunc(middleware.Cors),
negroni.HandlerFunc(middleware.Metrics(metricService)),
negroni.NewLogger(),
)
http.Handle("/metrics", promhttp.Handler())
pushgateway_cmd_duration_seconds_sum
Reference
이 문제에 관하여(Prometheus를 사용하여 Golang 애플리케이션에서 지표 수집), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/eminetto/using-prometheus-to-collect-metrics-from-golang-applications-35gc텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)