pasty/pkg/chizerolog/logger.go

79 lines
2.1 KiB
Go

package chizerolog
import (
"context"
"fmt"
"github.com/go-chi/chi/v5/middleware"
"github.com/rs/zerolog/log"
"net/http"
"time"
)
const dataKey = "chzl_meta"
// Logger uses the global zerolog logger to log HTTP requests.
// Log messages are printed with the debug level.
// This middleware should be registered first.
func Logger(next http.Handler) http.Handler {
fn := func(writer http.ResponseWriter, request *http.Request) {
request = request.WithContext(context.WithValue(request.Context(), dataKey, make(map[string]any)))
proxy := middleware.NewWrapResponseWriter(writer, request.ProtoMajor)
start := time.Now()
defer func() {
end := time.Now()
scheme := "http"
if request.TLS != nil {
scheme = "https"
}
url := fmt.Sprintf("%s://%s%s", scheme, request.Host, request.RequestURI)
var err error
data := request.Context().Value(dataKey)
if data != nil {
injErr, ok := data.(map[string]any)["err"]
if ok {
err = injErr.(error)
}
}
if err == nil {
log.Debug().
Str("proto", request.Proto).
Str("method", request.Method).
Str("route", url).
Str("client_address", request.RemoteAddr).
Int("response_size", proxy.BytesWritten()).
Str("elapsed", fmt.Sprintf("%s", end.Sub(start))).
Int("status_code", proxy.Status()).
Msg("An incoming request has been processed.")
} else {
log.Error().
Err(err).
Str("proto", request.Proto).
Str("method", request.Method).
Str("route", url).
Str("client_address", request.RemoteAddr).
Int("response_size", proxy.BytesWritten()).
Str("elapsed", fmt.Sprintf("%s", end.Sub(start))).
Int("status_code", proxy.Status()).
Msg("An incoming request has been processed and resulted in an unexpected error.")
}
}()
next.ServeHTTP(proxy, request)
}
return http.HandlerFunc(fn)
}
// InjectError injects the given error to a specific key so that Logger will log its occurrence later on in the request chain.
func InjectError(request *http.Request, err error) {
data := request.Context().Value(dataKey)
if data == nil {
return
}
data.(map[string]any)["err"] = err
}