add middlewares
This commit is contained in:
parent
45f69d994e
commit
b2445cf774
@ -9,6 +9,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
cacheproxy "git.jeffthecoder.xyz/guochao/cache-proxy"
|
cacheproxy "git.jeffthecoder.xyz/guochao/cache-proxy"
|
||||||
|
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware"
|
||||||
|
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/handlerlog"
|
||||||
|
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/httplog"
|
||||||
|
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/recover"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
@ -115,9 +120,16 @@ func main() {
|
|||||||
|
|
||||||
server := cacheproxy.NewServer(*config)
|
server := cacheproxy.NewServer(*config)
|
||||||
|
|
||||||
http.HandleFunc("GET /{path...}", server.HandleRequestWithCache)
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.HandleFunc("GET /", server.HandleRequestWithCache)
|
||||||
|
|
||||||
slog.With("addr", ":8881").Info("serving app")
|
slog.With("addr", ":8881").Info("serving app")
|
||||||
if err := http.ListenAndServe(":8881", nil); err != nil {
|
if err := http.ListenAndServe(":8881", middleware.Use(mux,
|
||||||
|
recover.Recover(),
|
||||||
|
handlerlog.FindHandler(mux),
|
||||||
|
httplog.Log(httplog.Config{LogStart: true, LogFinish: true}),
|
||||||
|
)); err != nil {
|
||||||
slog.With("error", err).Error("failed to start server")
|
slog.With("error", err).Error("failed to start server")
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
30
pkgs/middleware/handlerlog/log.go
Normal file
30
pkgs/middleware/handlerlog/log.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package handlerlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ctxKey int
|
||||||
|
|
||||||
|
var (
|
||||||
|
key ctxKey = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func FindHandler(mux *http.ServeMux) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
if _, pattern := mux.Handler(r); pattern != "" {
|
||||||
|
ctx = context.WithValue(ctx, key, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Pattern(r *http.Request) (string, bool) {
|
||||||
|
pattern, ok := r.Context().Value(key).(string)
|
||||||
|
return pattern, ok
|
||||||
|
}
|
75
pkgs/middleware/httplog/log.go
Normal file
75
pkgs/middleware/httplog/log.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
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(),
|
||||||
|
)
|
||||||
|
}
|
14
pkgs/middleware/middleware.go
Normal file
14
pkgs/middleware/middleware.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Use(handler http.Handler, wrappers ...func(http.Handler) http.Handler) http.Handler {
|
||||||
|
slices.Reverse(wrappers)
|
||||||
|
for _, wrapper := range wrappers {
|
||||||
|
handler = wrapper(handler)
|
||||||
|
}
|
||||||
|
return handler
|
||||||
|
}
|
25
pkgs/middleware/recover/recover.go
Normal file
25
pkgs/middleware/recover/recover.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package recover
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.jeffthecoder.xyz/guochao/cache-proxy/pkgs/middleware/httplog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func recoverer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
httplog.Logger(r).With("error", err).Error("request panicked")
|
||||||
|
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Recover() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer recoverer(w, r)
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user