2025-08-07 13:47:42 +00:00
|
|
|
|
package monitor
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// RequestMonitor создает middleware для мониторинга запросов в стиле htop
|
|
|
|
|
|
func RequestMonitor() 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()
|
2025-09-28 11:46:20 +00:00
|
|
|
|
|
2025-08-07 13:47:42 +00:00
|
|
|
|
// Создаем wrapper для ResponseWriter чтобы получить статус код
|
|
|
|
|
|
ww := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
2025-09-28 11:46:20 +00:00
|
|
|
|
|
2025-08-07 13:47:42 +00:00
|
|
|
|
// Выполняем запрос
|
|
|
|
|
|
next.ServeHTTP(ww, r)
|
2025-09-28 11:46:20 +00:00
|
|
|
|
|
2025-08-07 13:47:42 +00:00
|
|
|
|
// Вычисляем время выполнения
|
|
|
|
|
|
duration := time.Since(start)
|
2025-09-28 11:46:20 +00:00
|
|
|
|
|
2025-08-07 13:47:42 +00:00
|
|
|
|
// Форматируем URL (обрезаем если слишком длинный)
|
|
|
|
|
|
url := r.URL.Path
|
|
|
|
|
|
if r.URL.RawQuery != "" {
|
|
|
|
|
|
url += "?" + r.URL.RawQuery
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(url) > 60 {
|
|
|
|
|
|
url = url[:57] + "..."
|
|
|
|
|
|
}
|
2025-09-28 11:46:20 +00:00
|
|
|
|
|
2025-08-07 13:47:42 +00:00
|
|
|
|
// Определяем цвет статуса
|
|
|
|
|
|
statusColor := getStatusColor(ww.statusCode)
|
|
|
|
|
|
methodColor := getMethodColor(r.Method)
|
2025-09-28 11:46:20 +00:00
|
|
|
|
|
2025-08-07 13:47:42 +00:00
|
|
|
|
// Выводим информацию о запросе
|
|
|
|
|
|
fmt.Printf("\033[2K\r%s%-6s\033[0m %s%-3d\033[0m │ %-60s │ %6.2fms\n",
|
|
|
|
|
|
methodColor, r.Method,
|
|
|
|
|
|
statusColor, ww.statusCode,
|
|
|
|
|
|
url,
|
|
|
|
|
|
float64(duration.Nanoseconds())/1000000,
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type responseWriter struct {
|
|
|
|
|
|
http.ResponseWriter
|
|
|
|
|
|
statusCode int
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (rw *responseWriter) WriteHeader(code int) {
|
|
|
|
|
|
rw.statusCode = code
|
|
|
|
|
|
rw.ResponseWriter.WriteHeader(code)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// getStatusColor возвращает ANSI цвет для статус кода
|
|
|
|
|
|
func getStatusColor(status int) string {
|
|
|
|
|
|
switch {
|
|
|
|
|
|
case status >= 200 && status < 300:
|
|
|
|
|
|
return "\033[32m" // Зеленый
|
|
|
|
|
|
case status >= 300 && status < 400:
|
|
|
|
|
|
return "\033[33m" // Желтый
|
|
|
|
|
|
case status >= 400 && status < 500:
|
|
|
|
|
|
return "\033[31m" // Красный
|
|
|
|
|
|
case status >= 500:
|
|
|
|
|
|
return "\033[35m" // Фиолетовый
|
|
|
|
|
|
default:
|
|
|
|
|
|
return "\033[37m" // Белый
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// getMethodColor возвращает ANSI цвет для HTTP метода
|
|
|
|
|
|
func getMethodColor(method string) string {
|
|
|
|
|
|
switch strings.ToUpper(method) {
|
|
|
|
|
|
case "GET":
|
|
|
|
|
|
return "\033[34m" // Синий
|
|
|
|
|
|
case "POST":
|
|
|
|
|
|
return "\033[32m" // Зеленый
|
|
|
|
|
|
case "PUT":
|
|
|
|
|
|
return "\033[33m" // Желтый
|
|
|
|
|
|
case "DELETE":
|
|
|
|
|
|
return "\033[31m" // Красный
|
|
|
|
|
|
case "PATCH":
|
|
|
|
|
|
return "\033[36m" // Циан
|
|
|
|
|
|
default:
|
|
|
|
|
|
return "\033[37m" // Белый
|
|
|
|
|
|
}
|
2025-09-28 11:46:20 +00:00
|
|
|
|
}
|