package httplog import ( "context" "log/slog" "net/http" "time" "git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/handlerlog" ) type ctxKey int const ( loggerKey ctxKey = iota ) type Config struct { LogStart bool LogStartLevel slog.Level LogFinish bool LogFinishLevel slog.Level } type responseRecorder struct { http.ResponseWriter StatusCode int } func (recorder *responseRecorder) WriteHeader(statusCode int) { recorder.StatusCode = statusCode recorder.ResponseWriter.WriteHeader(statusCode) } func Log(config Config) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { args := []any{ "remote_addr", r.RemoteAddr, "host", r.Host, "path", r.URL.Path, } if pattern, handlerFound := handlerlog.Pattern(r); handlerFound { args = append(args, "handler", pattern) } startTime := time.Now() if config.LogStart { slog.With(append(args, "time", startTime)...).Log(r.Context(), config.LogStartLevel, "request start") } recorder := &responseRecorder{ResponseWriter: w, StatusCode: 200} next.ServeHTTP(recorder, r.WithContext(context.WithValue(r.Context(), loggerKey, slog.With(args...)))) if config.LogFinish { finishTime := time.Now() slog.With(append(args, "time", finishTime, "duration", finishTime.Sub(startTime), "status", recorder.StatusCode)...).Log(r.Context(), config.LogFinishLevel, "request finished") } }) } } func Logger(r *http.Request) *slog.Logger { if logger, ok := r.Context().Value(loggerKey).(*slog.Logger); ok { return logger.With("time", time.Now()) } return slog.With( "remote_addr", r.RemoteAddr, "host", r.Host, "path", r.URL.Path, "time", time.Now(), ) }