From cbb6be135af161daf33a190a9d9185fa81437c81 Mon Sep 17 00:00:00 2001 From: Trong Huu Nguyen Date: Tue, 19 Jul 2022 09:25:43 +0200 Subject: [PATCH] feat(metrics): add metrics for successful logins and logouts --- pkg/handler/handler_callback.go | 2 + pkg/handler/handler_frontchannellogout.go | 6 ++ pkg/handler/handler_logout.go | 2 + pkg/metrics/metrics.go | 83 ++++++++++++++++++++++- pkg/session/redis.go | 6 +- 5 files changed, 93 insertions(+), 6 deletions(-) diff --git a/pkg/handler/handler_callback.go b/pkg/handler/handler_callback.go index 623ece8..5f90e65 100644 --- a/pkg/handler/handler_callback.go +++ b/pkg/handler/handler_callback.go @@ -10,6 +10,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/nais/wonderwall/pkg/loginstatus" + "github.com/nais/wonderwall/pkg/metrics" logentry "github.com/nais/wonderwall/pkg/middleware" "github.com/nais/wonderwall/pkg/openid" "github.com/nais/wonderwall/pkg/openid/client" @@ -120,4 +121,5 @@ func logSuccessfulLogin(r *http.Request, tokens *openid.Tokens, referer string) } logentry.LogEntry(r).WithFields(fields).Info("callback: successful login") + metrics.ObserveLogin() } diff --git a/pkg/handler/handler_frontchannellogout.go b/pkg/handler/handler_frontchannellogout.go index 437034a..3437ab2 100644 --- a/pkg/handler/handler_frontchannellogout.go +++ b/pkg/handler/handler_frontchannellogout.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/nais/wonderwall/pkg/cookie" + "github.com/nais/wonderwall/pkg/metrics" logentry "github.com/nais/wonderwall/pkg/middleware" ) @@ -31,14 +32,19 @@ func (h *Handler) FrontChannelLogout(w http.ResponseWriter, r *http.Request) { sessionData, err := h.getSession(r, sessionID) if err != nil { logger.Infof("front-channel logout: getting session (user might already be logged out): %+v", err) + w.WriteHeader(http.StatusAccepted) + return } err = h.destroySession(w, r, sessionID) if err != nil { logger.Warnf("front-channel logout: destroying session: %+v", err) + w.WriteHeader(http.StatusAccepted) + return } else if sessionData != nil { logger.WithField("jti", sessionData.IDTokenJwtID).Infof("front-channel logout: successful logout") } + metrics.ObserveLogout(metrics.LogoutOperationFrontChannel) w.WriteHeader(http.StatusOK) } diff --git a/pkg/handler/handler_logout.go b/pkg/handler/handler_logout.go index 21a4b62..dfd0107 100644 --- a/pkg/handler/handler_logout.go +++ b/pkg/handler/handler_logout.go @@ -8,6 +8,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/nais/wonderwall/pkg/cookie" + "github.com/nais/wonderwall/pkg/metrics" logentry "github.com/nais/wonderwall/pkg/middleware" "github.com/nais/wonderwall/pkg/session" ) @@ -46,5 +47,6 @@ func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { } logger.Info("logout: redirecting to identity provider") + metrics.ObserveLogout(metrics.LogoutOperationSelfInitiated) http.Redirect(w, r, logout.SingleLogoutURL(idToken), http.StatusTemporaryRedirect) } diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 3918697..15821a7 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -11,7 +11,29 @@ import ( const ( Namespace = "wonderwall" - RedisOperationLabel = "operation" + LabelOperation = "operation" + LabelHpa = "hpa" +) + +type Hpa = string + +const ( + HpaRate = "rate" +) + +type LogoutOperation = string + +const ( + LogoutOperationSelfInitiated = "self_initiated" + LogoutOperationFrontChannel = "front_channel" +) + +type RedisOperation = string + +const ( + RedisOperationRead = "Read" + RedisOperationWrite = "Write" + RedisOperationDelete = "Delete" ) var ( @@ -20,11 +42,51 @@ var ( Namespace: Namespace, Help: "latency in redis operations", Buckets: prometheus.ExponentialBuckets(0.02, 2, 14), - }, []string{RedisOperationLabel}) + }, []string{LabelOperation}) + + Logins = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "logins", + Namespace: Namespace, + Help: "cumulative number of successful logins", + }, + []string{ + LabelHpa, + }, + ) + + Logouts = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "logouts", + Namespace: Namespace, + Help: "cumulative number of successful logouts", + }, + []string{ + LabelOperation, + LabelHpa, + }, + ) ) +// InitLabels zeroes out all possible label combinations +func InitLabels() { + logoutOperations := []LogoutOperation{LogoutOperationSelfInitiated, LogoutOperationFrontChannel} + + for _, operation := range logoutOperations { + Logouts.With(prometheus.Labels{ + LabelOperation: operation, + LabelHpa: HpaRate, + }) + } + + Logins.With(prometheus.Labels{ + LabelHpa: HpaRate, + }) +} + func Handle(address string) error { Register(prometheus.DefaultRegisterer) + InitLabels() handler := promhttp.Handler() return http.ListenAndServe(address, handler) } @@ -32,6 +94,8 @@ func Handle(address string) error { func Register(registry prometheus.Registerer) { registry.MustRegister( RedisLatency, + Logins, + Logouts, ) } @@ -40,7 +104,20 @@ func ObserveRedisLatency(operation string, fun func() error) error { err := fun() used := time.Now().Sub(timer) RedisLatency.With(prometheus.Labels{ - RedisOperationLabel: operation, + LabelOperation: operation, }).Observe(used.Seconds()) return err } + +func ObserveLogin() { + Logins.With(prometheus.Labels{ + LabelHpa: HpaRate, + }).Inc() +} + +func ObserveLogout(operation LogoutOperation) { + Logouts.With(prometheus.Labels{ + LabelOperation: operation, + LabelHpa: HpaRate, + }).Inc() +} diff --git a/pkg/session/redis.go b/pkg/session/redis.go index cbc2f66..255ad03 100644 --- a/pkg/session/redis.go +++ b/pkg/session/redis.go @@ -25,7 +25,7 @@ func NewRedis(client redis.Cmdable) Store { func (s *redisSessionStore) Read(ctx context.Context, key string) (*EncryptedData, error) { encryptedData := &EncryptedData{} - err := metrics.ObserveRedisLatency("Read", func() error { + err := metrics.ObserveRedisLatency(metrics.RedisOperationRead, func() error { return s.client.Get(ctx, key).Scan(encryptedData) }) if err == nil { @@ -40,13 +40,13 @@ func (s *redisSessionStore) Read(ctx context.Context, key string) (*EncryptedDat } func (s *redisSessionStore) Write(ctx context.Context, key string, value *EncryptedData, expiration time.Duration) error { - return metrics.ObserveRedisLatency("Write", func() error { + return metrics.ObserveRedisLatency(metrics.RedisOperationWrite, func() error { return s.client.Set(ctx, key, value, expiration).Err() }) } func (s *redisSessionStore) Delete(ctx context.Context, keys ...string) error { - err := metrics.ObserveRedisLatency("Delete", func() error { + err := metrics.ObserveRedisLatency(metrics.RedisOperationDelete, func() error { return s.client.Del(ctx, keys...).Err() }) if err == nil {