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