metrics.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright 2016 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package grpcproxy
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "math/rand"
  19. "net/http"
  20. "strings"
  21. "time"
  22. "github.com/prometheus/client_golang/prometheus"
  23. "go.etcd.io/etcd/etcdserver/api/etcdhttp"
  24. )
  25. var (
  26. watchersCoalescing = prometheus.NewGauge(prometheus.GaugeOpts{
  27. Namespace: "etcd",
  28. Subsystem: "grpc_proxy",
  29. Name: "watchers_coalescing_total",
  30. Help: "Total number of current watchers coalescing",
  31. })
  32. eventsCoalescing = prometheus.NewCounter(prometheus.CounterOpts{
  33. Namespace: "etcd",
  34. Subsystem: "grpc_proxy",
  35. Name: "events_coalescing_total",
  36. Help: "Total number of events coalescing",
  37. })
  38. cacheKeys = prometheus.NewGauge(prometheus.GaugeOpts{
  39. Namespace: "etcd",
  40. Subsystem: "grpc_proxy",
  41. Name: "cache_keys_total",
  42. Help: "Total number of keys/ranges cached",
  43. })
  44. cacheHits = prometheus.NewGauge(prometheus.GaugeOpts{
  45. Namespace: "etcd",
  46. Subsystem: "grpc_proxy",
  47. Name: "cache_hits_total",
  48. Help: "Total number of cache hits",
  49. })
  50. cachedMisses = prometheus.NewGauge(prometheus.GaugeOpts{
  51. Namespace: "etcd",
  52. Subsystem: "grpc_proxy",
  53. Name: "cache_misses_total",
  54. Help: "Total number of cache misses",
  55. })
  56. )
  57. func init() {
  58. prometheus.MustRegister(watchersCoalescing)
  59. prometheus.MustRegister(eventsCoalescing)
  60. prometheus.MustRegister(cacheKeys)
  61. prometheus.MustRegister(cacheHits)
  62. prometheus.MustRegister(cachedMisses)
  63. }
  64. // HandleMetrics performs a GET request against etcd endpoint and returns '/metrics'.
  65. func HandleMetrics(mux *http.ServeMux, c *http.Client, eps []string) {
  66. // random shuffle endpoints
  67. r := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
  68. if len(eps) > 1 {
  69. eps = shuffleEndpoints(r, eps)
  70. }
  71. pathMetrics := etcdhttp.PathMetrics
  72. mux.HandleFunc(pathMetrics, func(w http.ResponseWriter, r *http.Request) {
  73. target := fmt.Sprintf("%s%s", eps[0], pathMetrics)
  74. if !strings.HasPrefix(target, "http") {
  75. scheme := "http"
  76. if r.TLS != nil {
  77. scheme = "https"
  78. }
  79. target = fmt.Sprintf("%s://%s", scheme, target)
  80. }
  81. resp, err := c.Get(target)
  82. if err != nil {
  83. http.Error(w, "Internal server error", http.StatusInternalServerError)
  84. }
  85. defer resp.Body.Close()
  86. w.Header().Set("Content-Type", "text/plain; version=0.0.4")
  87. body, _ := ioutil.ReadAll(resp.Body)
  88. fmt.Fprintf(w, "%s", body)
  89. })
  90. }
  91. func shuffleEndpoints(r *rand.Rand, eps []string) []string {
  92. // copied from Go 1.9<= rand.Rand.Perm
  93. n := len(eps)
  94. p := make([]int, n)
  95. for i := 0; i < n; i++ {
  96. j := r.Intn(i + 1)
  97. p[i] = p[j]
  98. p[j] = i
  99. }
  100. neps := make([]string, n)
  101. for i, k := range p {
  102. neps[i] = eps[k]
  103. }
  104. return neps
  105. }