package handler import ( "fmt" "net/http" "runtime" "time" "github.com/gin-gonic/gin" ) // MetricsHandler exposes Prometheus-compatible metrics in text exposition format. // Reference: Chatwoot uses Prometheus exporter for Sidekiq, Rails metrics // This provides Go runtime + application metrics on a dedicated port. // PrometheusHandler returns metrics in Prometheus text format. func PrometheusHandler(startTime time.Time) gin.HandlerFunc { return func(c *gin.Context) { var m runtime.MemStats runtime.ReadMemStats(&m) uptime := time.Since(startTime).Seconds() metrics := []string{ // Go runtime metrics formatGauge("gochat_go_goroutines", uint64(runtime.NumGoroutine())), formatGauge("gochat_go_memory_alloc_bytes", m.Alloc), formatGauge("gochat_go_memory_sys_bytes", m.Sys), formatGauge("gochat_go_memory_total_alloc_bytes", m.TotalAlloc), formatGauge("gochat_go_gc_pause_total_ns", m.PauseTotalNs), formatCounter("gochat_go_gc_count", uint64(m.NumGC)), // Application metrics formatGauge("gochat_uptime_seconds", uint64(uptime)), formatGauge("gochat_threads_count", uint64(runtime.NumCPU())), } // HELP and TYPE annotations help := []string{ "# HELP gochat_go_goroutines Number of goroutines currently running", "# TYPE gochat_go_goroutines gauge", "# HELP gochat_go_memory_alloc_bytes Bytes of allocated heap objects", "# TYPE gochat_go_memory_alloc_bytes gauge", "# HELP gochat_go_memory_sys_bytes Bytes obtained from system", "# TYPE gochat_go_memory_sys_bytes gauge", "# HELP gochat_uptime_seconds Application uptime in seconds", "# TYPE gochat_uptime_seconds gauge", } output := "" for _, h := range help { output += h + "\n" } for _, m := range metrics { output += m + "\n" } c.Header("Content-Type", "text/plain; version=0.0.4; charset=utf-8") c.String(http.StatusOK, output) } } func formatGauge(name string, value uint64) string { return name + " " + formatValue(value) } func formatCounter(name string, value uint64) string { return name + " " + formatValue(value) } func formatValue(v uint64) string { // Simple uint64 formatting without strconv dependency if v == 0 { return "0" } result := "" for v > 0 { digit := v % 10 result = fmt.Sprintf("%d%s", digit, result) v /= 10 } return result }