proxy-pool/internal/platform/metrics/handler.go
2026-07-30 12:30:42 +08:00

71 lines
1.9 KiB
Go

package metrics
import (
"context"
"errors"
"net/http"
"reflect"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var ErrInvalidDependencies = errors.New("invalid metrics dependencies")
type ReadinessChecker interface {
Ready(context.Context) error
}
type Dependencies struct {
Gatherer prometheus.Gatherer
Readiness ReadinessChecker
}
func NewHandler(dependencies Dependencies) (http.Handler, error) {
if nilInterface(dependencies.Gatherer) || nilInterface(dependencies.Readiness) {
return nil, ErrInvalidDependencies
}
metricsHandler := promhttp.HandlerFor(dependencies.Gatherer, promhttp.HandlerOpts{})
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
response.Header().Set("Allow", http.MethodGet)
http.Error(response, "method not allowed", http.StatusMethodNotAllowed)
return
}
switch request.URL.Path {
case "/livez":
writeStatus(response, http.StatusOK, "live")
case "/readyz":
if err := dependencies.Readiness.Ready(request.Context()); err != nil {
writeStatus(response, http.StatusServiceUnavailable, "unavailable")
return
}
writeStatus(response, http.StatusOK, "ready")
case "/metrics":
metricsHandler.ServeHTTP(response, request)
default:
http.NotFound(response, request)
}
}), nil
}
func writeStatus(response http.ResponseWriter, status int, value string) {
response.Header().Set("Content-Type", "text/plain; charset=utf-8")
response.Header().Set("X-Content-Type-Options", "nosniff")
response.WriteHeader(status)
_, _ = response.Write([]byte(value + "\n"))
}
func nilInterface(value any) bool {
if value == nil {
return true
}
reflected := reflect.ValueOf(value)
switch reflected.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return reflected.IsNil()
default:
return false
}
}