package metrics import ( "fmt" "net/http" "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) var ( // Default metrics requestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "Duration of HTTP requests in seconds", Buckets: prometheus.DefBuckets, }, []string{"method", "path", "status"}, ) requestTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests", }, []string{"method", "path", "status"}, ) otpGenerationTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "otp_generation_total", Help: "Total number of OTP generations", }, []string{"user_id", "otp_id"}, ) otpVerificationTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "otp_verification_total", Help: "Total number of OTP verifications", }, []string{"user_id", "otp_id", "success"}, ) activeUsers = prometheus.NewGauge( prometheus.GaugeOpts{ Name: "active_users", Help: "Number of active users", }, ) cacheHits = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "cache_hits_total", Help: "Total number of cache hits", }, []string{"cache"}, ) cacheMisses = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "cache_misses_total", Help: "Total number of cache misses", }, []string{"cache"}, ) ) func init() { // Register metrics with prometheus prometheus.MustRegister( requestDuration, requestTotal, otpGenerationTotal, otpVerificationTotal, activeUsers, cacheHits, cacheMisses, ) } // MetricsService provides metrics functionality type MetricsService struct { activeUsersMutex sync.RWMutex activeUserIDs map[string]bool } // NewMetricsService creates a new MetricsService func NewMetricsService() *MetricsService { return &MetricsService{ activeUserIDs: make(map[string]bool), } } // Handler returns an HTTP handler for metrics func (s *MetricsService) Handler() http.Handler { return promhttp.Handler() } // RecordRequest records metrics for an HTTP request func (s *MetricsService) RecordRequest(method, path string, status int, duration time.Duration) { labels := prometheus.Labels{ "method": method, "path": path, "status": fmt.Sprintf("%d", status), } requestDuration.With(labels).Observe(duration.Seconds()) requestTotal.With(labels).Inc() } // RecordOTPGeneration records metrics for OTP generation func (s *MetricsService) RecordOTPGeneration(userID, otpID string) { otpGenerationTotal.With(prometheus.Labels{ "user_id": userID, "otp_id": otpID, }).Inc() } // RecordOTPVerification records metrics for OTP verification func (s *MetricsService) RecordOTPVerification(userID, otpID string, success bool) { otpVerificationTotal.With(prometheus.Labels{ "user_id": userID, "otp_id": otpID, "success": fmt.Sprintf("%t", success), }).Inc() } // RecordUserActivity records user activity func (s *MetricsService) RecordUserActivity(userID string) { s.activeUsersMutex.Lock() defer s.activeUsersMutex.Unlock() if !s.activeUserIDs[userID] { s.activeUserIDs[userID] = true activeUsers.Inc() } } // RecordUserInactivity records user inactivity func (s *MetricsService) RecordUserInactivity(userID string) { s.activeUsersMutex.Lock() defer s.activeUsersMutex.Unlock() if s.activeUserIDs[userID] { delete(s.activeUserIDs, userID) activeUsers.Dec() } } // RecordCacheHit records a cache hit func (s *MetricsService) RecordCacheHit(cache string) { cacheHits.With(prometheus.Labels{ "cache": cache, }).Inc() } // RecordCacheMiss records a cache miss func (s *MetricsService) RecordCacheMiss(cache string) { cacheMisses.With(prometheus.Labels{ "cache": cache, }).Inc() } // Middleware creates a middleware that records request metrics func (s *MetricsService) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // Create response writer that captures status code rw := &responseWriter{ResponseWriter: w, status: http.StatusOK} // Call next handler next.ServeHTTP(rw, r) // Record metrics s.RecordRequest(r.Method, r.URL.Path, rw.status, time.Since(start)) }) } // responseWriter wraps http.ResponseWriter to capture status code type responseWriter struct { http.ResponseWriter status int } func (rw *responseWriter) WriteHeader(code int) { rw.status = code rw.ResponseWriter.WriteHeader(code) }