71 lines
1.9 KiB
Go
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
|
|
}
|
|
}
|