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  }