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