Installs a wrapper around HTTP endpoints that generate metrics.

This commit is contained in:
James Phillips 2016-08-09 15:41:15 -07:00
parent 3585b5a4cb
commit f00e49112a
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
1 changed files with 98 additions and 66 deletions

View File

@ -15,6 +15,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/armon/go-metrics"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/tlsutil" "github.com/hashicorp/consul/tlsutil"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -191,93 +192,124 @@ func (s *HTTPServer) Shutdown() {
} }
} }
// handleFuncMetrics takes the given pattern and handler and wraps to produce
// metrics based on the pattern and request.
func (s *HTTPServer) handleFuncMetrics(pattern string, handler func(http.ResponseWriter, *http.Request)) {
// Get the parts of the pattern. We omit any initial empty for the
// leading slash, and put an underscore as a "thing" placeholder if we
// see a trailing slash, which means the part after is parsed. This lets
// us distinguish from things like /v1/query and /v1/query/<query id>.
var parts []string
for i, part := range strings.Split(pattern, "/") {
if part == "" {
if i == 0 {
continue
} else {
part = "_"
}
}
parts = append(parts, part)
}
// Register the wrapper, which will close over the expensive-to-compute
// parts from above.
wrapper := func(resp http.ResponseWriter, req *http.Request) {
start := time.Now()
handler(resp, req)
key := append([]string{"consul", "http", req.Method}, parts...)
metrics.MeasureSince(key, start)
}
s.mux.HandleFunc(pattern, wrapper)
}
// registerHandlers is used to attach our handlers to the mux // registerHandlers is used to attach our handlers to the mux
func (s *HTTPServer) registerHandlers(enableDebug bool) { func (s *HTTPServer) registerHandlers(enableDebug bool) {
s.mux.HandleFunc("/", s.Index) s.mux.HandleFunc("/", s.Index)
s.mux.HandleFunc("/v1/status/leader", s.wrap(s.StatusLeader)) s.handleFuncMetrics("/v1/status/leader", s.wrap(s.StatusLeader))
s.mux.HandleFunc("/v1/status/peers", s.wrap(s.StatusPeers)) s.handleFuncMetrics("/v1/status/peers", s.wrap(s.StatusPeers))
s.mux.HandleFunc("/v1/catalog/register", s.wrap(s.CatalogRegister)) s.handleFuncMetrics("/v1/catalog/register", s.wrap(s.CatalogRegister))
s.mux.HandleFunc("/v1/catalog/deregister", s.wrap(s.CatalogDeregister)) s.handleFuncMetrics("/v1/catalog/deregister", s.wrap(s.CatalogDeregister))
s.mux.HandleFunc("/v1/catalog/datacenters", s.wrap(s.CatalogDatacenters)) s.handleFuncMetrics("/v1/catalog/datacenters", s.wrap(s.CatalogDatacenters))
s.mux.HandleFunc("/v1/catalog/nodes", s.wrap(s.CatalogNodes)) s.handleFuncMetrics("/v1/catalog/nodes", s.wrap(s.CatalogNodes))
s.mux.HandleFunc("/v1/catalog/services", s.wrap(s.CatalogServices)) s.handleFuncMetrics("/v1/catalog/services", s.wrap(s.CatalogServices))
s.mux.HandleFunc("/v1/catalog/service/", s.wrap(s.CatalogServiceNodes)) s.handleFuncMetrics("/v1/catalog/service/", s.wrap(s.CatalogServiceNodes))
s.mux.HandleFunc("/v1/catalog/node/", s.wrap(s.CatalogNodeServices)) s.handleFuncMetrics("/v1/catalog/node/", s.wrap(s.CatalogNodeServices))
if !s.agent.config.DisableCoordinates { if !s.agent.config.DisableCoordinates {
s.mux.HandleFunc("/v1/coordinate/datacenters", s.wrap(s.CoordinateDatacenters)) s.handleFuncMetrics("/v1/coordinate/datacenters", s.wrap(s.CoordinateDatacenters))
s.mux.HandleFunc("/v1/coordinate/nodes", s.wrap(s.CoordinateNodes)) s.handleFuncMetrics("/v1/coordinate/nodes", s.wrap(s.CoordinateNodes))
} else { } else {
s.mux.HandleFunc("/v1/coordinate/datacenters", s.wrap(coordinateDisabled)) s.handleFuncMetrics("/v1/coordinate/datacenters", s.wrap(coordinateDisabled))
s.mux.HandleFunc("/v1/coordinate/nodes", s.wrap(coordinateDisabled)) s.handleFuncMetrics("/v1/coordinate/nodes", s.wrap(coordinateDisabled))
} }
s.mux.HandleFunc("/v1/health/node/", s.wrap(s.HealthNodeChecks)) s.handleFuncMetrics("/v1/health/node/", s.wrap(s.HealthNodeChecks))
s.mux.HandleFunc("/v1/health/checks/", s.wrap(s.HealthServiceChecks)) s.handleFuncMetrics("/v1/health/checks/", s.wrap(s.HealthServiceChecks))
s.mux.HandleFunc("/v1/health/state/", s.wrap(s.HealthChecksInState)) s.handleFuncMetrics("/v1/health/state/", s.wrap(s.HealthChecksInState))
s.mux.HandleFunc("/v1/health/service/", s.wrap(s.HealthServiceNodes)) s.handleFuncMetrics("/v1/health/service/", s.wrap(s.HealthServiceNodes))
s.mux.HandleFunc("/v1/agent/self", s.wrap(s.AgentSelf)) s.handleFuncMetrics("/v1/agent/self", s.wrap(s.AgentSelf))
s.mux.HandleFunc("/v1/agent/maintenance", s.wrap(s.AgentNodeMaintenance)) s.handleFuncMetrics("/v1/agent/maintenance", s.wrap(s.AgentNodeMaintenance))
s.mux.HandleFunc("/v1/agent/services", s.wrap(s.AgentServices)) s.handleFuncMetrics("/v1/agent/services", s.wrap(s.AgentServices))
s.mux.HandleFunc("/v1/agent/checks", s.wrap(s.AgentChecks)) s.handleFuncMetrics("/v1/agent/checks", s.wrap(s.AgentChecks))
s.mux.HandleFunc("/v1/agent/members", s.wrap(s.AgentMembers)) s.handleFuncMetrics("/v1/agent/members", s.wrap(s.AgentMembers))
s.mux.HandleFunc("/v1/agent/join/", s.wrap(s.AgentJoin)) s.handleFuncMetrics("/v1/agent/join/", s.wrap(s.AgentJoin))
s.mux.HandleFunc("/v1/agent/force-leave/", s.wrap(s.AgentForceLeave)) s.handleFuncMetrics("/v1/agent/force-leave/", s.wrap(s.AgentForceLeave))
s.mux.HandleFunc("/v1/agent/check/register", s.wrap(s.AgentRegisterCheck)) s.handleFuncMetrics("/v1/agent/check/register", s.wrap(s.AgentRegisterCheck))
s.mux.HandleFunc("/v1/agent/check/deregister/", s.wrap(s.AgentDeregisterCheck)) s.handleFuncMetrics("/v1/agent/check/deregister/", s.wrap(s.AgentDeregisterCheck))
s.mux.HandleFunc("/v1/agent/check/pass/", s.wrap(s.AgentCheckPass)) s.handleFuncMetrics("/v1/agent/check/pass/", s.wrap(s.AgentCheckPass))
s.mux.HandleFunc("/v1/agent/check/warn/", s.wrap(s.AgentCheckWarn)) s.handleFuncMetrics("/v1/agent/check/warn/", s.wrap(s.AgentCheckWarn))
s.mux.HandleFunc("/v1/agent/check/fail/", s.wrap(s.AgentCheckFail)) s.handleFuncMetrics("/v1/agent/check/fail/", s.wrap(s.AgentCheckFail))
s.mux.HandleFunc("/v1/agent/check/update/", s.wrap(s.AgentCheckUpdate)) s.handleFuncMetrics("/v1/agent/check/update/", s.wrap(s.AgentCheckUpdate))
s.mux.HandleFunc("/v1/agent/service/register", s.wrap(s.AgentRegisterService)) s.handleFuncMetrics("/v1/agent/service/register", s.wrap(s.AgentRegisterService))
s.mux.HandleFunc("/v1/agent/service/deregister/", s.wrap(s.AgentDeregisterService)) s.handleFuncMetrics("/v1/agent/service/deregister/", s.wrap(s.AgentDeregisterService))
s.mux.HandleFunc("/v1/agent/service/maintenance/", s.wrap(s.AgentServiceMaintenance)) s.handleFuncMetrics("/v1/agent/service/maintenance/", s.wrap(s.AgentServiceMaintenance))
s.mux.HandleFunc("/v1/event/fire/", s.wrap(s.EventFire)) s.handleFuncMetrics("/v1/event/fire/", s.wrap(s.EventFire))
s.mux.HandleFunc("/v1/event/list", s.wrap(s.EventList)) s.handleFuncMetrics("/v1/event/list", s.wrap(s.EventList))
s.mux.HandleFunc("/v1/kv/", s.wrap(s.KVSEndpoint)) s.handleFuncMetrics("/v1/kv/", s.wrap(s.KVSEndpoint))
s.mux.HandleFunc("/v1/session/create", s.wrap(s.SessionCreate)) s.handleFuncMetrics("/v1/session/create", s.wrap(s.SessionCreate))
s.mux.HandleFunc("/v1/session/destroy/", s.wrap(s.SessionDestroy)) s.handleFuncMetrics("/v1/session/destroy/", s.wrap(s.SessionDestroy))
s.mux.HandleFunc("/v1/session/renew/", s.wrap(s.SessionRenew)) s.handleFuncMetrics("/v1/session/renew/", s.wrap(s.SessionRenew))
s.mux.HandleFunc("/v1/session/info/", s.wrap(s.SessionGet)) s.handleFuncMetrics("/v1/session/info/", s.wrap(s.SessionGet))
s.mux.HandleFunc("/v1/session/node/", s.wrap(s.SessionsForNode)) s.handleFuncMetrics("/v1/session/node/", s.wrap(s.SessionsForNode))
s.mux.HandleFunc("/v1/session/list", s.wrap(s.SessionList)) s.handleFuncMetrics("/v1/session/list", s.wrap(s.SessionList))
if s.agent.config.ACLDatacenter != "" { if s.agent.config.ACLDatacenter != "" {
s.mux.HandleFunc("/v1/acl/create", s.wrap(s.ACLCreate)) s.handleFuncMetrics("/v1/acl/create", s.wrap(s.ACLCreate))
s.mux.HandleFunc("/v1/acl/update", s.wrap(s.ACLUpdate)) s.handleFuncMetrics("/v1/acl/update", s.wrap(s.ACLUpdate))
s.mux.HandleFunc("/v1/acl/destroy/", s.wrap(s.ACLDestroy)) s.handleFuncMetrics("/v1/acl/destroy/", s.wrap(s.ACLDestroy))
s.mux.HandleFunc("/v1/acl/info/", s.wrap(s.ACLGet)) s.handleFuncMetrics("/v1/acl/info/", s.wrap(s.ACLGet))
s.mux.HandleFunc("/v1/acl/clone/", s.wrap(s.ACLClone)) s.handleFuncMetrics("/v1/acl/clone/", s.wrap(s.ACLClone))
s.mux.HandleFunc("/v1/acl/list", s.wrap(s.ACLList)) s.handleFuncMetrics("/v1/acl/list", s.wrap(s.ACLList))
s.mux.HandleFunc("/v1/acl/replication", s.wrap(s.ACLReplicationStatus)) s.handleFuncMetrics("/v1/acl/replication", s.wrap(s.ACLReplicationStatus))
} else { } else {
s.mux.HandleFunc("/v1/acl/create", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/create", s.wrap(aclDisabled))
s.mux.HandleFunc("/v1/acl/update", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/update", s.wrap(aclDisabled))
s.mux.HandleFunc("/v1/acl/destroy/", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/destroy/", s.wrap(aclDisabled))
s.mux.HandleFunc("/v1/acl/info/", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/info/", s.wrap(aclDisabled))
s.mux.HandleFunc("/v1/acl/clone/", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/clone/", s.wrap(aclDisabled))
s.mux.HandleFunc("/v1/acl/list", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/list", s.wrap(aclDisabled))
s.mux.HandleFunc("/v1/acl/replication", s.wrap(aclDisabled)) s.handleFuncMetrics("/v1/acl/replication", s.wrap(aclDisabled))
} }
s.mux.HandleFunc("/v1/query", s.wrap(s.PreparedQueryGeneral)) s.handleFuncMetrics("/v1/query", s.wrap(s.PreparedQueryGeneral))
s.mux.HandleFunc("/v1/query/", s.wrap(s.PreparedQuerySpecific)) s.handleFuncMetrics("/v1/query/", s.wrap(s.PreparedQuerySpecific))
s.mux.HandleFunc("/v1/txn", s.wrap(s.Txn)) s.handleFuncMetrics("/v1/txn", s.wrap(s.Txn))
if enableDebug { if enableDebug {
s.mux.HandleFunc("/debug/pprof/", pprof.Index) s.handleFuncMetrics("/debug/pprof/", pprof.Index)
s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) s.handleFuncMetrics("/debug/pprof/cmdline", pprof.Cmdline)
s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile) s.handleFuncMetrics("/debug/pprof/profile", pprof.Profile)
s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) s.handleFuncMetrics("/debug/pprof/symbol", pprof.Symbol)
} }
// Use the custom UI dir if provided. // Use the custom UI dir if provided.
@ -288,9 +320,9 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
} }
// API's are under /internal/ui/ to avoid conflict // API's are under /internal/ui/ to avoid conflict
s.mux.HandleFunc("/v1/internal/ui/nodes", s.wrap(s.UINodes)) s.handleFuncMetrics("/v1/internal/ui/nodes", s.wrap(s.UINodes))
s.mux.HandleFunc("/v1/internal/ui/node/", s.wrap(s.UINodeInfo)) s.handleFuncMetrics("/v1/internal/ui/node/", s.wrap(s.UINodeInfo))
s.mux.HandleFunc("/v1/internal/ui/services", s.wrap(s.UIServices)) s.handleFuncMetrics("/v1/internal/ui/services", s.wrap(s.UIServices))
} }
// wrap is used to wrap functions to make them more convenient // wrap is used to wrap functions to make them more convenient