93 lines
2.9 KiB
Go
93 lines
2.9 KiB
Go
// Package telemetry provides Prometheus metrics collection and monitoring functionality
|
|
// for the Bifrost HTTP service. This file contains the setup and configuration
|
|
// for Prometheus metrics collection, including HTTP middleware and metric definitions.
|
|
package telemetry
|
|
|
|
import (
|
|
"log"
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// getPrometheusLabelValues takes an array of expected label keys and a map of header values,
|
|
// and returns an array of values in the same order as the keys, using empty string for missing values.
|
|
func getPrometheusLabelValues(expectedLabels []string, headerValues map[string]string) []string {
|
|
values := make([]string, len(expectedLabels))
|
|
for i, label := range expectedLabels {
|
|
if value, exists := headerValues[label]; exists {
|
|
values[i] = value
|
|
} else {
|
|
values[i] = "" // Default empty value for missing labels
|
|
}
|
|
}
|
|
return values
|
|
}
|
|
|
|
// collectPrometheusKeyValues collects all metrics for a request including:
|
|
// - Default metrics (path, method, status, request size)
|
|
// - Custom prometheus headers (x-bf-prom-*)
|
|
// Returns a map of all label values
|
|
func collectPrometheusKeyValues(ctx *fasthttp.RequestCtx) map[string]string {
|
|
path := string(ctx.Path())
|
|
method := string(ctx.Method())
|
|
|
|
// Initialize with default metrics
|
|
labelValues := map[string]string{
|
|
"path": path,
|
|
"method": method,
|
|
}
|
|
|
|
// Collect custom prometheus headers
|
|
ctx.Request.Header.All()(func(key, value []byte) bool {
|
|
keyStr := strings.ToLower(string(key))
|
|
if strings.HasPrefix(keyStr, "x-bf-prom-") {
|
|
labelName := strings.TrimPrefix(keyStr, "x-bf-prom-")
|
|
labelValues[labelName] = string(value)
|
|
ctx.SetUserValue(keyStr, string(value))
|
|
}
|
|
return true
|
|
})
|
|
|
|
return labelValues
|
|
}
|
|
|
|
// safeObserve safely records a value in a Prometheus histogram.
|
|
// It prevents recording invalid values (negative or infinite) that could cause issues.
|
|
func safeObserve(histogram *prometheus.HistogramVec, value float64, labels ...string) {
|
|
if value > 0 && value < math.MaxFloat64 {
|
|
metric, err := histogram.GetMetricWithLabelValues(labels...)
|
|
if err != nil {
|
|
log.Printf("Error getting metric with label values: %v", err)
|
|
} else {
|
|
metric.Observe(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
// containsLabel checks if a string slice contains a specific label, ignoring differences
|
|
// between underscores and hyphens. It checks for:
|
|
// - Direct match
|
|
// - Match after removing underscores
|
|
// - Match after replacing hyphens with underscores
|
|
// - Match after replacing underscores with hyphens
|
|
func containsLabel(slice []string, label string) bool {
|
|
for _, s := range slice {
|
|
// Direct match
|
|
if s == label {
|
|
return true
|
|
}
|
|
// Match after replacing hyphens with underscores
|
|
if strings.ReplaceAll(s, "-", "_") == strings.ReplaceAll(label, "-", "_") {
|
|
return true
|
|
}
|
|
// Match after replacing underscores with hyphens
|
|
if strings.ReplaceAll(s, "_", "-") == strings.ReplaceAll(label, "_", "-") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|