Files
telecom-platform/apps/api-server/internal/middleware/performance.go
T
nutcas3 7ee875c544 feat: Remove ETag generation and validation middleware, simplify CacheMiddleware to basic Cache-Control header
- Remove ResponseWriterWrapper struct with Write/Body methods for response body capture
- Remove ETagData/ETagGenerator structs and all ETag generation/validation/caching logic
- Remove generateETag/generateContentETag/generateWeakETag/etagMatches helper functions
- Remove ETagValidationMiddleware/ContentBasedETagMiddleware/CacheInvalidationMiddleware/AdvancedCacheMiddleware handlers
2026-04-24 07:33:07 +03:00

275 lines
7.7 KiB
Go

package middleware
import (
"context"
"fmt"
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// PerformanceMetrics tracks performance statistics
type PerformanceMetrics struct {
mu sync.RWMutex
requestCount int64
totalResponseTime time.Duration
slowRequests int64
fastRequests int64
errorRequests int64
}
// PerformanceMiddleware tracks and optimizes API performance
type PerformanceMiddleware struct {
metrics *PerformanceMetrics
slowThreshold time.Duration
}
// NewPerformanceMiddleware creates a new performance middleware
func NewPerformanceMiddleware(slowThreshold time.Duration) *PerformanceMiddleware {
return &PerformanceMiddleware{
metrics: &PerformanceMetrics{},
slowThreshold: slowThreshold,
}
}
// Middleware returns the gin.HandlerFunc
func (pm *PerformanceMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// Process request
c.Next()
// Calculate response time
responseTime := time.Since(start)
// Update metrics
pm.updateMetrics(c.Writer.Status(), responseTime)
// Add performance headers
c.Header("X-Response-Time", responseTime.String())
c.Header("X-Request-ID", c.GetHeader("X-Request-ID"))
// Log slow requests
if responseTime > pm.slowThreshold {
pm.logSlowRequest(c, responseTime)
}
}
}
// updateMetrics updates the performance metrics
func (pm *PerformanceMiddleware) updateMetrics(statusCode int, responseTime time.Duration) {
pm.metrics.mu.Lock()
defer pm.metrics.mu.Unlock()
pm.metrics.requestCount++
pm.metrics.totalResponseTime += responseTime
if responseTime > pm.slowThreshold {
pm.metrics.slowRequests++
} else {
pm.metrics.fastRequests++
}
if statusCode >= 400 {
pm.metrics.errorRequests++
}
}
// logSlowRequest logs slow requests for monitoring
func (pm *PerformanceMiddleware) logSlowRequest(c *gin.Context, responseTime time.Duration) {
// In production, this would go to a structured logging system
// For now, we'll use the standard logger
gin.DefaultWriter.Write(fmt.Appendf(nil,
"[SLOW REQUEST] %s %s - %v - Status: %d\n",
c.Request.Method,
c.Request.URL.Path,
responseTime,
c.Writer.Status(),
))
}
// GetMetrics returns current performance metrics
func (pm *PerformanceMiddleware) GetMetrics() PerformanceMetrics {
pm.metrics.mu.RLock()
defer pm.metrics.mu.RUnlock()
return PerformanceMetrics{
requestCount: pm.metrics.requestCount,
totalResponseTime: pm.metrics.totalResponseTime,
slowRequests: pm.metrics.slowRequests,
fastRequests: pm.metrics.fastRequests,
errorRequests: pm.metrics.errorRequests,
}
}
// GetAverageResponseTime returns the average response time
func (pm *PerformanceMiddleware) GetAverageResponseTime() time.Duration {
pm.metrics.mu.RLock()
defer pm.metrics.mu.RUnlock()
if pm.metrics.requestCount == 0 {
return 0
}
return pm.metrics.totalResponseTime / time.Duration(pm.metrics.requestCount)
}
// GetSlowRequestPercentage returns the percentage of slow requests
func (pm *PerformanceMiddleware) GetSlowRequestPercentage() float64 {
pm.metrics.mu.RLock()
defer pm.metrics.mu.RUnlock()
if pm.metrics.requestCount == 0 {
return 0
}
return float64(pm.metrics.slowRequests) / float64(pm.metrics.requestCount) * 100
}
// GetErrorRate returns the error rate percentage
func (pm *PerformanceMiddleware) GetErrorRate() float64 {
pm.metrics.mu.RLock()
defer pm.metrics.mu.RUnlock()
if pm.metrics.requestCount == 0 {
return 0
}
return float64(pm.metrics.errorRequests) / float64(pm.metrics.requestCount) * 100
}
// ResetMetrics resets all performance metrics
func (pm *PerformanceMiddleware) ResetMetrics() {
pm.metrics.mu.Lock()
defer pm.metrics.mu.Unlock()
pm.metrics.requestCount = 0
pm.metrics.totalResponseTime = 0
pm.metrics.slowRequests = 0
pm.metrics.fastRequests = 0
pm.metrics.errorRequests = 0
}
// Global performance middleware instance
var globalPerformanceMiddleware *PerformanceMiddleware
// InitializePerformanceMiddleware initializes the global performance middleware
func InitializePerformanceMiddleware(slowThreshold time.Duration) {
globalPerformanceMiddleware = NewPerformanceMiddleware(slowThreshold)
}
// GetPerformanceMiddleware returns the global performance middleware
func GetPerformanceMiddleware() *PerformanceMiddleware {
return globalPerformanceMiddleware
}
// PerformanceMiddlewareHandler returns a gin.HandlerFunc for performance tracking
func PerformanceMiddlewareHandler() gin.HandlerFunc {
if globalPerformanceMiddleware == nil {
// Initialize with default threshold if not already initialized
InitializePerformanceMiddleware(100 * time.Millisecond)
}
return globalPerformanceMiddleware.Middleware()
}
// PerformanceHandler returns performance metrics as JSON
func PerformanceHandler() gin.HandlerFunc {
return func(c *gin.Context) {
if globalPerformanceMiddleware == nil {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Performance middleware not initialized",
})
return
}
metrics := globalPerformanceMiddleware.GetMetrics()
avgResponseTime := globalPerformanceMiddleware.GetAverageResponseTime()
slowRequestPercentage := globalPerformanceMiddleware.GetSlowRequestPercentage()
errorRate := globalPerformanceMiddleware.GetErrorRate()
c.JSON(http.StatusOK, gin.H{
"metrics": gin.H{
"request_count": metrics.requestCount,
"total_response_time": metrics.totalResponseTime.String(),
"slow_requests": metrics.slowRequests,
"fast_requests": metrics.fastRequests,
"error_requests": metrics.errorRequests,
},
"performance": gin.H{
"average_response_time_ms": avgResponseTime.Milliseconds(),
"slow_request_percentage": slowRequestPercentage,
"error_rate_percentage": errorRate,
"slow_threshold_ms": globalPerformanceMiddleware.slowThreshold.Milliseconds(),
},
})
}
}
// RequestSizeMiddleware limits request size for performance
func RequestSizeMiddleware(maxSize int64) gin.HandlerFunc {
return func(c *gin.Context) {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxSize)
c.Next()
}
}
// CompressionMiddleware enables gzip compression for better performance
func CompressionMiddleware() gin.HandlerFunc {
// Use gin's built-in compression
return func(c *gin.Context) {
c.Header("Content-Encoding", "gzip")
c.Next()
}
}
// CacheMiddleware adds basic caching headers for GET requests
func CacheMiddleware(duration time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Method == "GET" {
c.Header("Cache-Control", "public, max-age="+duration.String())
}
c.Next()
}
}
// TimeoutMiddleware adds timeout to requests
func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
defer cancel()
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
// SecurityHeadersMiddleware adds security headers for better performance
func SecurityHeadersMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-Frame-Options", "DENY")
c.Header("X-XSS-Protection", "1; mode=block")
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
c.Next()
}
}
// CORSMiddleware handles CORS for better performance
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Header("Access-Control-Max-Age", "86400")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}