github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-twemproxy/lib/twemproxy.go (about) 1 package mptwemproxy 2 3 import ( 4 "flag" 5 "fmt" 6 "regexp" 7 8 mp "github.com/mackerelio/go-mackerel-plugin-helper" 9 "golang.org/x/text/cases" 10 "golang.org/x/text/language" 11 ) 12 13 // TwemproxyPlugin mackerel plugin 14 type TwemproxyPlugin struct { 15 Address string 16 Prefix string 17 Timeout uint 18 EachServerMetrics bool 19 } 20 21 // MetricKeyPrefix interface for PluginWithPrefix 22 func (p TwemproxyPlugin) MetricKeyPrefix() string { 23 if p.Prefix == "" { 24 p.Prefix = "twemproxy" 25 } 26 return p.Prefix 27 } 28 29 // GraphDefinition interface for mackerelplugin 30 func (p TwemproxyPlugin) GraphDefinition() map[string]mp.Graphs { 31 labelPrefix := cases.Title(language.Und, cases.NoLower).String(p.Prefix) 32 33 var graphdef = map[string]mp.Graphs{ 34 "connections": { 35 Label: (labelPrefix + " Connections"), 36 Unit: "integer", 37 Metrics: []mp.Metrics{ 38 {Name: "total_connections", Label: "New Connections", Diff: true}, 39 {Name: "curr_connections", Label: "Current Connections", Diff: false}, 40 }, 41 }, 42 "total_server_error": { 43 Label: (labelPrefix + " Total Server Error"), 44 Unit: "integer", 45 Metrics: []mp.Metrics{ 46 {Name: "total_pool_client_error", Label: "Pool Client Error", Diff: true}, 47 {Name: "total_pool_server_ejects", Label: "Pool Server Ejects", Diff: true}, 48 {Name: "total_pool_forward_error", Label: "Pool Forward Error", Diff: true}, 49 {Name: "total_server_timeout", Label: "Server Timeout", Diff: true}, 50 {Name: "total_server_error", Label: "Server Error", Diff: true}, 51 }, 52 }, 53 "pool_error.#": { 54 Label: (labelPrefix + " Pool Error"), 55 Unit: "integer", 56 Metrics: []mp.Metrics{ 57 {Name: "client_err", Label: "Client Error", Diff: true}, 58 {Name: "server_ejects", Label: "Server Ejects", Diff: true}, 59 {Name: "forward_error", Label: "Forward Error", Diff: true}, 60 }, 61 }, 62 "pool_client_connections.#": { 63 Label: (labelPrefix + " Pool Client Connections"), 64 Unit: "integer", 65 Metrics: []mp.Metrics{ 66 {Name: "client_connections", Label: "Client Connections", Diff: false}, 67 {Name: "client_eof", Label: "Client EOF", Diff: true}, 68 }, 69 }, 70 "server_error.#": { 71 Label: (labelPrefix + " Server Error"), 72 Unit: "integer", 73 Metrics: []mp.Metrics{ 74 {Name: "server_err", Label: "Server Error", Diff: true}, 75 {Name: "server_timedout", Label: "Server Timedout", Diff: true}, 76 }, 77 }, 78 "server_connections.#": { 79 Label: (labelPrefix + " Server Connections"), 80 Unit: "integer", 81 Metrics: []mp.Metrics{ 82 {Name: "server_connections", Label: "Server Connections", Diff: false}, 83 {Name: "server_eof", Label: "Server EOF", Diff: true}, 84 }, 85 }, 86 "server_queue.#": { 87 Label: (labelPrefix + " Server Queue"), 88 Unit: "integer", 89 Metrics: []mp.Metrics{ 90 {Name: "out_queue", Label: "Out Queue", Diff: false}, 91 {Name: "in_queue", Label: "In Queue", Diff: false}, 92 }, 93 }, 94 "server_queue_bytes.#": { 95 Label: (labelPrefix + " Server Queue Bytes"), 96 Unit: "bytes", 97 Metrics: []mp.Metrics{ 98 {Name: "out_queue_bytes", Label: "Out Queue Bytes", Diff: false}, 99 {Name: "in_queue_bytes", Label: "In Queue Bytes", Diff: false}, 100 }, 101 }, 102 "server_communications.#": { 103 Label: (labelPrefix + " Server Communications"), 104 Unit: "integer", 105 Metrics: []mp.Metrics{ 106 {Name: "requests", Label: "Requests", Diff: true}, 107 {Name: "responses", Label: "Responses", Diff: true}, 108 }, 109 }, 110 "server_communication_bytes.#": { 111 Label: (labelPrefix + " Server Communication Bytes"), 112 Unit: "bytes", 113 Metrics: []mp.Metrics{ 114 {Name: "request_bytes", Label: "Request Bytes", Diff: true}, 115 {Name: "response_bytes", Label: "Response Bytes", Diff: true}, 116 }, 117 }, 118 } 119 return graphdef 120 } 121 122 // FetchMetrics interface for mackerelplugin 123 func (p TwemproxyPlugin) FetchMetrics() (map[string]interface{}, error) { 124 stats, err := getStats(p) 125 if err != nil { 126 return nil, fmt.Errorf("Failed to fetch twemproxy metrics: %s", err) // nolint 127 } 128 129 metrics := make(map[string]interface{}) 130 131 if stats.TotalConnections != nil { 132 metrics["total_connections"] = *stats.TotalConnections 133 } 134 if stats.CurrConnections != nil { 135 metrics["curr_connections"] = *stats.CurrConnections 136 } 137 138 totalPoolClientErr := uint64(0) 139 totalPoolServerEjects := uint64(0) 140 totalPoolForwardErr := uint64(0) 141 totalServerTimeout := uint64(0) 142 totalServerErr := uint64(0) 143 144 // NOTE: Each custom metric name contains a wildcard. 145 for pName, po := range stats.Pools { 146 // A normalized pool name corresponds a wildcard 147 np := normalizeMetricName(pName) 148 wp := "." + np + "." 149 metrics["pool_error"+wp+"client_err"] = *po.ClientErr 150 metrics["pool_error"+wp+"server_ejects"] = *po.ServerEjects 151 metrics["pool_error"+wp+"forward_error"] = *po.ForwardError 152 metrics["pool_client_connections"+wp+"client_eof"] = *po.ClientEOF 153 metrics["pool_client_connections"+wp+"client_connections"] = *po.ClientConnections 154 totalPoolClientErr += *po.ClientErr 155 totalPoolServerEjects += *po.ServerEjects 156 totalPoolForwardErr += *po.ForwardError 157 158 for sName, s := range po.Servers { 159 if p.EachServerMetrics { 160 // A concat of normalized pool and server names corresponds a wildcard 161 ns := normalizeMetricName(sName) 162 ws := "." + np + "_" + ns + "." 163 metrics["server_error"+ws+"server_err"] = *s.ServerErr 164 metrics["server_error"+ws+"server_timedout"] = *s.ServerTimedout 165 metrics["server_connections"+ws+"server_eof"] = *s.ServerEOF 166 metrics["server_connections"+ws+"server_connections"] = *s.ServerConnections 167 metrics["server_queue"+ws+"out_queue"] = *s.OutQueue 168 metrics["server_queue"+ws+"in_queue"] = *s.InQueue 169 metrics["server_queue_bytes"+ws+"out_queue_bytes"] = *s.OutQueueBytes 170 metrics["server_queue_bytes"+ws+"in_queue_bytes"] = *s.InQueueBytes 171 metrics["server_communications"+ws+"requests"] = *s.Requests 172 metrics["server_communications"+ws+"responses"] = *s.Responses 173 metrics["server_communication_bytes"+ws+"request_bytes"] = *s.RequestBytes 174 metrics["server_communication_bytes"+ws+"response_bytes"] = *s.ResponseBytes 175 } 176 totalServerTimeout += *s.ServerTimedout 177 totalServerErr += *s.ServerErr 178 } 179 } 180 181 metrics["total_pool_client_error"] = totalPoolClientErr 182 metrics["total_pool_server_ejects"] = totalPoolServerEjects 183 metrics["total_pool_forward_error"] = totalPoolForwardErr 184 metrics["total_server_timeout"] = totalServerTimeout 185 metrics["total_server_error"] = totalServerErr 186 return metrics, nil 187 } 188 189 var normalizeMetricNameRe = regexp.MustCompile(`[^-a-zA-Z0-9_]`) 190 191 func normalizeMetricName(name string) string { 192 return normalizeMetricNameRe.ReplaceAllString(name, "_") 193 } 194 195 // Do the plugin 196 func Do() { 197 optAddress := flag.String("address", "localhost:22222", "twemproxy stats Address") 198 optPrefix := flag.String("metric-key-prefix", "twemproxy", "Metric key prefix") 199 optTimeout := flag.Uint("timeout", 5, "Timeout") 200 optEachServerMetrics := flag.Bool("enable-each-server-metrics", false, "Enable metric collection for each server") 201 optTempfile := flag.String("tempfile", "", "Temp file name") 202 flag.Parse() 203 204 p := TwemproxyPlugin{ 205 Address: *optAddress, 206 Prefix: *optPrefix, 207 Timeout: *optTimeout, 208 EachServerMetrics: *optEachServerMetrics, 209 } 210 211 helper := mp.NewMackerelPlugin(p) 212 helper.Tempfile = *optTempfile 213 helper.Run() 214 }