254 lines
6.2 KiB
Go
254 lines
6.2 KiB
Go
package middleware
|
||
|
||
import (
|
||
"net/http"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/prometheus/client_golang/prometheus"
|
||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||
)
|
||
|
||
var (
|
||
// HTTP метрики
|
||
httpRequestsTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "http_requests_total",
|
||
Help: "Total number of HTTP requests",
|
||
},
|
||
[]string{"method", "path", "status", "handler"},
|
||
)
|
||
|
||
httpRequestDuration = promauto.NewHistogramVec(
|
||
prometheus.HistogramOpts{
|
||
Name: "http_request_duration_seconds",
|
||
Help: "Duration of HTTP requests",
|
||
Buckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 2.5, 5, 10},
|
||
},
|
||
[]string{"method", "path", "handler"},
|
||
)
|
||
|
||
// GraphQL специфичные метрики
|
||
gqlOperationsTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "graphql_operations_total",
|
||
Help: "Total number of GraphQL operations",
|
||
},
|
||
[]string{"operation", "type", "name", "success"},
|
||
)
|
||
|
||
gqlOperationDuration = promauto.NewHistogramVec(
|
||
prometheus.HistogramOpts{
|
||
Name: "graphql_operation_duration_seconds",
|
||
Help: "Duration of GraphQL operations",
|
||
Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2, 5},
|
||
},
|
||
[]string{"operation", "name"},
|
||
)
|
||
|
||
// Бизнес метрики
|
||
usersTotal = promauto.NewGauge(
|
||
prometheus.GaugeOpts{
|
||
Name: "users_total",
|
||
Help: "Total number of registered users",
|
||
},
|
||
)
|
||
|
||
postsTotal = promauto.NewGauge(
|
||
prometheus.GaugeOpts{
|
||
Name: "posts_total",
|
||
Help: "Total number of posts",
|
||
},
|
||
)
|
||
|
||
commentsTotal = promauto.NewGauge(
|
||
prometheus.GaugeOpts{
|
||
Name: "comments_total",
|
||
Help: "Total number of comments",
|
||
},
|
||
)
|
||
|
||
messagesTotal = promauto.NewGauge(
|
||
prometheus.GaugeOpts{
|
||
Name: "messages_total",
|
||
Help: "Total number of messages",
|
||
},
|
||
)
|
||
|
||
activeWebsockets = promauto.NewGauge(
|
||
prometheus.GaugeOpts{
|
||
Name: "websocket_connections_active",
|
||
Help: "Number of active WebSocket connections",
|
||
},
|
||
)
|
||
|
||
websocketMessagesTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "websocket_messages_total",
|
||
Help: "Total number of WebSocket messages",
|
||
},
|
||
[]string{"direction", "type"},
|
||
)
|
||
|
||
// Метрики ошибок
|
||
errorsTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "errors_total",
|
||
Help: "Total number of errors by type",
|
||
},
|
||
[]string{"type", "source", "severity"},
|
||
)
|
||
|
||
// Метрики базы данных
|
||
dbQueriesTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "db_queries_total",
|
||
Help: "Total number of database queries",
|
||
},
|
||
[]string{"operation", "table", "success"},
|
||
)
|
||
|
||
dbQueryDuration = promauto.NewHistogramVec(
|
||
prometheus.HistogramOpts{
|
||
Name: "db_query_duration_seconds",
|
||
Help: "Duration of database queries",
|
||
Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2},
|
||
},
|
||
[]string{"operation", "table"},
|
||
)
|
||
|
||
// Метрики кэша
|
||
cacheHitsTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "cache_hits_total",
|
||
Help: "Total number of cache hits",
|
||
},
|
||
[]string{"type", "name"},
|
||
)
|
||
|
||
cacheMissesTotal = promauto.NewCounterVec(
|
||
prometheus.CounterOpts{
|
||
Name: "cache_misses_total",
|
||
Help: "Total number of cache misses",
|
||
},
|
||
[]string{"type", "name"},
|
||
)
|
||
)
|
||
|
||
// MetricsMiddleware собирает метрики для Prometheus
|
||
func MetricsMiddleware(next http.Handler) http.Handler {
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
start := time.Now()
|
||
rw := &responseWriter{w, http.StatusOK, 0}
|
||
|
||
next.ServeHTTP(rw, r)
|
||
|
||
duration := time.Since(start).Seconds()
|
||
status := strconv.Itoa(rw.status)
|
||
|
||
// Определяем handler type
|
||
handlerType := "http"
|
||
if r.URL.Path == "/query" {
|
||
handlerType = "graphql"
|
||
} else if r.URL.Path == "/ws" {
|
||
handlerType = "websocket"
|
||
}
|
||
|
||
httpRequestsTotal.WithLabelValues(
|
||
r.Method,
|
||
r.URL.Path,
|
||
status,
|
||
handlerType,
|
||
).Inc()
|
||
|
||
httpRequestDuration.WithLabelValues(
|
||
r.Method,
|
||
r.URL.Path,
|
||
handlerType,
|
||
).Observe(duration)
|
||
})
|
||
}
|
||
|
||
// GraphQLMetricsMiddleware для отслеживания GraphQL операций
|
||
func GraphQLMetricsMiddleware() func(http.Handler) http.Handler {
|
||
return func(next http.Handler) http.Handler {
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
if r.URL.Path != "/query" {
|
||
next.ServeHTTP(w, r)
|
||
return
|
||
}
|
||
|
||
// Здесь можно парсить GraphQL запрос и извлекать информацию об операции
|
||
// Для простоты пока пропускаем
|
||
next.ServeHTTP(w, r)
|
||
})
|
||
}
|
||
}
|
||
|
||
// Вспомогательные функции для обновления метрик
|
||
|
||
func IncGQLOperation(operationType, operationName string, success bool, duration time.Duration) {
|
||
status := "false"
|
||
if success {
|
||
status = "true"
|
||
}
|
||
|
||
gqlOperationsTotal.WithLabelValues(
|
||
operationType, // operation
|
||
operationName, // type (возможно нужно переименовать)
|
||
operationName, // name (дублирование)
|
||
status, // success
|
||
).Inc()
|
||
|
||
gqlOperationDuration.WithLabelValues(
|
||
operationType,
|
||
operationName,
|
||
).Observe(duration.Seconds())
|
||
}
|
||
|
||
func IncWebSocketMessage(direction, messageType string) {
|
||
websocketMessagesTotal.WithLabelValues(direction, messageType).Inc()
|
||
}
|
||
|
||
func SetActiveWebsockets(count int) {
|
||
activeWebsockets.Set(float64(count))
|
||
}
|
||
|
||
func IncError(errorType, source, severity string) {
|
||
errorsTotal.WithLabelValues(errorType, source, severity).Inc()
|
||
}
|
||
|
||
func IncDBQuery(operation, table string, success bool, duration time.Duration) {
|
||
status := "false"
|
||
if success {
|
||
status = "true"
|
||
}
|
||
|
||
dbQueriesTotal.WithLabelValues(operation, table, status).Inc()
|
||
dbQueryDuration.WithLabelValues(operation, table).Observe(duration.Seconds())
|
||
}
|
||
|
||
func IncCacheHit(cacheType, cacheName string) {
|
||
cacheHitsTotal.WithLabelValues(cacheType, cacheName).Inc()
|
||
}
|
||
|
||
func IncCacheMiss(cacheType, cacheName string) {
|
||
cacheMissesTotal.WithLabelValues(cacheType, cacheName).Inc()
|
||
}
|
||
|
||
func SetUsersCount(count int) {
|
||
usersTotal.Set(float64(count))
|
||
}
|
||
|
||
func SetPostsCount(count int) {
|
||
postsTotal.Set(float64(count))
|
||
}
|
||
|
||
func SetCommentsCount(count int) {
|
||
commentsTotal.Set(float64(count))
|
||
}
|
||
|
||
func SetMessagesCount(count int) {
|
||
messagesTotal.Set(float64(count))
|
||
}
|