metrics.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Licensed to Elasticsearch B.V. under one or more contributor
  2. // license agreements. See the NOTICE file distributed with
  3. // this work for additional information regarding copyright
  4. // ownership. Elasticsearch B.V. licenses this file to you under
  5. // the Apache License, Version 2.0 (the "License"); you may
  6. // not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. package estransport
  18. import (
  19. "errors"
  20. "fmt"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. )
  26. // Measurable defines the interface for transports supporting metrics.
  27. //
  28. type Measurable interface {
  29. Metrics() (Metrics, error)
  30. }
  31. // connectionable defines the interface for transports returning a list of connections.
  32. //
  33. type connectionable interface {
  34. connections() []*Connection
  35. }
  36. // Metrics represents the transport metrics.
  37. //
  38. type Metrics struct {
  39. Requests int `json:"requests"`
  40. Failures int `json:"failures"`
  41. Responses map[int]int `json:"responses"`
  42. Connections []fmt.Stringer `json:"connections"`
  43. }
  44. // ConnectionMetric represents metric information for a connection.
  45. //
  46. type ConnectionMetric struct {
  47. URL string `json:"url"`
  48. Failures int `json:"failures,omitempty"`
  49. IsDead bool `json:"dead,omitempty"`
  50. DeadSince *time.Time `json:"dead_since,omitempty"`
  51. Meta struct {
  52. ID string `json:"id"`
  53. Name string `json:"name"`
  54. Roles []string `json:"roles"`
  55. } `json:"meta"`
  56. }
  57. // metrics represents the inner state of metrics.
  58. //
  59. type metrics struct {
  60. sync.RWMutex
  61. requests int
  62. failures int
  63. responses map[int]int
  64. connections []*Connection
  65. }
  66. // Metrics returns the transport metrics.
  67. //
  68. func (c *Client) Metrics() (Metrics, error) {
  69. if c.metrics == nil {
  70. return Metrics{}, errors.New("transport metrics not enabled")
  71. }
  72. c.metrics.RLock()
  73. defer c.metrics.RUnlock()
  74. if lockable, ok := c.pool.(sync.Locker); ok {
  75. lockable.Lock()
  76. defer lockable.Unlock()
  77. }
  78. m := Metrics{
  79. Requests: c.metrics.requests,
  80. Failures: c.metrics.failures,
  81. Responses: make(map[int]int, len(c.metrics.responses)),
  82. }
  83. for code, num := range c.metrics.responses {
  84. m.Responses[code] = num
  85. }
  86. if pool, ok := c.pool.(connectionable); ok {
  87. for _, c := range pool.connections() {
  88. c.Lock()
  89. cm := ConnectionMetric{
  90. URL: c.URL.String(),
  91. IsDead: c.IsDead,
  92. Failures: c.Failures,
  93. }
  94. if !c.DeadSince.IsZero() {
  95. cm.DeadSince = &c.DeadSince
  96. }
  97. if c.ID != "" {
  98. cm.Meta.ID = c.ID
  99. }
  100. if c.Name != "" {
  101. cm.Meta.Name = c.Name
  102. }
  103. if len(c.Roles) > 0 {
  104. cm.Meta.Roles = c.Roles
  105. }
  106. m.Connections = append(m.Connections, cm)
  107. c.Unlock()
  108. }
  109. }
  110. return m, nil
  111. }
  112. // String returns the metrics as a string.
  113. //
  114. func (m Metrics) String() string {
  115. var (
  116. i int
  117. b strings.Builder
  118. )
  119. b.WriteString("{")
  120. b.WriteString("Requests:")
  121. b.WriteString(strconv.Itoa(m.Requests))
  122. b.WriteString(" Failures:")
  123. b.WriteString(strconv.Itoa(m.Failures))
  124. if len(m.Responses) > 0 {
  125. b.WriteString(" Responses: ")
  126. b.WriteString("[")
  127. for code, num := range m.Responses {
  128. b.WriteString(strconv.Itoa(code))
  129. b.WriteString(":")
  130. b.WriteString(strconv.Itoa(num))
  131. if i+1 < len(m.Responses) {
  132. b.WriteString(", ")
  133. }
  134. i++
  135. }
  136. b.WriteString("]")
  137. }
  138. b.WriteString(" Connections: [")
  139. for i, c := range m.Connections {
  140. b.WriteString(c.String())
  141. if i+1 < len(m.Connections) {
  142. b.WriteString(", ")
  143. }
  144. i++
  145. }
  146. b.WriteString("]")
  147. b.WriteString("}")
  148. return b.String()
  149. }
  150. // String returns the connection information as a string.
  151. //
  152. func (cm ConnectionMetric) String() string {
  153. var b strings.Builder
  154. b.WriteString("{")
  155. b.WriteString(cm.URL)
  156. if cm.IsDead {
  157. fmt.Fprintf(&b, " dead=%v", cm.IsDead)
  158. }
  159. if cm.Failures > 0 {
  160. fmt.Fprintf(&b, " failures=%d", cm.Failures)
  161. }
  162. if cm.DeadSince != nil {
  163. fmt.Fprintf(&b, " dead_since=%s", cm.DeadSince.Local().Format(time.Stamp))
  164. }
  165. b.WriteString("}")
  166. return b.String()
  167. }