Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feature: add prometheus metrics (#990)
  • Loading branch information
goenning committed Aug 8, 2021
1 parent 0433b8d commit a37a915
Show file tree
Hide file tree
Showing 18 changed files with 282 additions and 33 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Expand Up @@ -187,10 +187,12 @@ jobs:
docker run \
--pull never \
-p 3000:3000 \
-p 4000:4000 \
--env DATABASE_URL=postgres://fider_ci:fider_ci_pw@postgres:5432/fider_ci?sslmode=disable \
--env JWT_SECRET=not_so_secret \
--env HOST_MODE=multi \
--env HOST_DOMAIN=dev.fider.io \
--env METRICS_ENABLED=true \
--env SSL_CERT=dev-fider-io.crt \
--env SSL_CERT_KEY=dev-fider-io.key \
--env EMAIL_NOREPLY=noreply@fider.io \
Expand Down
4 changes: 2 additions & 2 deletions .test.env
Expand Up @@ -5,6 +5,8 @@ DATABASE_URL=postgres://fider_test:fider_test_pw@localhost:5566/fider_test?sslmo
HOST_DOMAIN=test.fider.io
JWT_SECRET=SOME_RANDOM_TOKEN_JUST_FOR_TESTING

METRICS_ENABLED=true

BLOB_STORAGE=s3
BLOB_STORAGE_S3_ENDPOINT_URL=http://localhost:9000
BLOB_STORAGE_S3_REGION=us-east-1
Expand All @@ -14,8 +16,6 @@ BLOB_STORAGE_S3_BUCKET=test-bucket

BLOB_STORAGE_FS_PATH=./testdata/tmp/fs_test

CDN_HOST=

OAUTH_FACEBOOK_APPID=FB_CL_ID
OAUTH_FACEBOOK_SECRET=FB_CL_SECRET

Expand Down
1 change: 1 addition & 0 deletions app/cmd/routes.go
Expand Up @@ -16,6 +16,7 @@ func routes(r *web.Engine) *web.Engine {
r.Worker().Use(middlewares.WorkerSetup())

r.Use(middlewares.CatchPanic())
r.Use(middlewares.Instrumentation())

r.NotFound(func(c *web.Context) error {
next := func(c *web.Context) error {
Expand Down
11 changes: 10 additions & 1 deletion app/handlers/apiv1/post.go
Expand Up @@ -2,6 +2,7 @@ package apiv1

import (
"github.com/getfider/fider/app/actions"
"github.com/getfider/fider/app/metrics"
"github.com/getfider/fider/app/models/cmd"
"github.com/getfider/fider/app/models/entity"
"github.com/getfider/fider/app/models/enum"
Expand Down Expand Up @@ -57,6 +58,7 @@ func CreatePost() web.HandlerFunc {

c.Enqueue(tasks.NotifyAboutNewPost(newPost.Result))

metrics.TotalPosts.Inc()
return c.Ok(web.Map{
"id": newPost.Result.ID,
"number": newPost.Result.Number,
Expand Down Expand Up @@ -250,6 +252,7 @@ func PostComment() web.HandlerFunc {

c.Enqueue(tasks.NotifyAboutNewComment(getPost.Result, action.Content))

metrics.TotalComments.Inc()
return c.Ok(web.Map{
"id": addNewComment.Result.ID,
})
Expand Down Expand Up @@ -309,9 +312,15 @@ func DeleteComment() web.HandlerFunc {
// AddVote adds current user to given post list of votes
func AddVote() web.HandlerFunc {
return func(c *web.Context) error {
return addOrRemove(c, func(post *entity.Post, user *entity.User) bus.Msg {
err := addOrRemove(c, func(post *entity.Post, user *entity.User) bus.Msg {
return &cmd.AddVote{Post: post, User: user}
})

if err == nil {
metrics.TotalVotes.Inc()
}

return err
}
}

Expand Down
2 changes: 2 additions & 0 deletions app/handlers/signup.go
Expand Up @@ -3,6 +3,7 @@ package handlers
import (
"time"

"github.com/getfider/fider/app/metrics"
"github.com/getfider/fider/app/models/entity"
"github.com/getfider/fider/app/models/enum"
"github.com/getfider/fider/app/models/query"
Expand Down Expand Up @@ -69,6 +70,7 @@ func CreateTenant() web.HandlerFunc {
return c.Failure(err)
}

metrics.TotalTenants.Inc()
c.SetTenant(createTenant.Result)

user := &entity.User{
Expand Down
47 changes: 47 additions & 0 deletions app/metrics/metrics_fider.go
@@ -0,0 +1,47 @@
package metrics

import (
"github.com/getfider/fider/app/pkg/env"
"github.com/prometheus/client_golang/prometheus"
)

var TotalTenants = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "fider_tenants_total",
Help: "Number of Fider tenants.",
},
)

var TotalPosts = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "fider_posts_total",
Help: "Number of Fider posts.",
},
)

var TotalComments = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "fider_comments_total",
Help: "Number of Fider comments.",
},
)

var TotalVotes = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "fider_votes_total",
Help: "Number of Fider votes.",
},
)

var fiderInfo = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "fider_info",
Help: "Information about Fider environment.",
ConstLabels: prometheus.Labels{"version": env.Version()},
},
)

func init() {
fiderInfo.Inc()
prometheus.MustRegister(TotalTenants, TotalPosts, TotalComments, TotalVotes, fiderInfo)
}
21 changes: 21 additions & 0 deletions app/metrics/metrics_http.go
@@ -0,0 +1,21 @@
package metrics

import "github.com/prometheus/client_golang/prometheus"

var HttpRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Number of HTTP requests.",
},
[]string{"code", "operation"},
)

var HttpDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests.",
Buckets: []float64{0.2, 0.5, 1, 2, 5},
}, []string{"operation"})

func init() {
prometheus.MustRegister(HttpRequests, HttpDuration)
}
34 changes: 34 additions & 0 deletions app/middlewares/instrumentation.go
@@ -0,0 +1,34 @@
package middlewares

import (
"fmt"
"strconv"
"time"

"github.com/getfider/fider/app/metrics"
"github.com/getfider/fider/app/pkg/env"
"github.com/getfider/fider/app/pkg/web"
"github.com/julienschmidt/httprouter"
)

// Instrumentation adds Prometheus HTTP Middlewares
func Instrumentation() web.MiddlewareFunc {
if !env.Config.Metrics.Enabled {
return nil
}

return func(next web.HandlerFunc) web.HandlerFunc {
return func(c *web.Context) error {
begin := time.Now()

err := next(c)

operation := fmt.Sprintf("%s /%s", c.Request.Method, c.Param(httprouter.MatchedRoutePathParam))
code := strconv.Itoa(c.ResponseStatusCode)
metrics.HttpRequests.WithLabelValues(code, operation).Inc()
metrics.HttpDuration.WithLabelValues(operation).Observe(time.Since(begin).Seconds())

return err
}
}
}
58 changes: 58 additions & 0 deletions app/middlewares/instrumentation_test.go
@@ -0,0 +1,58 @@
package middlewares_test

import (
"net/http"
"testing"

"github.com/getfider/fider/app/metrics"
"github.com/getfider/fider/app/middlewares"
. "github.com/getfider/fider/app/pkg/assert"
"github.com/getfider/fider/app/pkg/env"
"github.com/getfider/fider/app/pkg/mock"
"github.com/getfider/fider/app/pkg/web"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)

func TestInstrumentation_Disabled(t *testing.T) {
RegisterT(t)

defer func() {
env.Config.Metrics.Enabled = true
}()

server := mock.NewServer()
env.Config.Metrics.Enabled = false
server.Use(middlewares.Instrumentation())
handler := func(c *web.Context) error {
return c.NoContent(http.StatusOK)
}

status, _ := server.Execute(handler)

Expect(status).Equals(http.StatusOK)
Expect(getCounterValue(metrics.HttpRequests, "200", "GET /")).Equals(float64(0))
}

func TestInstrumentation_Enabled(t *testing.T) {
RegisterT(t)

server := mock.NewServer()
server.Use(middlewares.Instrumentation())
handler := func(c *web.Context) error {
return c.NoContent(http.StatusOK)
}

status, _ := server.Execute(handler)

Expect(status).Equals(http.StatusOK)
Expect(getCounterValue(metrics.HttpRequests, "200", "GET /")).Equals(float64(1))
}

func getCounterValue(metric *prometheus.CounterVec, lvs ...string) float64 {
var m = &dto.Metric{}
if err := metric.WithLabelValues(lvs...).Write(m); err != nil {
return 0
}
return m.Counter.GetValue()
}
7 changes: 4 additions & 3 deletions app/middlewares/maintenance.go
Expand Up @@ -9,11 +9,12 @@ import (

// Maintenance returns a maintenance page when system is under maintenance
func Maintenance() web.MiddlewareFunc {
if !env.Config.Maintenance.Enabled {
return nil
}

return func(next web.HandlerFunc) web.HandlerFunc {
return func(c *web.Context) error {
if !env.Config.Maintenance.Enabled {
return next(c)
}

c.Response.Header().Set("Retry-After", "3600")

Expand Down
8 changes: 6 additions & 2 deletions app/pkg/env/env.go
Expand Up @@ -16,7 +16,7 @@ import (
var (
// these values are replaced during CI build
buildnumber = ""
version = "0.18.1"
version = "0.19.0-dev"
)

func Version() string {
Expand All @@ -40,7 +40,11 @@ type config struct {
HostDomain string `env:"HOST_DOMAIN,required"`
Locale string `env:"LOCALE,default=en"`
JWTSecret string `env:"JWT_SECRET,required"`
Database struct {
Metrics struct {
Enabled bool `env:"METRICS_ENABLED,default=false"`
Port string `env:"METRICS_PORT,default=4000"`
}
Database struct {
URL string `env:"DATABASE_URL,required"`
MaxIdleConns int `env:"DATABASE_MAX_IDLE_CONNS,default=2,strict"`
MaxOpenConns int `env:"DATABASE_MAX_OPEN_CONNS,default=4,strict"`
Expand Down
4 changes: 3 additions & 1 deletion app/pkg/mock/server.go
Expand Up @@ -49,7 +49,9 @@ func (s *Server) Engine() *web.Engine {

// Use adds a new middleware to pipeline
func (s *Server) Use(middleware web.MiddlewareFunc) *Server {
s.middleware = append(s.middleware, middleware)
if middleware != nil {
s.middleware = append(s.middleware, middleware)
}
return s
}

Expand Down

0 comments on commit a37a915

Please sign in to comment.