madipo2611 6b9cdeba55
All checks were successful
continuous-integration/drone/push Build is passing
v0.0.17 Переработан websocket
2025-08-11 00:22:16 +03:00

100 lines
2.5 KiB
Go

package middleware
import (
"bufio"
"bytes"
"errors"
"io"
"log"
"net"
"net/http"
"time"
)
// LoggingMiddleware логирует входящие HTTP-запросы
func LoggingMiddleware(logger *log.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()
if r.Header.Get("Upgrade") == "websocket" {
next.ServeHTTP(w, r)
return
}
// Логируем основные параметры запроса
logData := map[string]interface{}{
"method": r.Method,
"path": r.URL.Path,
"query": r.URL.RawQuery,
"ip": r.RemoteAddr,
}
// Чтение тела запроса (для логирования)
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
if len(bodyBytes) > 0 {
logData["body_size"] = len(bodyBytes)
// Для JSON-запросов логируем тело
if r.Header.Get("Content-Type") == "application/json" {
logData["body"] = string(bodyBytes)
}
}
}
// Перехват ответа
rw := &responseWriter{ResponseWriter: w, status: http.StatusOK}
// Обработка запроса
next.ServeHTTP(rw, r)
// Дополняем данные для логирования
duration := time.Since(start)
logData["status"] = rw.status
logData["duration"] = duration.String()
logData["response_size"] = rw.size
// Форматированный вывод лога
logger.Printf(
"%s %s %d %s | IP: %s | Duration: %s | Body: %d bytes",
r.Method,
r.URL.Path,
rw.status,
http.StatusText(rw.status),
r.RemoteAddr,
duration,
len(bodyBytes),
)
})
}
}
// Кастомный responseWriter для перехвата статуса и размера ответа
type responseWriter struct {
http.ResponseWriter
status int
size int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.status = code
rw.ResponseWriter.WriteHeader(code)
}
func (rw *responseWriter) Write(b []byte) (int, error) {
size, err := rw.ResponseWriter.Write(b)
rw.size += size
return size, err
}
// Добавляем поддержку Hijacker
func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if hijacker, ok := rw.ResponseWriter.(http.Hijacker); ok {
return hijacker.Hijack()
}
return nil, nil, errors.New("response writer does not support hijacking")
}