From 0892c33b134b32e0808f0d6756e73400b19584c9 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 29 Apr 2026 15:38:06 +0200 Subject: [PATCH] feat: metrics, makefile --- projects/greenlight/Makefile | 34 +++++++++++++++++++++++ projects/greenlight/cmd/api/middleware.go | 34 +++++++++++++++++++++++ projects/greenlight/cmd/api/routes.go | 2 +- projects/greenlight/go.mod | 1 + projects/greenlight/go.sum | 2 ++ 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/projects/greenlight/Makefile b/projects/greenlight/Makefile index e69de29..4456ccb 100644 --- a/projects/greenlight/Makefile +++ b/projects/greenlight/Makefile @@ -0,0 +1,34 @@ +include .env + +## help: print this help message +.PHONY: help +help: + @echo 'Usage:' + @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' + +.PHONY: confirm +confirm: + @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] + +## run/api: run the cmd/api application +.PHONY: run/api +run/api: + go run ./cmd/api -db-dsn=${DATABASE_DSN} + +## db/psql: connect to the database using psql +.PHONY: db/psql +db/psql: + psql ${DATABASE_DSN} + +## db/migrations/new name=$1: create a new database migration +.PHONY: db/migrations/new +db/migrations/new: + @echo 'Creating migration files for ${name}...' + migrate create -seq -ext=.sql -dir=./migrations ${name} + +## db/migrations/up: apply all up database migrations +.PHONY: db/migrations/up +db/migrations/up: confirm + @echo 'Running up migrations...' + migrate -path ./migrations -database ${DATABASE_DSN} up + diff --git a/projects/greenlight/cmd/api/middleware.go b/projects/greenlight/cmd/api/middleware.go index 292a52a..d016242 100644 --- a/projects/greenlight/cmd/api/middleware.go +++ b/projects/greenlight/cmd/api/middleware.go @@ -2,13 +2,16 @@ package main import ( "errors" + "expvar" "fmt" "net" "net/http" + "strconv" "strings" "sync" "time" + "github.com/felixge/httpsnoop" "golang.org/x/time/rate" "greenlight.debuggingjon.dev/internal/data" "greenlight.debuggingjon.dev/internal/validator" @@ -280,3 +283,34 @@ func (app *application) enableCORS(next http.Handler) http.Handler { next.ServeHTTP(w, r) }) } + +func (app *application) metrics(next http.Handler) http.Handler { + totalRequestsReceived := expvar.NewInt("total_requests_received") + totalResponsesSent := expvar.NewInt("total_responses_sent") + totalProcessingTimeMicroseconds := expvar.NewInt("total_processing_time_μs") + // Declare a new expvar map to hold the count of responses for each HTTP status + // code. + totalResponsesSentByStatus := expvar.NewMap("total_responses_sent_by_status") + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Increment the requests received count, like before. + totalRequestsReceived.Add(1) + + // Call the httpsnoop.CaptureMetrics() function, passing in the next handler in + // the chain along with the existing http.ResponseWriter and http.Request. This + // returns the metrics struct that we saw above. + metrics := httpsnoop.CaptureMetrics(next, w, r) + + // Increment the response sent count, like before. + totalResponsesSent.Add(1) + + // Get the request processing time in microseconds from httpsnoop and increment + // the cumulative processing time. + totalProcessingTimeMicroseconds.Add(metrics.Duration.Microseconds()) + + // Use the Add() method to increment the count for the given status code by 1. + // Note that the expvar map is string-keyed, so we need to use the strconv.Itoa() + // function to convert the status code (which is an integer) to a string. + totalResponsesSentByStatus.Add(strconv.Itoa(metrics.Code), 1) + }) +} diff --git a/projects/greenlight/cmd/api/routes.go b/projects/greenlight/cmd/api/routes.go index 00fa5b8..dbc9dec 100644 --- a/projects/greenlight/cmd/api/routes.go +++ b/projects/greenlight/cmd/api/routes.go @@ -31,5 +31,5 @@ func (app *application) routes() http.Handler { // Register a new GET /debug/vars endpoint pointing to the expvar handler. router.Handler(http.MethodGet, "/debug/vars", expvar.Handler()) - return app.recoverPanic(app.enableCORS(app.rateLimit(app.authenticate(router)))) + return app.metrics(app.recoverPanic(app.enableCORS(app.rateLimit(app.authenticate(router))))) } diff --git a/projects/greenlight/go.mod b/projects/greenlight/go.mod index 1e0254a..4b78c26 100644 --- a/projects/greenlight/go.mod +++ b/projects/greenlight/go.mod @@ -11,6 +11,7 @@ require ( ) require ( + github.com/felixge/httpsnoop v1.0.1 // indirect github.com/go-mail/mail/v2 v2.3.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect ) diff --git a/projects/greenlight/go.sum b/projects/greenlight/go.sum index 9d9d661..e8f00b2 100644 --- a/projects/greenlight/go.sum +++ b/projects/greenlight/go.sum @@ -1,3 +1,5 @@ +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-mail/mail/v2 v2.3.0 h1:wha99yf2v3cpUzD1V9ujP404Jbw2uEvs+rBJybkdYcw= github.com/go-mail/mail/v2 v2.3.0/go.mod h1:oE2UK8qebZAjjV1ZYUpY7FPnbi/kIU53l1dmqPRb4go= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=