mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
refactor: metrics integration with caddy/admin.go
This commit is contained in:
parent
f259ed52bb
commit
755c22af13
2 changed files with 53 additions and 62 deletions
65
admin.go
65
admin.go
|
@ -43,7 +43,6 @@ import (
|
|||
|
||||
"github.com/caddyserver/caddy/v2/notify"
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
@ -209,53 +208,26 @@ func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool) admi
|
|||
muxWrap.enforceOrigin = admin.EnforceOrigin
|
||||
}
|
||||
|
||||
addRouteWithMetrics := func(pattern string, handlerLabel string, h http.Handler) {
|
||||
labels := prometheus.Labels{"path": pattern, "handler": handlerLabel}
|
||||
h = instrumentHandlerCounter(
|
||||
adminMetrics.requestCount.MustCurryWith(labels),
|
||||
h,
|
||||
)
|
||||
muxWrap.mux.Handle(pattern, h)
|
||||
}
|
||||
// addRoute just calls muxWrap.mux.Handle after
|
||||
// wrapping the handler with error handling
|
||||
addRoute := func(pattern string, handlerLabel string, h AdminHandler) {
|
||||
wrapper := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
err := h.ServeHTTP(w, r)
|
||||
if err != nil {
|
||||
labels := prometheus.Labels{
|
||||
"path": pattern,
|
||||
"handler": handlerLabel,
|
||||
"method": strings.ToUpper(r.Method),
|
||||
}
|
||||
adminMetrics.requestErrors.With(labels).Inc()
|
||||
}
|
||||
muxWrap.handleError(w, r, err)
|
||||
})
|
||||
addRouteWithMetrics(pattern, handlerLabel, wrapper)
|
||||
}
|
||||
|
||||
const handlerLabel = "admin"
|
||||
|
||||
// register standard config control endpoints
|
||||
addRoute("/"+rawConfigKey+"/", handlerLabel, AdminHandlerFunc(handleConfig))
|
||||
addRoute("/id/", handlerLabel, AdminHandlerFunc(handleConfigID))
|
||||
addRoute("/stop", handlerLabel, AdminHandlerFunc(handleStop))
|
||||
muxWrap.Handle("/"+rawConfigKey+"/", handlerLabel, AdminHandlerFunc(handleConfig))
|
||||
muxWrap.Handle("/id/", handlerLabel, AdminHandlerFunc(handleConfigID))
|
||||
muxWrap.Handle("/stop", handlerLabel, AdminHandlerFunc(handleStop))
|
||||
|
||||
// register debugging endpoints
|
||||
addRouteWithMetrics("/debug/pprof/", handlerLabel, http.HandlerFunc(pprof.Index))
|
||||
addRouteWithMetrics("/debug/pprof/cmdline", handlerLabel, http.HandlerFunc(pprof.Cmdline))
|
||||
addRouteWithMetrics("/debug/pprof/profile", handlerLabel, http.HandlerFunc(pprof.Profile))
|
||||
addRouteWithMetrics("/debug/pprof/symbol", handlerLabel, http.HandlerFunc(pprof.Symbol))
|
||||
addRouteWithMetrics("/debug/pprof/trace", handlerLabel, http.HandlerFunc(pprof.Trace))
|
||||
addRouteWithMetrics("/debug/vars", handlerLabel, expvar.Handler())
|
||||
muxWrap.HandleStd("/debug/pprof/", handlerLabel, http.HandlerFunc(pprof.Index))
|
||||
muxWrap.HandleStd("/debug/pprof/cmdline", handlerLabel, http.HandlerFunc(pprof.Cmdline))
|
||||
muxWrap.HandleStd("/debug/pprof/profile", handlerLabel, http.HandlerFunc(pprof.Profile))
|
||||
muxWrap.HandleStd("/debug/pprof/symbol", handlerLabel, http.HandlerFunc(pprof.Symbol))
|
||||
muxWrap.HandleStd("/debug/pprof/trace", handlerLabel, http.HandlerFunc(pprof.Trace))
|
||||
muxWrap.HandleStd("/debug/vars", handlerLabel, expvar.Handler())
|
||||
|
||||
// register third-party module endpoints
|
||||
for _, m := range GetModules("admin.api") {
|
||||
router := m.New().(AdminRouter)
|
||||
handlerLabel := m.ID.Name()
|
||||
for _, route := range router.Routes() {
|
||||
addRoute(route.Pattern, handlerLabel, route.Handler)
|
||||
muxWrap.Handle(route.Pattern, handlerLabel, route.Handler)
|
||||
}
|
||||
admin.routers = append(admin.routers, router)
|
||||
}
|
||||
|
@ -712,6 +684,21 @@ type adminHandler struct {
|
|||
remoteControl *RemoteAdmin
|
||||
}
|
||||
|
||||
// Handle registers an AdminHandler-type handler for the given pattern.
|
||||
func (h adminHandler) Handle(pattern, label string, handler AdminHandler) {
|
||||
h.mux.Handle(pattern, instrumentAdminHandler(pattern, label, handler, h.handleError))
|
||||
}
|
||||
|
||||
// HandleStd registers an http.Handler-type handler for the given pattern.
|
||||
func (h adminHandler) HandleStd(pattern, label string, handler http.Handler) {
|
||||
h.Handle(pattern, label, AdminHandlerFunc(func(
|
||||
w http.ResponseWriter, r *http.Request,
|
||||
) error {
|
||||
handler.ServeHTTP(w, r)
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
// ServeHTTP is the external entry point for API requests.
|
||||
// It will only be called once per request.
|
||||
func (h adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -1294,7 +1281,7 @@ var (
|
|||
// will get deleted before the process gracefully exits.
|
||||
func PIDFile(filename string) error {
|
||||
pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
|
||||
err := os.WriteFile(filename, pid, 0600)
|
||||
err := os.WriteFile(filename, pid, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
50
metrics.go
50
metrics.go
|
@ -3,7 +3,7 @@ package caddy
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/internal/metrics"
|
||||
internal "github.com/caddyserver/caddy/v2/internal/metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
|
@ -15,13 +15,13 @@ func init() {
|
|||
|
||||
const ns, sub = "caddy", "admin"
|
||||
|
||||
adminMetrics.requestCount = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
adminMetrics.requests = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns,
|
||||
Subsystem: sub,
|
||||
Name: "http_requests_total",
|
||||
Help: "Counter of requests made to the Admin API's HTTP endpoints.",
|
||||
}, []string{"handler", "path", "code", "method"})
|
||||
adminMetrics.requestErrors = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
adminMetrics.errors = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns,
|
||||
Subsystem: sub,
|
||||
Name: "http_request_errors_total",
|
||||
|
@ -31,29 +31,33 @@ func init() {
|
|||
|
||||
// adminMetrics is a collection of metrics that can be tracked for the admin API.
|
||||
var adminMetrics = struct {
|
||||
requestCount *prometheus.CounterVec
|
||||
requestErrors *prometheus.CounterVec
|
||||
requests *prometheus.CounterVec
|
||||
errors *prometheus.CounterVec
|
||||
}{}
|
||||
|
||||
// Similar to promhttp.InstrumentHandlerCounter, but upper-cases method names
|
||||
// instead of lower-casing them.
|
||||
//
|
||||
// Unlike promhttp.InstrumentHandlerCounter, this assumes a "code" and "method"
|
||||
// label is present, and will panic otherwise.
|
||||
func instrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
d := newDelegator(w)
|
||||
next.ServeHTTP(d, r)
|
||||
counter.With(prometheus.Labels{
|
||||
"code": metrics.SanitizeCode(d.status),
|
||||
"method": metrics.SanitizeMethod(r.Method),
|
||||
}).Inc()
|
||||
})
|
||||
}
|
||||
// instrumentAdminHandler wraps the handler with total and errored-out request count
|
||||
// in a manner similar to promhttp.InstrumentHandlerCounter. All errors are handled
|
||||
// using the passed error handler.
|
||||
func instrumentAdminHandler(pattern, handlerLabel string,
|
||||
h AdminHandler, errorHandler func(http.ResponseWriter, *http.Request, error),
|
||||
) http.HandlerFunc {
|
||||
labels := prometheus.Labels{"path": pattern, "handler": handlerLabel}
|
||||
requests := adminMetrics.requests.MustCurryWith(labels)
|
||||
errors := adminMetrics.errors.MustCurryWith(labels)
|
||||
|
||||
func newDelegator(w http.ResponseWriter) *delegator {
|
||||
return &delegator{
|
||||
ResponseWriter: w,
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
d := delegator{ResponseWriter: w}
|
||||
labels := prometheus.Labels{
|
||||
"method": internal.SanitizeMethod(r.Method),
|
||||
}
|
||||
|
||||
if err := h.ServeHTTP(w, r); err != nil {
|
||||
errors.With(labels).Inc()
|
||||
errorHandler(w, r, err)
|
||||
}
|
||||
|
||||
labels["code"] = internal.SanitizeCode(d.status)
|
||||
requests.With(labels).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue