Files
homelab-frontend/internal/observability/exporter.go
T

128 lines
4.5 KiB
Go
Raw Normal View History

package observability
import (
"fmt"
"strings"
)
// ExportPrometheus exports metrics in Prometheus text format.
func (m *Metrics) ExportPrometheus() string {
m.mu.RLock()
defer m.mu.RUnlock()
var sb strings.Builder
// Help and type for request_total counter
sb.WriteString("# HELP gateway_requests_total Total number of HTTP requests\n")
sb.WriteString("# TYPE gateway_requests_total counter\n")
for key, count := range m.requestTotal {
parts := strings.Split(key, ":")
if len(parts) == 3 {
route, upstream, status := parts[0], parts[1], parts[2]
sb.WriteString(fmt.Sprintf("gateway_requests_total{route=\"%s\",upstream=\"%s\",status=\"%s\"} %d\n",
route, upstream, status, count))
}
}
sb.WriteString("\n")
// Help and type for request_duration_seconds histogram
sb.WriteString("# HELP gateway_request_duration_seconds Request latency in seconds\n")
sb.WriteString("# TYPE gateway_request_duration_seconds histogram\n")
buckets := []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}
for key := range m.requestDurationBuckets {
parts := strings.Split(key, ":")
if len(parts) == 2 {
route, upstream := parts[0], parts[1]
// Write buckets
cumulativeCount := int64(0)
for _, bucket := range buckets {
if count, ok := m.requestDurationBuckets[key][bucket]; ok {
cumulativeCount += count
}
sb.WriteString(fmt.Sprintf("gateway_request_duration_seconds_bucket{route=\"%s\",upstream=\"%s\",le=\"%g\"} %d\n",
route, upstream, bucket, cumulativeCount))
}
// Write +Inf bucket
totalCount := int64(0)
for _, count := range m.requestDurationBuckets[key] {
totalCount += count
}
sb.WriteString(fmt.Sprintf("gateway_request_duration_seconds_bucket{route=\"%s\",upstream=\"%s\",le=\"+Inf\"} %d\n",
route, upstream, totalCount))
// Write sum
totalDuration := m.requestDuration[key]
sb.WriteString(fmt.Sprintf("gateway_request_duration_seconds_sum{route=\"%s\",upstream=\"%s\"} %g\n",
route, upstream, float64(totalDuration)/1000.0)) // convert ms to seconds
// Write count
sb.WriteString(fmt.Sprintf("gateway_request_duration_seconds_count{route=\"%s\",upstream=\"%s\"} %d\n",
route, upstream, totalCount))
}
}
sb.WriteString("\n")
// Help and type for gateway_bytes_in counter
sb.WriteString("# HELP gateway_bytes_in_total Total bytes received from clients\n")
sb.WriteString("# TYPE gateway_bytes_in_total counter\n")
for key, count := range m.bytesIn {
parts := strings.Split(key, ":")
if len(parts) == 2 {
route, upstream := parts[0], parts[1]
sb.WriteString(fmt.Sprintf("gateway_bytes_in_total{route=\"%s\",upstream=\"%s\"} %d\n",
route, upstream, count))
}
}
sb.WriteString("\n")
// Help and type for gateway_bytes_out counter
sb.WriteString("# HELP gateway_bytes_out_total Total bytes sent to clients\n")
sb.WriteString("# TYPE gateway_bytes_out_total counter\n")
for key, count := range m.bytesOut {
parts := strings.Split(key, ":")
if len(parts) == 2 {
route, upstream := parts[0], parts[1]
sb.WriteString(fmt.Sprintf("gateway_bytes_out_total{route=\"%s\",upstream=\"%s\"} %d\n",
route, upstream, count))
}
}
sb.WriteString("\n")
// Help and type for upstream_health gauge
sb.WriteString("# HELP gateway_upstream_health Upstream health status (1=healthy, 0=unhealthy)\n")
sb.WriteString("# TYPE gateway_upstream_health gauge\n")
for upstream, health := range m.upstreamHealth {
sb.WriteString(fmt.Sprintf("gateway_upstream_health{upstream=\"%s\"} %d\n", upstream, health))
}
sb.WriteString("\n")
// Help and type for streaming responses
sb.WriteString("# HELP gateway_streaming_responses_total Total streaming responses\n")
sb.WriteString("# TYPE gateway_streaming_responses_total counter\n")
for key, count := range m.streamingResponsesTotal {
parts := strings.Split(key, ":")
if len(parts) == 2 {
route, upstream := parts[0], parts[1]
sb.WriteString(fmt.Sprintf("gateway_streaming_responses_total{route=\"%s\",upstream=\"%s\"} %d\n",
route, upstream, count))
}
}
sb.WriteString("\n")
// Help and type for streaming byte count
sb.WriteString("# HELP gateway_streaming_bytes_total Total bytes in streaming responses\n")
sb.WriteString("# TYPE gateway_streaming_bytes_total counter\n")
for key, count := range m.streamingByteCount {
parts := strings.Split(key, ":")
if len(parts) == 2 {
route, upstream := parts[0], parts[1]
sb.WriteString(fmt.Sprintf("gateway_streaming_bytes_total{route=\"%s\",upstream=\"%s\"} %d\n",
route, upstream, count))
}
}
return sb.String()
}