Skip to content

Commit

Permalink
Merge 4fee54a into 0c086d2
Browse files Browse the repository at this point in the history
  • Loading branch information
BenedictHomuth authored Jul 26, 2024
2 parents 0c086d2 + 4fee54a commit ba0a2c7
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ server/src/coverage.txt
build
server/src/main
server/src/server
server/src/tmp

# misc
.DS_Store
Expand Down
3 changes: 3 additions & 0 deletions server/config_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,6 @@ disable-check-origin = true

# Specify the URL for the feedback webhook.
feedback-webhook-url = ""

# Enable metrics for the backend server
enable-metrics = true
18 changes: 18 additions & 0 deletions server/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,21 @@ services:
- POSTGRES_PASSWORD=supersecret
- POSTGRES_DB=scrumlr
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C

prometheus:
image: prom/prometheus:v2.52.0
ports:
- 9090:9090
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command: --config.file=/etc/prometheus/prometheus.yml

grafana:
image: grafana/grafana:10.4.2
ports:
- 3001:3000
environment:
- GF_SECURITY_ADMIN_USER=test
- GF_SECURITY_ADMIN_PASSWORD=test
volumes:
- ./grafana.yml:/etc/grafana/provisioning/datasources/datasources.yml
6 changes: 6 additions & 0 deletions server/grafana.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: 1
datasources:
- name: Main
type: prometheus
url: http://prometheus:9090
isDefault: true
15 changes: 15 additions & 0 deletions server/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.

scrape_configs:
- job_name: 'scrumlr-api'

# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
scrape_timeout: 5s
metrics_path: /metrics
scheme: http
follow_redirects: true

static_configs:
- targets: ['host.docker.internal:8080']
79 changes: 79 additions & 0 deletions server/src/api/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package api

import (
"context"
"net/http"
"strconv"
"strings"
"time"

"github.com/go-chi/chi/middleware"
"github.com/prometheus/client_golang/prometheus"
"scrumlr.io/server/logger"
)

var (
endpointLatency = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request latency per endpoint.",
Buckets: []float64{0.1, 0.2, 0.5, 1, 3, 5, 10},
},
[]string{"method", "route", "status"},
)
requestCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_request_counter",
Help: "Counter of HTTP requests",
}, []string{"method", "route", "status"},
)
)

func (s *Server) initMetrics() {
log := logger.FromContext(context.Background())
err := s.customMetrics.RegisterCounterVec(requestCounter)
if err != nil {
log.Errorw("unable to register counter for custom metrics", err)
}
err = s.customMetrics.RegisterHistogramVec(endpointLatency)
if err != nil {
log.Errorw("unable to register histogram for custom metrics", err)
}
}

func metricCounterMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
defer func() {
urlPath := generalizeURLPath(r.URL.Path)
requestCounter.WithLabelValues(r.Method, urlPath, strconv.Itoa(ww.Status())).Inc()
}()
next.ServeHTTP(ww, r)

})
}

func metricMeasureLatencyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
defer func() {
duration := time.Since(start).Seconds()
urlPath := generalizeURLPath(r.URL.Path)
endpointLatency.WithLabelValues(r.Method, urlPath, strconv.Itoa(ww.Status())).Observe(duration)
}()
next.ServeHTTP(ww, r)

})
}

// Replaces UUID-like segments with a wildcard placeholder
func generalizeURLPath(path string) string {
segments := strings.Split(path, "/")
for i, segment := range segments {
if len(segment) == 36 && strings.Contains(segment, "-") {
segments[i] = "*"
}
}
return strings.Join(segments, "/")
}
3 changes: 2 additions & 1 deletion server/src/api/notes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package api

