6de2455917
- Added GLOBAL_MARKETS_TITLE to all translation files - Updated footer with 12 markets (4 active + 8 upcoming) - Translated market section to: zh-cn, zh-tw, ja, ko, th, vi, id, ms, hi - Built and deployed to production - CloudFront invalidation: I3RTMXVFDJXWLG3SYX208OP1CC
80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/hlog"
|
|
)
|
|
|
|
// responseWriter wraps http.ResponseWriter to capture status code and bytes written.
|
|
type responseWriter struct {
|
|
http.ResponseWriter
|
|
status int
|
|
bytes int
|
|
}
|
|
|
|
func (rw *responseWriter) WriteHeader(code int) {
|
|
rw.status = code
|
|
rw.ResponseWriter.WriteHeader(code)
|
|
}
|
|
|
|
func (rw *responseWriter) Write(b []byte) (int, error) {
|
|
n, err := rw.ResponseWriter.Write(b)
|
|
rw.bytes += n
|
|
return n, err
|
|
}
|
|
|
|
// Logger returns a zerolog structured logging middleware. It logs each request
|
|
// with method, path, status, latency, remote IP, and request ID (if set by
|
|
// hlog.RequestIDHandler upstream).
|
|
func Logger(log zerolog.Logger) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
start := time.Now()
|
|
|
|
rw := &responseWriter{ResponseWriter: w, status: http.StatusOK}
|
|
next.ServeHTTP(rw, r)
|
|
|
|
latency := time.Since(start)
|
|
|
|
event := log.Info()
|
|
|
|
// Attach request ID if hlog placed one in context.
|
|
if id, ok := hlog.IDFromRequest(r); ok {
|
|
event = event.Str("req_id", id.String())
|
|
}
|
|
|
|
event.
|
|
Str("method", r.Method).
|
|
Str("path", r.URL.Path).
|
|
Str("query", r.URL.RawQuery).
|
|
Int("status", rw.status).
|
|
Int("bytes", rw.bytes).
|
|
Dur("latency_ms", latency).
|
|
Str("remote_ip", realIP(r)).
|
|
Str("user_agent", r.UserAgent()).
|
|
Msg("request")
|
|
})
|
|
}
|
|
}
|
|
|
|
// realIP returns the originating IP, preferring X-Forwarded-For / X-Real-IP
|
|
// headers set by a trusted reverse proxy.
|
|
func realIP(r *http.Request) string {
|
|
if ip := r.Header.Get("X-Real-IP"); ip != "" {
|
|
return ip
|
|
}
|
|
if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
|
|
// X-Forwarded-For may be a comma-separated list; take the first entry.
|
|
for i := 0; i < len(ip); i++ {
|
|
if ip[i] == ',' {
|
|
return ip[:i]
|
|
}
|
|
}
|
|
return ip
|
|
}
|
|
return r.RemoteAddr
|
|
}
|