package riak import ( "encoding/json" "fmt" "net/http" "net/url" "time" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" ) // Type Riak gathers statistics from one or more Riak instances type Riak struct { // Servers is a slice of servers as http addresses (ex. http://127.0.0.1:8098) Servers []string client *http.Client } // NewRiak return a new instance of Riak with a default http client func NewRiak() *Riak { tr := &http.Transport{ResponseHeaderTimeout: time.Duration(3 * time.Second)} client := &http.Client{ Transport: tr, Timeout: time.Duration(4 * time.Second), } return &Riak{client: client} } // Type riakStats represents the data that is received from Riak type riakStats struct { CpuAvg1 int64 `json:"cpu_avg1"` CpuAvg15 int64 `json:"cpu_avg15"` CpuAvg5 int64 `json:"cpu_avg5"` MemoryCode int64 `json:"memory_code"` MemoryEts int64 `json:"memory_ets"` MemoryProcesses int64 `json:"memory_processes"` MemorySystem int64 `json:"memory_system"` MemoryTotal int64 `json:"memory_total"` NodeGetFsmObjsize100 int64 `json:"node_get_fsm_objsize_100"` NodeGetFsmObjsize95 int64 `json:"node_get_fsm_objsize_95"` NodeGetFsmObjsize99 int64 `json:"node_get_fsm_objsize_99"` NodeGetFsmObjsizeMean int64 `json:"node_get_fsm_objsize_mean"` NodeGetFsmObjsizeMedian int64 `json:"node_get_fsm_objsize_median"` NodeGetFsmSiblings100 int64 `json:"node_get_fsm_siblings_100"` NodeGetFsmSiblings95 int64 `json:"node_get_fsm_siblings_95"` NodeGetFsmSiblings99 int64 `json:"node_get_fsm_siblings_99"` NodeGetFsmSiblingsMean int64 `json:"node_get_fsm_siblings_mean"` NodeGetFsmSiblingsMedian int64 `json:"node_get_fsm_siblings_median"` NodeGetFsmTime100 int64 `json:"node_get_fsm_time_100"` NodeGetFsmTime95 int64 `json:"node_get_fsm_time_95"` NodeGetFsmTime99 int64 `json:"node_get_fsm_time_99"` NodeGetFsmTimeMean int64 `json:"node_get_fsm_time_mean"` NodeGetFsmTimeMedian int64 `json:"node_get_fsm_time_median"` NodeGets int64 `json:"node_gets"` NodeGetsTotal int64 `json:"node_gets_total"` Nodename string `json:"nodename"` NodePutFsmTime100 int64 `json:"node_put_fsm_time_100"` NodePutFsmTime95 int64 `json:"node_put_fsm_time_95"` NodePutFsmTime99 int64 `json:"node_put_fsm_time_99"` NodePutFsmTimeMean int64 `json:"node_put_fsm_time_mean"` NodePutFsmTimeMedian int64 `json:"node_put_fsm_time_median"` NodePuts int64 `json:"node_puts"` NodePutsTotal int64 `json:"node_puts_total"` PbcActive int64 `json:"pbc_active"` PbcConnects int64 `json:"pbc_connects"` PbcConnectsTotal int64 `json:"pbc_connects_total"` VnodeGets int64 `json:"vnode_gets"` VnodeGetsTotal int64 `json:"vnode_gets_total"` VnodeIndexReads int64 `json:"vnode_index_reads"` VnodeIndexReadsTotal int64 `json:"vnode_index_reads_total"` VnodeIndexWrites int64 `json:"vnode_index_writes"` VnodeIndexWritesTotal int64 `json:"vnode_index_writes_total"` VnodePuts int64 `json:"vnode_puts"` VnodePutsTotal int64 `json:"vnode_puts_total"` } // A sample configuration to only gather stats from localhost, default port. const sampleConfig = ` # Specify a list of one or more riak http servers servers = ["http://localhost:8098"] ` // Returns a sample configuration for the plugin func (r *Riak) SampleConfig() string { return sampleConfig } // Returns a description of the plugin func (r *Riak) Description() string { return "Read metrics one or many Riak servers" } // Reads stats from all configured servers. func (r *Riak) Gather(acc telegraf.Accumulator) error { // Default to a single server at localhost (default port) if none specified if len(r.Servers) == 0 { r.Servers = []string{"http://127.0.0.1:8098"} } // Range over all servers, gathering stats. Returns early in case of any error. for _, s := range r.Servers { if err := r.gatherServer(s, acc); err != nil { return err } } return nil } // Gathers stats from a single server, adding them to the accumulator func (r *Riak) gatherServer(s string, acc telegraf.Accumulator) error { // Parse the given URL to extract the server tag u, err := url.Parse(s) if err != nil { return fmt.Errorf("riak unable to parse given server url %s: %s", s, err) } // Perform the GET request to the riak /stats endpoint resp, err := r.client.Get(s + "/stats") if err != nil { return err } defer resp.Body.Close() // Successful responses will always return status code 200 if resp.StatusCode != http.StatusOK { return fmt.Errorf("riak responded with unexepcted status code %d", resp.StatusCode) } // Decode the response JSON into a new stats struct stats := &riakStats{} if err := json.NewDecoder(resp.Body).Decode(stats); err != nil { return fmt.Errorf("unable to decode riak response: %s", err) } // Build a map of tags tags := map[string]string{ "nodename": stats.Nodename, "server": u.Host, } // Build a map of field values fields := map[string]interface{}{ "cpu_avg1": stats.CpuAvg1, "cpu_avg15": stats.CpuAvg15, "cpu_avg5": stats.CpuAvg5, "memory_code": stats.MemoryCode, "memory_ets": stats.MemoryEts, "memory_processes": stats.MemoryProcesses, "memory_system": stats.MemorySystem, "memory_total": stats.MemoryTotal, "node_get_fsm_objsize_100": stats.NodeGetFsmObjsize100, "node_get_fsm_objsize_95": stats.NodeGetFsmObjsize95, "node_get_fsm_objsize_99": stats.NodeGetFsmObjsize99, "node_get_fsm_objsize_mean": stats.NodeGetFsmObjsizeMean, "node_get_fsm_objsize_median": stats.NodeGetFsmObjsizeMedian, "node_get_fsm_siblings_100": stats.NodeGetFsmSiblings100, "node_get_fsm_siblings_95": stats.NodeGetFsmSiblings95, "node_get_fsm_siblings_99": stats.NodeGetFsmSiblings99, "node_get_fsm_siblings_mean": stats.NodeGetFsmSiblingsMean, "node_get_fsm_siblings_median": stats.NodeGetFsmSiblingsMedian, "node_get_fsm_time_100": stats.NodeGetFsmTime100, "node_get_fsm_time_95": stats.NodeGetFsmTime95, "node_get_fsm_time_99": stats.NodeGetFsmTime99, "node_get_fsm_time_mean": stats.NodeGetFsmTimeMean, "node_get_fsm_time_median": stats.NodeGetFsmTimeMedian, "node_gets": stats.NodeGets, "node_gets_total": stats.NodeGetsTotal, "node_put_fsm_time_100": stats.NodePutFsmTime100, "node_put_fsm_time_95": stats.NodePutFsmTime95, "node_put_fsm_time_99": stats.NodePutFsmTime99, "node_put_fsm_time_mean": stats.NodePutFsmTimeMean, "node_put_fsm_time_median": stats.NodePutFsmTimeMedian, "node_puts": stats.NodePuts, "node_puts_total": stats.NodePutsTotal, "pbc_active": stats.PbcActive, "pbc_connects": stats.PbcConnects, "pbc_connects_total": stats.PbcConnectsTotal, "vnode_gets": stats.VnodeGets, "vnode_gets_total": stats.VnodeGetsTotal, "vnode_index_reads": stats.VnodeIndexReads, "vnode_index_reads_total": stats.VnodeIndexReadsTotal, "vnode_index_writes": stats.VnodeIndexWrites, "vnode_index_writes_total": stats.VnodeIndexWritesTotal, "vnode_puts": stats.VnodePuts, "vnode_puts_total": stats.VnodePutsTotal, } // Accumulate the tags and values acc.AddFields("riak", fields, tags) return nil } func init() { inputs.Add("riak", func() telegraf.Input { return NewRiak() }) }