1
Fork 0
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:
renbou 2022-07-08 00:59:53 +08:00
parent f259ed52bb
commit 755c22af13
No known key found for this signature in database
GPG key ID: C2781315C81C98DE
2 changed files with 53 additions and 62 deletions

View file

@ -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
}

View file

@ -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()
}
}