From 4cffdc4c7f162716e0b3133a716e68a1df06aada Mon Sep 17 00:00:00 2001 From: timurIsaevIY <118661906+timurIsaevIY@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:22:52 +0300 Subject: [PATCH] metrics added (#48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * metrics added * updated * some fix * merged with configs * reg metric + последовательность * cadvisor added * cadvisor added * small fixes * grafana folder added * small fixes * Delete grafana directory --- .gitignore | 1 + build/attraction.Dockerfile | 2 +- build/gateway.Dockerfile | 2 +- cmd/attractions/main.go | 60 +- cmd/gateway/main.go | 44 +- cmd/trips/main.go | 46 +- cmd/users/main.go | 46 +- config/config.yaml | 2 +- .../Services_metric/Services_metric.json | 3104 +++++++++++++++++ docker-compose.yml | 72 +- go.mod | 11 +- go.sum | 13 + .../pkg/attractions/delivery/http/handler.go | 3 +- internal/pkg/config/config.go | 15 +- internal/pkg/metrics/interfaces.go | 18 + internal/pkg/metrics/middleware/prometheus.go | 191 + internal/pkg/metrics/prometheus.go | 58 - prometheus.yml | 37 + 18 files changed, 3632 insertions(+), 93 deletions(-) create mode 100644 dashboards/Services_metric/Services_metric.json create mode 100644 internal/pkg/metrics/interfaces.go create mode 100644 internal/pkg/metrics/middleware/prometheus.go delete mode 100644 internal/pkg/metrics/prometheus.go create mode 100644 prometheus.yml diff --git a/.gitignore b/.gitignore index 4e232e8..05a728d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ go.mod 2024-2-Zdes-budet-nazvanie-UykwHnIE.crt data/ .env +/grafana/ diff --git a/build/attraction.Dockerfile b/build/attraction.Dockerfile index a680b39..8b44992 100644 --- a/build/attraction.Dockerfile +++ b/build/attraction.Dockerfile @@ -8,5 +8,5 @@ FROM scratch AS runner WORKDIR /build COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/attractions/.bin . COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/attractions/config config/ -EXPOSE 50051 +EXPOSE 50051 8091 ENTRYPOINT ["./.bin"] diff --git a/build/gateway.Dockerfile b/build/gateway.Dockerfile index 34ff36a..07db91b 100644 --- a/build/gateway.Dockerfile +++ b/build/gateway.Dockerfile @@ -8,5 +8,5 @@ FROM scratch AS runner WORKDIR /build COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/gateway/.bin . COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/gateway/config config/ -EXPOSE 8080 +EXPOSE 8081 ENTRYPOINT ["./.bin"] diff --git a/cmd/attractions/main.go b/cmd/attractions/main.go index 6e7fead..0e11a02 100644 --- a/cmd/attractions/main.go +++ b/cmd/attractions/main.go @@ -16,6 +16,7 @@ import ( "2024_2_ThereWillBeName/internal/pkg/config" "2024_2_ThereWillBeName/internal/pkg/dblogger" "2024_2_ThereWillBeName/internal/pkg/logger" + metricsMw "2024_2_ThereWillBeName/internal/pkg/metrics/middleware" grpcReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc" genReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc/gen" reviewRepo "2024_2_ThereWillBeName/internal/pkg/reviews/repo" @@ -25,14 +26,19 @@ import ( searchRepo "2024_2_ThereWillBeName/internal/pkg/search/repo" searchUsecase "2024_2_ThereWillBeName/internal/pkg/search/usecase" "database/sql" + "errors" "fmt" + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus/promhttp" "log" "log/slog" "net" + "net/http" "os" "os/signal" "strconv" "syscall" + "time" _ "github.com/lib/pq" "google.golang.org/grpc" @@ -46,18 +52,33 @@ func main() { logger := setupLogger() db, err := sql.Open("postgres", fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", cfg.Database.DbHost, cfg.Database.DbPort, cfg.Database.DbUser, cfg.Database.DbPass, cfg.Database.DbName)) - if err != nil { - log.Fatalf("failed to connect to database: %v", err) - } - defer db.Close() err = db.Ping() if err != nil { log.Fatalf("failed to ping database: %v", err) } + metricMw := metricsMw.Create() + metricMw.RegisterMetrics() wrappedDB := dblogger.NewDB(db, logger) + r := mux.NewRouter() + r.Handle("/metrics", promhttp.Handler()) + httpSrv := &http.Server{ + Addr: fmt.Sprintf(":%d", cfg.Metric.AttractionPort), + Handler: r, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + go func() { + logger.Info(fmt.Sprintf("Starting HTTP server for metrics on :%d", cfg.Metric.AttractionPort)) + if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Error(fmt.Sprintf("HTTP server listen: %s\n", err)) + } + }() + reviewsRepo := reviewRepo.NewReviewRepository(wrappedDB) reviewUsecase := reviewUsecase.NewReviewsUsecase(reviewsRepo) placeRepo := placeRepo.NewPLaceRepository(wrappedDB) @@ -69,21 +90,18 @@ func main() { searchRepo := searchRepo.NewSearchRepository(wrappedDB) searchUsecase := searchUsecase.NewSearchUsecase(searchRepo) - grpcAttractionsServer := grpc.NewServer() - attractionsHandler := grpcAttractions.NewGrpcAttractionsHandler(placeUsecase) - genPlaces.RegisterAttractionsServer(grpcAttractionsServer, attractionsHandler) - citiesHandler := grpcCities.NewGrpcCitiesHandler(citiesUsecase) - genCities.RegisterCitiesServer(grpcAttractionsServer, citiesHandler) - reviewsHandler := grpcReviews.NewGrpcReviewsHandler(reviewUsecase) - genReviews.RegisterReviewsServer(grpcAttractionsServer, reviewsHandler) - categoriesHandler := grpcCategories.NewGrpcCategoriesHandler(categoriesUsecase) - genCategories.RegisterCategoriesServer(grpcAttractionsServer, categoriesHandler) - searchHandler := grpcSearch.NewGrpcSearchHandler(searchUsecase, logger) + + grpcAttractionsServer := grpc.NewServer(grpc.UnaryInterceptor(metricMw.ServerMetricsInterceptor)) + + genPlaces.RegisterAttractionsServer(grpcAttractionsServer, attractionsHandler) + genCities.RegisterCitiesServer(grpcAttractionsServer, citiesHandler) + genReviews.RegisterReviewsServer(grpcAttractionsServer, reviewsHandler) + genCategories.RegisterCategoriesServer(grpcAttractionsServer, categoriesHandler) genSearch.RegisterSearchServer(grpcAttractionsServer, searchHandler) reflection.Register(grpcAttractionsServer) @@ -101,6 +119,20 @@ func main() { stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + metricMw.TrackSystemMetrics("attractions") + case <-stop: + return + } + } + }() + <-stop log.Println("Shutting down gRPC server...") diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go index 0e9ef6d..abea70e 100644 --- a/cmd/gateway/main.go +++ b/cmd/gateway/main.go @@ -12,6 +12,7 @@ import ( httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" "2024_2_ThereWillBeName/internal/pkg/jwt" "2024_2_ThereWillBeName/internal/pkg/logger" + metricsMw "2024_2_ThereWillBeName/internal/pkg/metrics/middleware" "2024_2_ThereWillBeName/internal/pkg/middleware" genReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc/gen" httpReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/http" @@ -26,6 +27,7 @@ import ( "context" "errors" "fmt" + "github.com/prometheus/client_golang/prometheus/promhttp" "log" "log/slog" "net/http" @@ -33,6 +35,7 @@ import ( "os/signal" "strconv" "syscall" + "time" _ "github.com/lib/pq" @@ -46,6 +49,9 @@ func main() { logger := setupLogger() + metricMw := metricsMw.Create() + metricMw.RegisterMetrics() + jwtSecret := os.Getenv("JWT_SECRET") jwtHandler := jwt.NewJWT(jwtSecret, logger) @@ -86,6 +92,24 @@ func main() { corsMiddleware := middleware.NewCORSMiddleware(cfg.AllowedOrigins) r := mux.NewRouter().PathPrefix("/api/v1").Subrouter() r.Use(corsMiddleware.CorsMiddleware) + r.Use(metricMw.MetricsMiddleware) + r.Use(corsMiddleware.CorsMiddleware) + metricsRouter := mux.NewRouter().PathPrefix("/api/v1").Subrouter() + metricsRouter.Handle("/metrics", promhttp.Handler()) + httpSrvMw := &http.Server{ + Addr: fmt.Sprintf(":%d", cfg.Metric.GatewayPort), + Handler: metricsRouter, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + go func() { + logger.Info(fmt.Sprintf("Starting HTTP server for metrics on :%d", cfg.Metric.GatewayPort)) + if err := httpSrvMw.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Error(fmt.Sprintf("HTTP server listen: %s\n", err)) + } + }() r.Use(middleware.RequestLoggerMiddleware(logger)) @@ -105,8 +129,8 @@ func main() { places := r.PathPrefix("/places").Subrouter() places.HandleFunc("", placesHandler.GetPlacesHandler).Methods(http.MethodGet) places.HandleFunc("/search", placesHandler.SearchPlacesHandler).Methods(http.MethodGet) + places.HandleFunc("/category", placesHandler.GetPlacesByCategoryHandler).Methods(http.MethodGet) places.HandleFunc("/{id}", placesHandler.GetPlaceHandler).Methods(http.MethodGet) - places.HandleFunc("/category/{categoryName}", placesHandler.GetPlacesByCategoryHandler).Methods(http.MethodGet) categoriesHandler := httpCategories.NewCategoriesHandler(categoriesClient, logger) categories := r.PathPrefix("/categories").Subrouter() @@ -164,9 +188,9 @@ func main() { survey.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(surveyHandler.CreateSurveyResponse), logger)).Methods(http.MethodPost) survey.Handle("/users/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(surveyHandler.GetSurveyStatsByUserId), logger)).Methods(http.MethodGet) - httpSrv := &http.Server{Handler: r, Addr: fmt.Sprintf(":%d", 8080)} + httpSrv := &http.Server{Handler: r, Addr: fmt.Sprintf(":%d", cfg.HttpServer.Address)} go func() { - logger.Info("HTTP server listening on :%d", 8080) + logger.Info(fmt.Sprintf("HTTP server listening on :%d", cfg.HttpServer.Address)) if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Error("failed to serve HTTP: %d", err) os.Exit(1) @@ -175,6 +199,20 @@ func main() { stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + metricMw.TrackSystemMetrics("gateway") + case <-stop: + return + } + } + }() <-stop logger.Info("Shutting down HTTP server...") diff --git a/cmd/trips/main.go b/cmd/trips/main.go index b52d588..56cb029 100644 --- a/cmd/trips/main.go +++ b/cmd/trips/main.go @@ -4,21 +4,26 @@ import ( "2024_2_ThereWillBeName/internal/pkg/config" "2024_2_ThereWillBeName/internal/pkg/dblogger" "2024_2_ThereWillBeName/internal/pkg/logger" + metricsMw "2024_2_ThereWillBeName/internal/pkg/metrics/middleware" grpcTrips "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc" "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc/gen" tripRepo "2024_2_ThereWillBeName/internal/pkg/trips/repo" tripUsecase "2024_2_ThereWillBeName/internal/pkg/trips/usecase" "database/sql" + "errors" "fmt" + "github.com/gorilla/mux" + _ "github.com/lib/pq" + "github.com/prometheus/client_golang/prometheus/promhttp" "log" "log/slog" "net" + "net/http" "os" "os/signal" "strconv" "syscall" - - _ "github.com/lib/pq" + "time" "google.golang.org/grpc" "google.golang.org/grpc/reflection" @@ -29,6 +34,9 @@ func main() { logger := setupLogger() + metricMw := metricsMw.Create() + metricMw.RegisterMetrics() + db, err := sql.Open("postgres", fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", cfg.Database.DbHost, cfg.Database.DbPort, cfg.Database.DbUser, cfg.Database.DbPass, cfg.Database.DbName)) if err != nil { log.Fatalf("failed to connect to database: %v", err) @@ -42,10 +50,28 @@ func main() { wrappedDB := dblogger.NewDB(db, logger) + r := mux.NewRouter() + r.Handle("/metrics", promhttp.Handler()) + httpSrv := &http.Server{ + Addr: fmt.Sprintf(":%d", cfg.Metric.TripPort), + Handler: r, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + go func() { + + logger.Info(fmt.Sprintf("Starting HTTP server for metrics on :%d", cfg.Metric.TripPort)) + if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Error(fmt.Sprintf("HTTP server listen: %s\n", err)) + } + }() + tripRepo := tripRepo.NewTripRepository(wrappedDB) tripUsecase := tripUsecase.NewTripsUsecase(tripRepo) - grpcTripsServer := grpc.NewServer() + grpcTripsServer := grpc.NewServer(grpc.UnaryInterceptor(metricMw.ServerMetricsInterceptor)) tripsHandler := grpcTrips.NewGrpcTripHandler(tripUsecase, logger) gen.RegisterTripsServer(grpcTripsServer, tripsHandler) reflection.Register(grpcTripsServer) @@ -63,6 +89,20 @@ func main() { stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + metricMw.TrackSystemMetrics("trips") + case <-stop: + return + } + } + }() <-stop log.Println("Shutting down gRPC server...") diff --git a/cmd/users/main.go b/cmd/users/main.go index 8669a01..ffbcd9d 100644 --- a/cmd/users/main.go +++ b/cmd/users/main.go @@ -4,21 +4,26 @@ import ( "2024_2_ThereWillBeName/internal/pkg/config" "2024_2_ThereWillBeName/internal/pkg/dblogger" "2024_2_ThereWillBeName/internal/pkg/logger" + metricsMw "2024_2_ThereWillBeName/internal/pkg/metrics/middleware" grpcUsers "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc" "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc/gen" userRepo "2024_2_ThereWillBeName/internal/pkg/user/repo" userUsecase "2024_2_ThereWillBeName/internal/pkg/user/usecase" "database/sql" + "errors" "fmt" + "github.com/gorilla/mux" + _ "github.com/lib/pq" + "github.com/prometheus/client_golang/prometheus/promhttp" "log" "log/slog" "net" + "net/http" "os" "os/signal" "strconv" "syscall" - - _ "github.com/lib/pq" + "time" "google.golang.org/grpc" "google.golang.org/grpc/reflection" @@ -29,6 +34,9 @@ func main() { logger := setupLogger() + metricMw := metricsMw.Create() + metricMw.RegisterMetrics() + storagePath := os.Getenv("AVATAR_STORAGE_PATH") db, err := sql.Open("postgres", fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", cfg.Database.DbHost, cfg.Database.DbPort, cfg.Database.DbUser, cfg.Database.DbPass, cfg.Database.DbName)) @@ -44,10 +52,28 @@ func main() { wrappedDB := dblogger.NewDB(db, logger) + r := mux.NewRouter() + r.Handle("/metrics", promhttp.Handler()) + httpSrv := &http.Server{ + Addr: fmt.Sprintf(":%d", cfg.Metric.UserPort), + Handler: r, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + go func() { + + logger.Info(fmt.Sprintf("Starting HTTP server for metrics on :%d", cfg.Metric.UserPort)) + if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Error(fmt.Sprintf("HTTP server listen: %s\n", err)) + } + }() + userRepo := userRepo.NewAuthRepository(wrappedDB) userUsecase := userUsecase.NewUserUsecase(userRepo, storagePath) - grpcUsersServer := grpc.NewServer() + grpcUsersServer := grpc.NewServer(grpc.UnaryInterceptor(metricMw.ServerMetricsInterceptor)) usersHandler := grpcUsers.NewGrpcUserHandler(userUsecase, logger) gen.RegisterUserServiceServer(grpcUsersServer, usersHandler) reflection.Register(grpcUsersServer) @@ -65,6 +91,20 @@ func main() { stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + metricMw.TrackSystemMetrics("users") + case <-stop: + return + } + } + }() <-stop log.Println("Shutting down gRPC server...") diff --git a/config/config.yaml b/config/config.yaml index 7c767e2..dd12d4a 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,2 +1,2 @@ HttpServer: - Address: "8080" + Address: 8081 diff --git a/dashboards/Services_metric/Services_metric.json b/dashboards/Services_metric/Services_metric.json new file mode 100644 index 0000000..da63098 --- /dev/null +++ b/dashboards/Services_metric/Services_metric.json @@ -0,0 +1,3104 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 5, + "panels": [], + "title": "CPU/RAM/MEM", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "description": "Busy state of all CPU cores together", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 1, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 85 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 95 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 1 + }, + "id": 2, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ee54y4rbob7r4b" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "100 * (1 - avg(rate(node_cpu_seconds_total{mode=\"idle\"}[1m])))", + "hide": false, + "instant": true, + "intervalFactor": 1, + "legendFormat": "__auto", + "range": false, + "refId": "A", + "step": 240 + } + ], + "title": "CPU Busy", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "description": "System load over all CPU cores together", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 1, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 85 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 95 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 1 + }, + "id": 3, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "100 * (1 - (node_memory_MemFree_bytes{job=\"node_exporter\",instance=\"node_exporter:9100\"} + node_memory_Buffers_bytes{job=\"node_exporter\",instance=\"node_exporter:9100\"} + node_memory_Cached_bytes{job=\"node_exporter\",instance=\"node_exporter:9100\"}) / node_memory_MemTotal_bytes{job=\"node_exporter\",instance=\"node_exporter:9100\"})", + "format": "time_series", + "hide": false, + "instant": true, + "intervalFactor": 1, + "range": false, + "refId": "A", + "step": 240 + } + ], + "title": "RAM used", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "description": "Non available RAM memory", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 1, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 80 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 6, + "y": 1 + }, + "id": 4, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "(sum(node_filesystem_size_bytes{job=\"node_exporter\"}) - sum(node_filesystem_free_bytes{job=\"node_exporter\"})) / sum(node_filesystem_size_bytes{job=\"node_exporter\"}) * 100", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Disk Used", + "type": "gauge" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 6, + "panels": [], + "title": "Attractions service", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 6 + }, + "id": 7, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto", + "text": {} + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "service_cpu_usage_percent{job=\"attractions\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "CPU", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 3, + "y": 6 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "titleSize": 20, + "valueSize": 15 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "expr": "go_memstats_alloc_bytes{job=\"attractions\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "interval": "", + "legendFormat": "занято", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "editorMode": "code", + "exemplar": false, + "expr": "go_memstats_sys_bytes{job=\"attractions\"}", + "hide": false, + "instant": false, + "legendFormat": "всего", + "range": true, + "refId": "B" + } + ], + "title": "MEM", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 7, + "y": 6 + }, + "id": 11, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "service_disk_usage_percent{service=\"attractions\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Disk Usage", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 10, + "y": 6 + }, + "id": 30, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "titleSize": 20, + "valueSize": 15 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "go_memstats_heap_inuse_bytes{job=\"attractions\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "interval": "", + "legendFormat": "dynamic mem", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "go_memstats_heap_sys_bytes{job=\"attractions\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "heap", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "goroutines", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 14, + "y": 6 + }, + "id": 29, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "go_goroutines{job=\"attractions\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Panel Title", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 7, + "x": 0, + "y": 10 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Hits by status code", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 7, + "y": 10 + }, + "id": 27, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum(rate(http_method_errors_total{job=\"attractions\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "errors", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total errors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 13, + "y": 10 + }, + "id": 28, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 16 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetPLaces hits", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 16 + }, + "id": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/attractions.Attractions/GetPlaces\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetPlaces timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 22 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places/:id\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetPLace by id hits", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 22 + }, + "id": 32, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/attractions.Attractions/GetPlace\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetPlace by id timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 28 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places/search\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "SearchPLaces", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 28 + }, + "id": 33, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/attractions.Attractions/SearchPlaces\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "SearchPlaces id timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 34 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places/category\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetPLaces by category", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 34 + }, + "id": 34, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/attractions.Attractions/SearchPlaces\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "SearchPlaces timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 40 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/categories\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetCategories", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 40 + }, + "id": 35, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/categories.Categories/GetCategories\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetCategories timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 46 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places/:id/reviews\", method=\"GET\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetReviews by place", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 46 + }, + "id": 36, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/reviews.Reviews/GetReviewsByPlaceID\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetReviews timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 52 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places/:id/reviews\", method=\"POST\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Create review", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 52 + }, + "id": 37, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/reviews.Reviews/CreateReview\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CreateReviews timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 58 + }, + "id": 23, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/places/:id/reviews/:id\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Delete review", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 58 + }, + "id": 38, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/reviews.Reviews/DeleteReview\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "DeleteReview timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 64 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/cities/:id\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Get city by id", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 64 + }, + "id": 39, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/cities.Cities/SearchCityByID\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GetCities timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 70 + }, + "id": 25, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/cities/search\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Search city", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 70 + }, + "id": 40, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/cities.Cities/SearchCitiesByName\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "SearchCities timings", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 0, + "y": 76 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(status_code) (rate(http_method_hits_total{path=\"/api/v1/search\"}[$__rate_interval]))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Global search", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "be51wi1hz2rr4a" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 76 + }, + "id": 41, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (http_method_duration_seconds_bucket{method=\"/search.Search/Search\", job=\"attractions\"}))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "ms", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "GlobalSearch timings", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Services metric", + "uid": "be51xyvjmnv9cc", + "version": 64, + "weekStart": "" +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 70ec345..d7e3cfc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,8 @@ services: restart: unless-stopped ports: - ${DB_PORT}:5432 + networks: + - my_network gateway: container_name: gateway @@ -28,9 +30,12 @@ services: - ./assets/avatars:/assets/avatars - ./assets/photos:/assets/photos ports: - - 8080:8080 + - "8081:8081" + - "8094:8094" depends_on: - tripdb + networks: + - my_network restart: unless-stopped attractions: @@ -47,10 +52,13 @@ services: - ./assets/photos:/assets/photos ports: + - "8091:8091" - "50051:50051" depends_on: - tripdb restart: unless-stopped + networks: + - my_network trips: container_name: trips build: @@ -66,9 +74,12 @@ services: - ./assets/photos:/assets/photos ports: - "50053:50053" + - "8092:8092" depends_on: - tripdb restart: unless-stopped + networks: + - my_network users: container_name: users build: @@ -84,9 +95,12 @@ services: - ./assets/photos:/assets/photos ports: - "50052:50052" + - "8093:8093" depends_on: - tripdb restart: unless-stopped + networks: + - my_network survey: container_name: survey build: @@ -104,4 +118,58 @@ services: - "50054:50054" depends_on: - tripdb - restart: unless-stopped + networks: + - my_network + prometheus: + image: prom/prometheus:latest + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + command: + - --config.file=/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + networks: + - my_network + node_exporter: + depends_on: + - prometheus + image: quay.io/prometheus/node-exporter:latest + container_name: node_exporter + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + command: + - --path.procfs=/host/proc + - --path.sysfs=/host/sys + - --collector.filesystem.ignored-mount-points + - ^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/) + hostname: node_exporter + ports: + - "9100:9100" + networks: + - my_network + grafana: + image: grafana/grafana-enterprise + volumes: + - ./grafana:/var/lib/grafana/ + - ./grafana/provisioning/:/etc/grafana/provisioning/ + ports: + - "8080:3000" + networks: + - my_network + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + container_name: cadvisor + ports: + - "8088:8080" + volumes: + - /:/rootfs:ro + - /var/run:/var/run:rw + - /sys:/sys:rw + - /var/lib/docker/:/var/lib/docker:rw + networks: + - my_network + +networks: + my_network: diff --git a/go.mod b/go.mod index 372c348..aa8ca29 100644 --- a/go.mod +++ b/go.mod @@ -14,8 +14,10 @@ require ( require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/fatih/color v1.18.0 - github.com/golang/protobuf v1.5.4 + github.com/google/uuid v1.6.0 + github.com/ilyakaznacheev/cleanenv v1.5.0 github.com/prometheus/client_golang v1.20.5 + github.com/shirou/gopsutil v3.21.11+incompatible google.golang.org/grpc v1.68.0 google.golang.org/protobuf v1.35.2 ) @@ -24,15 +26,18 @@ require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/ilyakaznacheev/cleanenv v1.5.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/joho/godotenv v1.5.1 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect diff --git a/go.sum b/go.sum index babf615..7f7a7fe 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -48,6 +50,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -75,12 +79,20 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= @@ -99,6 +111,7 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/pkg/attractions/delivery/http/handler.go b/internal/pkg/attractions/delivery/http/handler.go index 5e878a8..83f0249 100644 --- a/internal/pkg/attractions/delivery/http/handler.go +++ b/internal/pkg/attractions/delivery/http/handler.go @@ -313,7 +313,8 @@ func (h *PlacesHandler) SearchPlacesHandler(w http.ResponseWriter, r *http.Reque } func (h *PlacesHandler) GetPlacesByCategoryHandler(w http.ResponseWriter, r *http.Request) { - categoryName := mux.Vars(r)["categoryName"] + categoryName := r.URL.Query().Get("category") + categoryName = template.HTMLEscapeString(categoryName) logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) h.logger.DebugContext(logCtx, "Handling request for searching attractions by category", slog.String("categoryName", categoryName)) diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 241fb37..22b9953 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -11,8 +11,9 @@ type Config struct { ConfigPath string `env:"CONFIG_PATH" env-default:"config/config.yaml"` Database Database Grpc Grpc - HttpServer HttpServer - AllowedOrigins []string `env:"ALLOWED_ORIGINS" env-default:"*"` + HttpServer HttpServer `yaml:"HttpServer"` + AllowedOrigins []string `env:"ALLOWED_ORIGINS" env-default:"*"` + Metric Metric } type Database struct { @@ -37,12 +38,20 @@ type Grpc struct { } type HttpServer struct { - Address string `yaml:"address" yaml-default:"8080"` + Address int `yaml:"Address" yaml-default:"8081"` IdleTimeout time.Duration `yaml:"idle_timeout" yaml-default:"60s"` ReadTimeout time.Duration `yaml:"read_timeout" yaml-default:"10s"` WriteTimeout time.Duration `yaml:"write_timeout" yaml-default:"10s"` } +type Metric struct { + UserPort int `env:"USER_METRIC_PORT" env-default:"8093"` + AttractionPort int `env:"ATTRACTIONS_METRIC_PORT" env-default:"8091"` + TripPort int `env:"TRIPS_METRIC_PORT" env-default:"8092"` + SurveyPort int `env:"SURVEYS_METRIC_PORT" env-default:"8095"` + GatewayPort int `env:"GATEWAY_METRIC_PORT" env-default:"8094"` +} + func Load() *Config { var cfg Config diff --git a/internal/pkg/metrics/interfaces.go b/internal/pkg/metrics/interfaces.go new file mode 100644 index 0000000..5756673 --- /dev/null +++ b/internal/pkg/metrics/interfaces.go @@ -0,0 +1,18 @@ +package metrics + +import ( + "context" + "google.golang.org/grpc" + "net/http" + "time" +) + +type MetricsHTTP interface { + IncreaseHits(method, path, statusCode string) + IncreaseErr(method, path, service string) + AddDurationToHistogram(method, service string, duration time.Duration) + TrackSystemMetrics(serviceName string) + ServerMetricsInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) + RegisterMetrics() + MetricsMiddleware(next http.Handler) http.Handler +} diff --git a/internal/pkg/metrics/middleware/prometheus.go b/internal/pkg/metrics/middleware/prometheus.go new file mode 100644 index 0000000..4e58639 --- /dev/null +++ b/internal/pkg/metrics/middleware/prometheus.go @@ -0,0 +1,191 @@ +package metrics + +import ( + "2024_2_ThereWillBeName/internal/pkg/metrics" + "context" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/disk" + "github.com/shirou/gopsutil/mem" + "log" + "net/http" + "os" + "regexp" + "strconv" + "strings" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "google.golang.org/grpc" +) + +type SystemMetrics struct { + cpuUsage *prometheus.GaugeVec + memoryUsage *prometheus.GaugeVec + diskUsage *prometheus.GaugeVec +} + +type GrpcMiddleware struct { + hits *prometheus.CounterVec + errors *prometheus.CounterVec + durations *prometheus.HistogramVec + statusCodes *prometheus.CounterVec // Добавляем метрику для статусов кодов + systemMetric *SystemMetrics + mu sync.Mutex +} + +func Create() metrics.MetricsHTTP { + middleware := &GrpcMiddleware{ + hits: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "http_method_hits_total", + Help: "Total number of http method calls across all services", + }, []string{"method", "path", "status_code"}), + + errors: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "http_method_errors_total", + Help: "Total number of http method errors across all services", + }, []string{"method", "path", "service"}), + + durations: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "http_method_duration_seconds", + Help: "Histogram of http method call durations across services", + Buckets: prometheus.DefBuckets, + }, []string{"method", "service"}), + + systemMetric: &SystemMetrics{ + cpuUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "service_cpu_usage_percent", + Help: "CPU usage percentage per service", + }, []string{"service", "hostname"}), + + memoryUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "service_memory_usage_bytes", + Help: "Memory usage in bytes per service", + }, []string{"service", "hostname"}), + + diskUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "service_disk_usage_percent", + Help: "Disk usage percentage per service", + }, []string{"service", "hostname"}), + }, + } + + return middleware +} + +func (m *GrpcMiddleware) RegisterMetrics() { + prometheus.MustRegister(m.hits) + prometheus.MustRegister(m.errors) + prometheus.MustRegister(m.durations) + prometheus.MustRegister(m.systemMetric.cpuUsage) + prometheus.MustRegister(m.systemMetric.memoryUsage) + prometheus.MustRegister(m.systemMetric.diskUsage) +} + +func (m *GrpcMiddleware) IncreaseHits(method, path, statusCode string) { + m.hits.WithLabelValues(method, path, statusCode).Inc() +} + +func (m *GrpcMiddleware) IncreaseErr(method, path, service string) { + m.errors.WithLabelValues(method, path, service).Inc() +} + +func (m *GrpcMiddleware) AddDurationToHistogram(method, service string, duration time.Duration) { + m.durations.WithLabelValues(method, service).Observe(duration.Seconds()) +} + +func (m *GrpcMiddleware) TrackSystemMetrics(serviceName string) { + m.mu.Lock() + defer m.mu.Unlock() + + hostname, _ := os.Hostname() + + // CPU Usage + cpuPercent, err := cpu.Percent(time.Second, false) + if err == nil && len(cpuPercent) > 0 { + m.systemMetric.cpuUsage.WithLabelValues(serviceName, hostname).Set(cpuPercent[0]) + } + + // Memory Usage + vmStat, err := mem.VirtualMemory() + if err == nil { + m.systemMetric.memoryUsage.WithLabelValues(serviceName, hostname).Set(float64(vmStat.Used)) + } + + // Disk Usage + diskStat, err := disk.Usage("/") + if err == nil { + m.systemMetric.diskUsage.WithLabelValues(serviceName, hostname).Set(diskStat.UsedPercent) + } +} + +func (m *GrpcMiddleware) ServerMetricsInterceptor( + ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler, +) (interface{}, error) { + start := time.Now() + serviceName := extractServiceName(info.FullMethod) + path := extractPath(info.FullMethod) + + h, err := handler(ctx, req) + log.Println(err) + duration := time.Since(start) + + if err != nil { + m.IncreaseErr(info.FullMethod, path, serviceName) + } + m.AddDurationToHistogram(info.FullMethod, serviceName, duration) + + return h, err +} + +func (m *GrpcMiddleware) MetricsMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rw := NewResponseWriter(w) + next.ServeHTTP(rw, r) + normalizedPath := normalizePath(r.URL.Path) + + m.IncreaseHits(r.Method, normalizedPath, strconv.Itoa(rw.statusCode)) + }) +} + +type responseWriter struct { + http.ResponseWriter + statusCode int +} + +func NewResponseWriter(w http.ResponseWriter) *responseWriter { + return &responseWriter{w, http.StatusOK} +} + +func (rw *responseWriter) WriteHeader(code int) { + rw.statusCode = code + rw.ResponseWriter.WriteHeader(code) +} + +func extractServiceName(fullMethod string) string { + parts := strings.Split(fullMethod, "/") + if len(parts) >= 2 { + if len(parts[1]) >= 2 { + return strings.Split(parts[1], ".")[0] + } + } + return "unknown" +} + +func extractPath(fullMethod string) string { + parts := strings.Split(fullMethod, "/") + if len(parts) >= 3 { + log.Println(fullMethod) + log.Println(parts[2]) + return parts[2] + } + return "unknown" +} + +func normalizePath(path string) string { + re := regexp.MustCompile(`/\d+`) + return re.ReplaceAllString(path, "/:id") +} diff --git a/internal/pkg/metrics/prometheus.go b/internal/pkg/metrics/prometheus.go deleted file mode 100644 index 162a404..0000000 --- a/internal/pkg/metrics/prometheus.go +++ /dev/null @@ -1,58 +0,0 @@ -package metrics - -import ( - "time" - - "github.com/prometheus/client_golang/prometheus" -) - -type PrometheusMetrics struct { - hits *prometheus.CounterVec - errors *prometheus.CounterVec - durations *prometheus.HistogramVec -} - -func NewPrometheusMetrics() *PrometheusMetrics { - hits := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "grpc_method_hits_total", - Help: "Total number of gRPC method calls.", - }, - []string{"method"}, - ) - errors := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "grpc_method_errors_total", - Help: "Total number of gRPC method errors.", - }, - []string{"method"}, - ) - durations := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "grpc_method_duration_seconds", - Help: "Histogram of gRPC method call durations.", - Buckets: prometheus.DefBuckets, - }, - []string{"method"}, - ) - - prometheus.MustRegister(hits, errors, durations) - - return &PrometheusMetrics{ - hits: hits, - errors: errors, - durations: durations, - } -} - -func (m *PrometheusMetrics) IncreaseHits(method string) { - m.hits.WithLabelValues(method).Inc() -} - -func (m *PrometheusMetrics) IncreaseErr(method string) { - m.errors.WithLabelValues(method).Inc() -} - -func (m *PrometheusMetrics) AddDurationToHistogram(method string, duration time.Duration) { - m.durations.WithLabelValues(method).Observe(duration.Seconds()) -} diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..3db66a3 --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,37 @@ +global: + scrape_interval: 5s + evaluation_interval: 5s +scrape_configs: + - job_name: "prometheus" + static_configs: + - targets: [ 'localhost:9090' ] + + - job_name: "node_exporter" + metrics_path: /metrics + static_configs: + - targets: [ 'node_exporter:9100' ] + + - job_name: "gateway" + metrics_path: /api/v1/metrics + static_configs: + - targets: [ 'gateway:8094' ] + + - job_name: "attractions" + metrics_path: /metrics + static_configs: + - targets: [ 'attractions:8091' ] + + - job_name: "trips" + metrics_path: /metrics + static_configs: + - targets: [ 'trips:8092' ] + + - job_name: "users" + metrics_path: /metrics + static_configs: + - targets: [ 'users:8093' ] + + - job_name: "cadvisor" + metrics_path: /metrics + static_configs: + - targets: [ 'cadvisor:8080' ]