Skip to content

Commit 369241b

Browse files
author
Christian Roessner
committed
Refactor Prometheus timing metrics across multiple modules
The commit introduces a more efficient approach to handling Prometheus timers across several modules. A new function, `PrometheusTimer`, has been created in `statistics.go` that manages the creation and usage of Prometheus Timers. This function has been utilized across various modules to replace redundant and repetitive code. This change improves code maintainability and promotes a DRY (Don't Repeat Yourself) approach. Signed-off-by: Christian Roessner <[email protected]>
1 parent e044c4d commit 369241b

File tree

16 files changed

+163
-80
lines changed

16 files changed

+163
-80
lines changed

server/backend/ldap.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"github.com/croessner/nauthilus/server/util"
2323
"github.com/go-kit/log/level"
2424
"github.com/go-ldap/ldap/v3"
25-
"github.com/prometheus/client_golang/prometheus"
2625
"github.com/segmentio/ksuid"
2726
"github.com/yuin/gopher-lua"
2827
)
@@ -58,7 +57,7 @@ type LDAPConnection struct {
5857
// essential when multiple goroutines need to access or modify the same connection concurrently.
5958
Mu sync.Mutex
6059

61-
// Conn is the active LDAP connection. It is a pointer to an ldap.Conn object.
60+
// Conn is the active LDAP connection. It is a pointer to a ldap.Conn object.
6261
Conn *ldap.Conn
6362
}
6463

