From cb0c87d2005dd884ac7bc56d28ba627839ef07ac Mon Sep 17 00:00:00 2001 From: Ryan McGuire Date: Sun, 24 Aug 2025 15:50:34 +0000 Subject: [PATCH] refactors HTTP server initialization, improves logging middleware, and updates documentation (#1) Reviewed-on: https://gitea.libretechconsulting.com/rmcguire/go-app/pulls/1 --- pkg/srv/http/http.go | 7 +++++-- pkg/srv/http/http_log.go | 30 ++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/pkg/srv/http/http.go b/pkg/srv/http/http.go index 8f7ec42..78bbea6 100644 --- a/pkg/srv/http/http.go +++ b/pkg/srv/http/http.go @@ -1,3 +1,6 @@ +// Package http provides functionality for setting up and managing HTTP servers. +// It includes features for health checks, Prometheus metrics, OpenTelemetry +// tracing, and custom middleware integration. package http import ( @@ -133,8 +136,8 @@ func prepHTTPServer(opts *opts.AppHTTP) *http.Server { } } -// Returns a shutdown func and a done channel if the -// server aborts abnormally. Returns error on failure to start +// InitHTTPServer returns a shutdown func and a done channel if the +// server aborts abnormally. Returns error on failure to start. func InitHTTPServer(opts *opts.AppHTTP) ( func(context.Context) error, <-chan any, error, ) { diff --git a/pkg/srv/http/http_log.go b/pkg/srv/http/http_log.go index 6e9b520..cb05005 100644 --- a/pkg/srv/http/http_log.go +++ b/pkg/srv/http/http_log.go @@ -3,7 +3,8 @@ package http import ( "bytes" "context" - "errors" + "fmt" + "io" "net/http" "regexp" "time" @@ -41,29 +42,42 @@ func loggingMiddleware(appCtx context.Context, next http.Handler) http.Handler { Dur("duration", time.Since(start)). Msg("http request served") - // Log response body + // Log response with body if not 204 + if lrr.statusCode == http.StatusNoContent { + trcLog := log.Trace(). + Str("path", r.URL.Path). + Int("statusCode", lrr.statusCode) + trcLog.Msg("http response (no content)") // Explicitly log 204 + return // No body to log for 204 No Content + } + trcLog := log.Trace(). Str("path", r.URL.Path). Int("statusCode", lrr.statusCode) - // Check if it's JSON firstByte, err := lrr.body.ReadByte() if err != nil { - trcLog.Err(errors.New("invalid response body")).Send() - return + if err == io.EOF { + // Body is empty, which might be valid for some non-204 responses. + trcLog.Msg("http response (empty body)") + } else { + // Other error reading the body. Wrap the original error for context. + trcLog.Err(fmt.Errorf("error reading response body: %w", err)).Send() + } + return // No further body processing if there was an error or it was empty } - lrr.body.UnreadByte() + lrr.body.UnreadByte() // Put the byte back for Bytes() to read if firstByte == '{' { trcLog = trcLog.RawJSON("response", lrr.body.Bytes()) } else { trcLog = trcLog.Bytes("response", lrr.body.Bytes()) } - trcLog.Msg("response payload") + trcLog.Msg("http response") }) } -// Implement Flush to support the http.Flusher interface +// Flush implements the http.Flusher interface to allow flushing buffered data. func (w *LoggingResponseWriter) Flush() { if flusher, ok := w.ResponseWriter.(http.Flusher); ok { flusher.Flush()