import (
"fmt"
"net/http"

"github.com/go-chi/render"
"github.com/google/uuid"
"net/http"
"scrumlr.io/server/common"
"scrumlr.io/server/common/dto"
"scrumlr.io/server/identifiers"
Expand Down
24 changes: 22 additions & 2 deletions server/src/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/go-chi/render"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus/promhttp"

"scrumlr.io/server/auth"
"scrumlr.io/server/logger"
Expand All @@ -36,6 +37,8 @@ type Server struct {
feedback services.Feedback
boardReactions services.BoardReactions
boardTemplates services.BoardTemplates
customMetrics services.CustomMetrics


upgrader websocket.Upgrader

Expand All @@ -61,7 +64,7 @@ func New(
feedback services.Feedback,
boardReactions services.BoardReactions,
boardTemplates services.BoardTemplates,

customMetrics services.CustomMetrics,
verbose bool,
checkOrigin bool,
anonymousLoginDisabled bool,
Expand All @@ -71,6 +74,8 @@ func New(
r.Use(middleware.RequestID)
r.Use(logger.RequestIDMiddleware)
r.Use(render.SetContentType(render.ContentTypeJSON))
r.Use(metricCounterMiddleware)
r.Use(metricMeasureLatencyMiddleware)

if !checkOrigin {
r.Use(cors.Handler(cors.Options{
Expand Down Expand Up @@ -105,7 +110,7 @@ func New(
feedback: feedback,
boardReactions: boardReactions,
boardTemplates: boardTemplates,

customMetrics: customMetrics,
anonymousLoginDisabled: anonymousLoginDisabled,
}

Expand All @@ -115,6 +120,11 @@ func New(
WriteBufferSize: 1024,
}

// Registers needed metrics for prometheus
if s.customMetrics != nil {
s.initMetrics()
}

if checkOrigin {
s.upgrader.CheckOrigin = nil
} else {
Expand Down Expand Up @@ -148,6 +158,7 @@ func (s *Server) publicRoutes(r chi.Router) chi.Router {
r.Get("/callback", s.verifyAuthProviderCallback)
})
})
s.initCustomMetricResources(r)
})
}

Expand Down Expand Up @@ -332,3 +343,12 @@ func (s *Server) initBoardReactionResources(r chi.Router) {
r.Post("/", s.createBoardReaction)
})
}

func (s *Server) initCustomMetricResources(r chi.Router) {
if s.customMetrics != nil {
r.Handle("/metrics", promhttp.HandlerFor(
s.customMetrics.Registry(),
promhttp.HandlerOpts{Registry: s.customMetrics.Registry()},
))
}
}
6 changes: 6 additions & 0 deletions server/src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go 1.22
replace github.com/opencontainers/runc v1.1.0 => github.com/opencontainers/runc v1.1.2

require (
github.com/go-chi/chi v1.5.5
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1
github.com/go-chi/httprate v0.12.0
Expand All @@ -20,6 +21,7 @@ require (
github.com/nats-io/nats.go v1.36.0
github.com/ory/dockertest/v3 v3.10.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.19.1
github.com/stretchr/testify v1.9.0
github.com/uptrace/bun v1.1.17
github.com/uptrace/bun/dbfixture v1.1.17
Expand All @@ -39,6 +41,7 @@ require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/continuity v0.3.0 // indirect
Expand Down Expand Up @@ -83,6 +86,9 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.12 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.2 // indirect
Expand Down
12 changes: 12 additions & 0 deletions server/src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
Expand Down Expand Up @@ -52,6 +54,8 @@ github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4Nij
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
Expand Down Expand Up @@ -180,6 +184,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
Expand Down
17 changes: 16 additions & 1 deletion server/src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strings"

"scrumlr.io/server/auth"
"scrumlr.io/server/services"
"scrumlr.io/server/services/custom_metrics"
"scrumlr.io/server/services/health"

"scrumlr.io/server/api"
Expand Down Expand Up @@ -206,6 +208,13 @@ func main() {
Usage: "TOML `filepath` to be loaded ",
Required: false,
},
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "enable-metrics",
Aliases: []string{"m"},
EnvVars: []string{"ENABLE_SCRUMLR_METRICS"},
Usage: "enables metrics and its endpoint",
Required: false,
}),
},
}
app.Before = altsrc.InitInputSourceWithContext(app.Flags, altsrc.NewTomlSourceFromFlagFunc("config"))
Expand Down Expand Up @@ -296,6 +305,12 @@ func run(c *cli.Context) error {
RedirectUri: fmt.Sprintf("%s%s/login/apple/callback", strings.TrimSuffix(c.String("auth-callback-host"), "/"), strings.TrimSuffix(basePath, "/")),
}
}
var customMetricService services.CustomMetrics
if c.IsSet("enable-metrics") {
customMetricService = custom_metrics.NewCustomMetricService(true)
} else {
customMetricService = nil
}

dbConnection := database.New(db, c.Bool("verbose"))

Expand Down Expand Up @@ -329,7 +344,7 @@ func run(c *cli.Context) error {
feedbackService,
boardReactionService,
boardTemplateService,

customMetricService,
c.Bool("verbose"),
!c.Bool("disable-check-origin"),
c.Bool("disable-anonymous-login"),
Expand Down
34 changes: 34 additions & 0 deletions server/src/services/custom_metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package custom_metrics

import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"scrumlr.io/server/services"
)

type CustomMetricService struct {
registry prometheus.Registry
}

func NewCustomMetricService(enabled bool) services.CustomMetrics {
if enabled {
m := &CustomMetricService{}
m.registry = *prometheus.NewRegistry()
m.registry.Unregister(collectors.NewGoCollector()) // Unregister the default bloat of metrics
return m
}

return nil
}

func (cms *CustomMetricService) RegisterHistogramVec(histo *prometheus.HistogramVec) error {
return cms.registry.Register(histo)
}

func (cms *CustomMetricService) RegisterCounterVec(ctr *prometheus.CounterVec) error {
return cms.registry.Register(ctr)
}

func (cms *CustomMetricService) Registry() *prometheus.Registry {
return &cms.registry
}
7 changes: 7 additions & 0 deletions server/src/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"scrumlr.io/server/common/dto"
"scrumlr.io/server/common/filter"
)
Expand Down Expand Up @@ -114,3 +115,9 @@ type BoardTemplates interface {
UpdateColumnTemplate(ctx context.Context, body dto.ColumnTemplateUpdateRequest) (*dto.ColumnTemplate, error)
DeleteColumnTemplate(ctx context.Context, boar, column, user uuid.UUID) error
}

type CustomMetrics interface {
RegisterHistogramVec(hist *prometheus.HistogramVec) error
RegisterCounterVec(ctr *prometheus.CounterVec) error
Registry() *prometheus.Registry
}

0 comments on commit ba0a2c7

Please sign in to comment.