@@ -1135,7 +1134,7 @@ func (l *LDAPConnection) search(ldapRequest *LDAPRequest) (result DatabaseResult
11351134
//
11361135
// err: Error. If an error occurs during the search operation or modification, it will return an error, otherwise this function will return nil.
11371136
//
1138-
// Note: This operation is destructive. It modifies the LDAP directory. Therefore, it should be used judiciously and you should make sure that ldapRequest contains correct values.
1137+
// Note: This operation is destructive. It modifies the LDAP directory. Therefore, it should be used judiciously, and you should make sure that ldapRequest contains correct values.
11391138
func (l *LDAPConnection) modifyAdd(ldapRequest *LDAPRequest) (err error) {
11401139
var (
11411140
assertOk bool
@@ -1270,10 +1269,10 @@ func (l *LDAPPool) processLookupModifyAddRequest(index int, ldapRequest *LDAPReq
12701269
// It sets the connection state to global.LDAPStateFree.
12711270
// It unlocks the connection.
12721271
func (l *LDAPPool) proccessLookupRequest(index int, ldapRequest *LDAPRequest, ldapWaitGroup *sync.WaitGroup) {
1273-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Backend", "LDAPMainWorker"))
1272+
stopTimer := stats.PrometheusTimer(global.PromBackend, stats.FunctionDuration.WithLabelValues(global.PromBackend, "ldap_backend_lookup_request_total"))
12741273

12751274
defer func() {
1276-
timer.ObserveDuration()
1275+
stopTimer()
12771276
ldapWaitGroup.Done()
12781277
}()
12791278

@@ -1381,10 +1380,10 @@ func (l *LDAPPool) processAuthBindRequest(index int, ldapAuthRequest *LDAPAuthRe
13811380
// The function sends the LDAPReply object (which may contain the result, the raw result, or an error) to the LDAPAuthRequest channel.
13821381
// It locks the connection's mutex, sets the connection state to LDAPStateFree, and unlocks the mutex.
13831382
func (l *LDAPPool) processAuthRequest(index int, ldapAuthRequest *LDAPAuthRequest, ldapWaitGroup *sync.WaitGroup) {
1384-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Backend", "LDAPAuthWorker"))
1383+
stopTimer := stats.PrometheusTimer(global.PromBackend, stats.FunctionDuration.WithLabelValues(global.PromBackend, "ldap_backend_auth_request_total"))
13851384

13861385
defer func() {
1387-
timer.ObserveDuration()
1386+
stopTimer()
13881387
ldapWaitGroup.Done()
13891388
}()
13901389

server/backend/lua.go

-6
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ import (
1010
"github.com/croessner/nauthilus/server/global"
1111
"github.com/croessner/nauthilus/server/logging"
1212
"github.com/croessner/nauthilus/server/lualib"
13-
"github.com/croessner/nauthilus/server/stats"
1413
"github.com/croessner/nauthilus/server/util"
1514
"github.com/gin-gonic/gin"
1615
"github.com/go-kit/log/level"
17-
"github.com/prometheus/client_golang/prometheus"
1816
"github.com/spf13/viper"
1917
lua "github.com/yuin/gopher-lua"
2018
)
@@ -231,10 +229,6 @@ func setLuaRequestParameters(luaRequest *LuaRequest, request *lua.LTable) (luaCo
231229
//
232230
// err := executeAndHandleError(compiledScript, luaCommand, luaRequest, L, request, nret, logs)
233231
func executeAndHandleError(compiledScript *lua.FunctionProto, luaCommand string, luaRequest *LuaRequest, L *lua.LState, request *lua.LTable, nret int, logs *lualib.CustomLogKeyValue) (err error) {
234-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Backend", "executeLua"))
235-
236-
defer timer.ObserveDuration()
237-
238232
if err = lualib.PackagePath(L); err != nil {
239233
processError(err, luaRequest, logs)
240234
}

server/config/file.go

+29
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,34 @@ func (f *File) validateSecrets() error {
908908
return nil
909909
}
910910

911+
// validatePrometheusLabels is a method on the File struct that validates the Prometheus labels used in the server's Prometheus timer configuration.
912+
// If the Prometheus timer is enabled, it checks that each label is one of the predefined constants:
913+
// - global.PromAction
914+
// - global.PromAccount
915+
// - global.PromBackend
916+
// - global.PromBruteForce
917+
// - global.PromFeature
918+
// - global.PromFilter
919+
// - global.PromPostAction
920+
// - global.PromRequest
921+
// - global.PromStoreTOTP
922+
// If any label is unknown, it returns an error with a message indicating the unknown label.
923+
// If the Prometheus timer is not enabled, it returns nil.
924+
func (f *File) validatePrometheusLabels() error {
925+
if f.Server.PrometheusTimer.Enabled {
926+
for _, label := range f.Server.PrometheusTimer.Labels {
927+
switch label {
928+
case global.PromAction, global.PromAccount, global.PromBackend, global.PromBruteForce, global.PromFeature, global.PromFilter, global.PromPostAction, global.PromRequest, global.PromStoreTOTP:
929+
continue
930+
}
931+
932+
return fmt.Errorf("the prometheus_timer::label name '%s' is unknown", label)
933+
}
934+
}
935+
936+
return nil
937+
}
938+
911939
// LDAPHavePoolOnly is a method on the File struct.
912940
// It checks if the LDAP field and LDAP.Config field are not nil,
913941
// and returns the value of LDAP.Config.PoolOnly.
@@ -1269,6 +1297,7 @@ func (f *File) validate() (err error) {
12691297
f.validateRedisSentinels,
12701298
f.validateRedisDatabaseNumber,
12711299
f.validateRedisPoolSize,
1300+
f.validatePrometheusLabels,
12721301

12731302
// Without errors, but fixing things
12741303
f.validateInstanceName,

server/config/server.go

+21-15
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ import (
55
)
66

77
type ServerSection struct {
8-
Address string `mapstructure:"address"`
9-
HAproxyV2 bool `mapstructure:"haproxy_v2"`
10-
TLS TLS `mapstructure:"tls"`
11-
BasicAuth BasicAuth `mapstructure:"basic_auth"`
12-
InstanceName string `mapstructure:"instance_name"`
13-
Log Log `maptostructure:"log"`
14-
Backends []*Backend `mapstructure:"backends"`
15-
Features []*Feature `mapstructure:"features"`
16-
BruteForceProtocols []*Protocol `mapstructure:"brute_force_protocols"`
17-
HydraAdminUrl string `mapstructure:"ory_hydra_admin_url"`
18-
DNS DNS `mapstructure:"dns"`
19-
Insights Insights `mapstructure:"insights"`
20-
Redis Redis `mapstructure:"redis"`
21-
MasterUser MasterUser `mapstructure:"master_user"`
22-
Frontend Frontend `mapstructure:"frontend"`
8+
Address string `mapstructure:"address"`
9+
HAproxyV2 bool `mapstructure:"haproxy_v2"`
10+
TLS TLS `mapstructure:"tls"`
11+
BasicAuth BasicAuth `mapstructure:"basic_auth"`
12+
InstanceName string `mapstructure:"instance_name"`
13+
Log Log `maptostructure:"log"`
14+
Backends []*Backend `mapstructure:"backends"`
15+
Features []*Feature `mapstructure:"features"`
16+
BruteForceProtocols []*Protocol `mapstructure:"brute_force_protocols"`
17+
HydraAdminUrl string `mapstructure:"ory_hydra_admin_url"`
18+
DNS DNS `mapstructure:"dns"`
19+
Insights Insights `mapstructure:"insights"`
20+
Redis Redis `mapstructure:"redis"`
21+
MasterUser MasterUser `mapstructure:"master_user"`
22+
Frontend Frontend `mapstructure:"frontend"`
23+
PrometheusTimer PrometheusTimer `mapstructure:"prometheus_timer"`
2324
}
2425

2526
type TLS struct {
@@ -101,3 +102,8 @@ type Frontend struct {
101102
CookieStoreAuthKey string `mapstructure:"cookie_store_auth_key"`
102103
CookieStoreEncKey string `mapstructure:"cookie_store_encryption_key"`
103104
}
105+
106+
type PrometheusTimer struct {
107+
Enabled bool `mapstructure:"enabled"`
108+
Labels []string `mapstructure:"labels"`
109+
}

server/core/auth.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"github.com/go-kit/log/level"
3232
"github.com/go-webauthn/webauthn/webauthn"
3333
openapi "github.com/ory/hydra-client-go/v2"
34-
"github.com/prometheus/client_golang/prometheus"
3534
)
3635

3736
// ClaimHandler represents a claim handler struct.
@@ -1135,9 +1134,9 @@ func (a *Authentication) handleFeatures(ctx *gin.Context) (authResult global.Aut
11351134
return
11361135
}
11371136

1138-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Action", luaActionName))
1137+
stopTimer := stats.PrometheusTimer(global.PromAction, stats.FunctionDuration.WithLabelValues(global.PromAction, luaActionName))
11391138

1140-
defer timer.ObserveDuration()
1139+
defer stopTimer()
11411140

11421141
finished := make(chan action.Done)
11431142

@@ -1261,9 +1260,9 @@ func (a *Authentication) postLuaAction(passDBResult *PassDBResult) {
12611260
}
12621261

12631262
go func() {
1264-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("PostAction", "postLuaAction"))
1263+
stopTimer := stats.PrometheusTimer(global.PromPostAction, stats.FunctionDuration.WithLabelValues(global.PromPostAction, "lua_post_action_request_total"))
12651264

1266-
defer timer.ObserveDuration()
1265+
defer stopTimer()
12671266

12681267
finished := make(chan action.Done)
12691268

@@ -1617,9 +1616,9 @@ func (a *Authentication) filterLua(passDBResult *PassDBResult, ctx *gin.Context)
16171616
return global.AuthResultFail
16181617
}
16191618

1620-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Filter", "filterLua"))
1619+
stopTimer := stats.PrometheusTimer(global.PromFilter, stats.FunctionDuration.WithLabelValues(global.PromFilter, "lua_filter_request_total"))
16211620

1622-
defer timer.ObserveDuration()
1621+
defer stopTimer()
16231622

16241623
BackendServers.mu.RLock()
16251624

server/core/bruteforce.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/croessner/nauthilus/server/util"
2222
"github.com/dspinhirne/netaddr-go"
2323
"github.com/go-kit/log/level"
24-
"github.com/prometheus/client_golang/prometheus"
2524
"github.com/redis/go-redis/v9"
2625
)
2726

@@ -594,9 +593,9 @@ func (a *Authentication) checkBruteForce() (blockClientIP bool) {
594593
network *net.IPNet
595594
)
596595

597-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("BruteForce", "checkBruteForce"))
596+
stopTimer := stats.PrometheusTimer(global.PromBruteForce, stats.FunctionDuration.WithLabelValues(global.PromBruteForce, "check_brute_force_request_total"))
598597

599-
defer timer.ObserveDuration()
598+
defer stopTimer()
600599

601600
if config.LoadableConfig.BruteForce == nil {
602601
return false

server/core/cache.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/croessner/nauthilus/server/global"
77
"github.com/croessner/nauthilus/server/stats"
88
"github.com/croessner/nauthilus/server/util"
9-
"github.com/prometheus/client_golang/prometheus"
109
)
1110

1211
// cachePassDB implements the redis password database backend.
@@ -16,9 +15,9 @@ func cachePassDB(auth *Authentication) (passDBResult *PassDBResult, err error) {
1615
ppc *backend.PositivePasswordCache
1716
)
1817

19-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Authentication", "cachePassDB"))
18+
stopTimer := stats.PrometheusTimer(global.PromBackend, stats.FunctionDuration.WithLabelValues(global.PromBackend, "cache_backend_request_total"))
2019

21-
defer timer.ObserveDuration()
20+
defer stopTimer()
2221

2322
passDBResult = &PassDBResult{}
2423

server/core/features.go

+8-9
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ import (
1616
"github.com/croessner/nauthilus/server/util"
1717
"github.com/gin-gonic/gin"
1818
"github.com/go-kit/log/level"
19-
"github.com/prometheus/client_golang/prometheus"
2019
)
2120

2221
// featureLua runs Lua scripts and returns a trigger result.
2322
func (a *Authentication) featureLua(ctx *gin.Context) (triggered bool, abortFeatures bool, err error) {
24-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Feature", global.FeatureLua))
23+
stopTimer := stats.PrometheusTimer(global.PromFeature, stats.FunctionDuration.WithLabelValues(global.PromFeature, global.FeatureLua))
2524

26-
defer timer.ObserveDuration()
25+
defer stopTimer()
2726

2827
if !(a.ClientIP == global.Localhost4 || a.ClientIP == global.Localhost6 || a.ClientIP == "") {
2928
featureRequest := feature.Request{
@@ -91,9 +90,9 @@ func (a *Authentication) featureLua(ctx *gin.Context) (triggered bool, abortFeat
9190

9291
// featureTLSEncryption checks, if the remote client connection was secured.
9392
func (a *Authentication) featureTLSEncryption() (triggered bool) {
94-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Feature", global.FeatureTLSEncryption))
93+
stopTimer := stats.PrometheusTimer(global.PromFeature, stats.FunctionDuration.WithLabelValues(global.PromFeature, global.FeatureTLSEncryption))
9594

96-
defer timer.ObserveDuration()
95+
defer stopTimer()
9796

9897
if !(a.ClientIP == global.Localhost4 || a.ClientIP == global.Localhost6 || a.ClientIP == "") {
9998
if a.XSSL == global.NotAvailable {
@@ -125,9 +124,9 @@ func (a *Authentication) featureTLSEncryption() (triggered bool) {
125124
// featureRelayDomains triggers if a user sent an email address as a login name and the domain component does not
126125
// match the list of known domains.
127126
func (a *Authentication) featureRelayDomains() (triggered bool) {
128-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Feature", global.FeatureRelayDomains))
127+
stopTimer := stats.PrometheusTimer(global.PromFeature, stats.FunctionDuration.WithLabelValues(global.PromFeature, global.FeatureRelayDomains))
129128

130-
defer timer.ObserveDuration()
129+
defer stopTimer()
131130

132131
if config.LoadableConfig.RelayDomains == nil {
133132
return
@@ -171,9 +170,9 @@ func (a *Authentication) featureRBLs(ctx *gin.Context) (triggered bool, err erro
171170
totalRBLScore int
172171
)
173172

174-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Feature", global.FeatureRBL))
173+
stopTimer := stats.PrometheusTimer(global.PromFeature, stats.FunctionDuration.WithLabelValues(global.PromFeature, global.FeatureRBL))
175174

176-
defer timer.ObserveDuration()
175+
defer stopTimer()
177176

178177
if config.LoadableConfig.RBLs == nil {
179178
return

server/core/http.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,14 @@ func setupHTTPServer(router *gin.Engine) *http.Server {
464464
// This middleware function should be used in the setup of routing to collect metrics for each HTTP request.
465465
func prometheusMiddleware() gin.HandlerFunc {
466466
return func(ctx *gin.Context) {
467+
var timer *prometheus.Timer
468+
469+
stopTimer := stats.PrometheusTimer(global.PromRequest, stats.FunctionDuration.WithLabelValues(global.PromRequest, "request_total"))
467470
path := ctx.FullPath()
468471

469-
timer := prometheus.NewTimer(stats.HttpResponseTimeSecondsHist.WithLabelValues(path))
470-
timer2 := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Request", "prometheusMiddleware"))
472+
if config.LoadableConfig.Server.PrometheusTimer.Enabled {
473+
timer = prometheus.NewTimer(stats.HttpResponseTimeSecondsHist.WithLabelValues(path))
474+
}
471475

472476
ctx.Next()
473477

@@ -490,8 +494,11 @@ func prometheusMiddleware() gin.HandlerFunc {
490494
stats.RedisStaleConns.With(prometheus.Labels{"type": handleType}).Set(float64(redisStats.StaleConns))
491495
}
492496

493-
timer.ObserveDuration()
494-
timer2.ObserveDuration()
497+
if config.LoadableConfig.Server.PrometheusTimer.Enabled {
498+
timer.ObserveDuration()
499+
}
500+
501+
stopTimer()
495502
}
496503
}
497504

server/core/ldap.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/go-kit/log/level"
1616
"github.com/go-ldap/ldap/v3"
1717
"github.com/go-webauthn/webauthn/webauthn"
18-
"github.com/prometheus/client_golang/prometheus"
1918
)
2019

2120
// handleMasterUserMode handles the master user mode functionality for authentication.
@@ -266,9 +265,9 @@ func ldapAccountDB(auth *Authentication) (accounts AccountList, err error) {
266265
protocol *config.LDAPSearchProtocol
267266
)
268267

269-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("Account", "ldapAccountDB"))
268+
stopTimer := stats.PrometheusTimer(global.PromAccount, stats.FunctionDuration.WithLabelValues(global.PromAccount, "ldap_account_request_total"))
270269

271-
defer timer.ObserveDuration()
270+
defer stopTimer()
272271

273272
ldapReplyChan := make(chan *backend.LDAPReply)
274273

@@ -356,9 +355,9 @@ func ldapAddTOTPSecret(auth *Authentication, totp *TOTPSecret) (err error) {
356355
ldapError *ldap.Error
357356
)
358357

359-
timer := prometheus.NewTimer(stats.FunctionDuration.WithLabelValues("StoreTOTP", "ldapAddTOTPSecret"))
358+
stopTimer := stats.PrometheusTimer(global.PromStoreTOTP, stats.FunctionDuration.WithLabelValues(global.PromStoreTOTP, "ldap_store_totp_request_total"))
360359

361-
defer timer.ObserveDuration()
360+
defer stopTimer()
362361

363362
ldapReplyChan := make(chan *backend.LDAPReply)
364363

0 commit comments

Comments
 (0)