github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-twemproxy/lib/stats.go (about)

     1  package mptwemproxy
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net"
     8  	"time"
     9  )
    10  
    11  // TwemproxyStats represents a twemproxy stats
    12  type TwemproxyStats struct {
    13  	TotalConnections *uint64
    14  	CurrConnections  *uint64
    15  	Pools            map[string]*PoolStats
    16  }
    17  
    18  // PoolStats represents a pool stats
    19  type PoolStats struct {
    20  	ClientEOF         *uint64
    21  	ClientErr         *uint64
    22  	ClientConnections *uint64
    23  	ServerEjects      *uint64
    24  	ForwardError      *uint64
    25  	Servers           map[string]*ServerStats
    26  }
    27  
    28  // ServerStats represents a server stats
    29  type ServerStats struct {
    30  	ServerEOF         *uint64
    31  	ServerErr         *uint64
    32  	ServerTimedout    *uint64
    33  	ServerConnections *uint64
    34  	OutQueueBytes     *uint64
    35  	InQueueBytes      *uint64
    36  	OutQueue          *uint64
    37  	InQueue           *uint64
    38  	RequestBytes      *uint64
    39  	ResponseBytes     *uint64
    40  	Requests          *uint64
    41  	Responses         *uint64
    42  }
    43  
    44  func getStats(p TwemproxyPlugin) (*TwemproxyStats, error) {
    45  	// get json data
    46  	address := p.Address
    47  	timeout := time.Duration(p.Timeout) * time.Second
    48  	conn, err := net.DialTimeout("tcp", address, timeout)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	res := bufio.NewReader(conn)
    53  
    54  	// decode the json data to TwemproxyStats struct
    55  	var t TwemproxyStats
    56  	decoder := json.NewDecoder(res)
    57  	err = decoder.Decode(&t)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	return &t, nil
    62  }
    63  
    64  // UnmarshalJSON interface for json.Unmarshaler
    65  func (t *TwemproxyStats) UnmarshalJSON(data []byte) error {
    66  	var raw map[string]interface{}
    67  	err := json.Unmarshal(data, &raw)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	t.Pools = make(map[string]*PoolStats)
    73  
    74  L:
    75  	for k, v := range raw {
    76  		switch v := v.(type) {
    77  		case float64:
    78  			cv := uint64(v)
    79  			switch k {
    80  			case "total_connections":
    81  				t.TotalConnections = &cv
    82  			case "curr_connections":
    83  				t.CurrConnections = &cv
    84  			case "uptime", "timestamp":
    85  				// do not use these parameters. skip.
    86  			default:
    87  				err = fmt.Errorf("invalid key: %v in rawTwemproxy: %v", k, raw)
    88  				break L
    89  			}
    90  		case map[string]interface{}:
    91  			pool, perr := decodePoolStats(v)
    92  			if perr != nil {
    93  				err = perr
    94  				break L
    95  			}
    96  			t.Pools[k] = pool
    97  		case string:
    98  			// do not use parameters(service, source, version). skip.
    99  		default:
   100  			err = fmt.Errorf("invalid type in rawTwemproxy: %v", raw)
   101  			break L
   102  		}
   103  	}
   104  
   105  	return err
   106  }
   107  
   108  func decodePoolStats(rawStats map[string]interface{}) (*PoolStats, error) {
   109  	pool := new(PoolStats)
   110  	pool.Servers = make(map[string]*ServerStats)
   111  
   112  	var err error
   113  
   114  L:
   115  	for k, v := range rawStats {
   116  		switch v := v.(type) {
   117  		case float64:
   118  			cv := uint64(v)
   119  			switch k {
   120  			case "client_eof":
   121  				pool.ClientEOF = &cv
   122  			case "client_err":
   123  				pool.ClientErr = &cv
   124  			case "client_connections":
   125  				pool.ClientConnections = &cv
   126  			case "server_ejects":
   127  				pool.ServerEjects = &cv
   128  			case "forward_error":
   129  				pool.ForwardError = &cv
   130  			case "fragments":
   131  				// do not use this parameter. skip.
   132  			default:
   133  				err = fmt.Errorf("invalid key: %v in rawPool: %v", k, rawStats)
   134  				break L
   135  			}
   136  		case map[string]interface{}:
   137  			server, serr := decodeServerStats(v)
   138  			if serr != nil {
   139  				err = serr
   140  				break L
   141  			}
   142  			pool.Servers[k] = server
   143  		default:
   144  			err = fmt.Errorf("invalid type in rawPool: %v", rawStats)
   145  			break L
   146  		}
   147  	}
   148  
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	return pool, nil
   153  }
   154  
   155  func decodeServerStats(rawStats map[string]interface{}) (*ServerStats, error) {
   156  	server := new(ServerStats)
   157  
   158  	var err error
   159  
   160  L:
   161  	for k, v := range rawStats {
   162  		cv := uint64(v.(float64))
   163  		switch k {
   164  		case "server_eof":
   165  			server.ServerEOF = &cv
   166  		case "server_err":
   167  			server.ServerErr = &cv
   168  		case "server_timedout":
   169  			server.ServerTimedout = &cv
   170  		case "server_connections":
   171  			server.ServerConnections = &cv
   172  		case "out_queue_bytes":
   173  			server.OutQueueBytes = &cv
   174  		case "in_queue_bytes":
   175  			server.InQueueBytes = &cv
   176  		case "out_queue":
   177  			server.OutQueue = &cv
   178  		case "in_queue":
   179  			server.InQueue = &cv
   180  		case "request_bytes":
   181  			server.RequestBytes = &cv
   182  		case "response_bytes":
   183  			server.ResponseBytes = &cv
   184  		case "requests":
   185  			server.Requests = &cv
   186  		case "responses":
   187  			server.Responses = &cv
   188  		case "server_ejected_at":
   189  			// do not use this parameter. skip.
   190  		default:
   191  			err = fmt.Errorf("invalid key: %v in rawServer: %v", k, rawStats)
   192  			break L
   193  		}
   194  	}
   195  
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	return server, nil
   200  }