297 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
| package couchdb
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf"
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs"
 | |
| )
 | |
| 
 | |
| type (
 | |
| 	metaData struct {
 | |
| 		Current *float64 `json:"current"`
 | |
| 		Sum     *float64 `json:"sum"`
 | |
| 		Mean    *float64 `json:"mean"`
 | |
| 		Stddev  *float64 `json:"stddev"`
 | |
| 		Min     *float64 `json:"min"`
 | |
| 		Max     *float64 `json:"max"`
 | |
| 		Value   *float64 `json:"value"`
 | |
| 	}
 | |
| 
 | |
| 	oldValue struct {
 | |
| 		Value metaData `json:"value"`
 | |
| 		metaData
 | |
| 	}
 | |
| 
 | |
| 	couchdb struct {
 | |
| 		AuthCacheHits       metaData            `json:"auth_cache_hits"`
 | |
| 		AuthCacheMisses     metaData            `json:"auth_cache_misses"`
 | |
| 		DatabaseWrites      metaData            `json:"database_writes"`
 | |
| 		DatabaseReads       metaData            `json:"database_reads"`
 | |
| 		OpenDatabases       metaData            `json:"open_databases"`
 | |
| 		OpenOsFiles         metaData            `json:"open_os_files"`
 | |
| 		RequestTime         oldValue            `json:"request_time"`
 | |
| 		HttpdRequestMethods httpdRequestMethods `json:"httpd_request_methods"`
 | |
| 		HttpdStatusCodes    httpdStatusCodes    `json:"httpd_status_codes"`
 | |
| 	}
 | |
| 
 | |
| 	httpdRequestMethods struct {
 | |
| 		Put    metaData `json:"PUT"`
 | |
| 		Get    metaData `json:"GET"`
 | |
| 		Copy   metaData `json:"COPY"`
 | |
| 		Delete metaData `json:"DELETE"`
 | |
| 		Post   metaData `json:"POST"`
 | |
| 		Head   metaData `json:"HEAD"`
 | |
| 	}
 | |
| 
 | |
| 	httpdStatusCodes struct {
 | |
| 		Status200 metaData `json:"200"`
 | |
| 		Status201 metaData `json:"201"`
 | |
| 		Status202 metaData `json:"202"`
 | |
| 		Status301 metaData `json:"301"`
 | |
| 		Status304 metaData `json:"304"`
 | |
| 		Status400 metaData `json:"400"`
 | |
| 		Status401 metaData `json:"401"`
 | |
| 		Status403 metaData `json:"403"`
 | |
| 		Status404 metaData `json:"404"`
 | |
| 		Status405 metaData `json:"405"`
 | |
| 		Status409 metaData `json:"409"`
 | |
| 		Status412 metaData `json:"412"`
 | |
| 		Status500 metaData `json:"500"`
 | |
| 	}
 | |
| 
 | |
| 	httpd struct {
 | |
| 		BulkRequests             metaData `json:"bulk_requests"`
 | |
| 		Requests                 metaData `json:"requests"`
 | |
| 		TemporaryViewReads       metaData `json:"temporary_view_reads"`
 | |
| 		ViewReads                metaData `json:"view_reads"`
 | |
| 		ClientsRequestingChanges metaData `json:"clients_requesting_changes"`
 | |
| 	}
 | |
| 
 | |
| 	Stats struct {
 | |
| 		Couchdb             couchdb             `json:"couchdb"`
 | |
| 		HttpdRequestMethods httpdRequestMethods `json:"httpd_request_methods"`
 | |
| 		HttpdStatusCodes    httpdStatusCodes    `json:"httpd_status_codes"`
 | |
| 		Httpd               httpd               `json:"httpd"`
 | |
| 	}
 | |
| 
 | |
| 	CouchDB struct {
 | |
| 		Hosts         []string `toml:"hosts"`
 | |
| 		BasicUsername string   `toml:"basic_username"`
 | |
| 		BasicPassword string   `toml:"basic_password"`
 | |
| 
 | |
| 		client *http.Client
 | |
| 	}
 | |
| )
 | |
| 
 | |
| func (*CouchDB) Description() string {
 | |
| 	return "Read CouchDB Stats from one or more servers"
 | |
| }
 | |
| 
 | |
| func (*CouchDB) SampleConfig() string {
 | |
| 	return `
 | |
|   ## Works with CouchDB stats endpoints out of the box
 | |
|   ## Multiple Hosts from which to read CouchDB stats:
 | |
|   hosts = ["http://localhost:8086/_stats"]
 | |
| 
 | |
|   ## Use HTTP Basic Authentication.
 | |
|   # basic_username = "telegraf"
 | |
|   # basic_password = "p@ssw0rd"
 | |
| `
 | |
| }
 | |
| 
 | |
| func (c *CouchDB) Gather(accumulator telegraf.Accumulator) error {
 | |
| 	var wg sync.WaitGroup
 | |
| 	for _, u := range c.Hosts {
 | |
| 		wg.Add(1)
 | |
| 		go func(host string) {
 | |
| 			defer wg.Done()
 | |
| 			if err := c.fetchAndInsertData(accumulator, host); err != nil {
 | |
| 				accumulator.AddError(fmt.Errorf("[host=%s]: %s", host, err))
 | |
| 			}
 | |
| 		}(u)
 | |
| 	}
 | |
| 
 | |
| 	wg.Wait()
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *CouchDB) fetchAndInsertData(accumulator telegraf.Accumulator, host string) error {
 | |
| 	if c.client == nil {
 | |
| 		c.client = &http.Client{
 | |
| 			Transport: &http.Transport{
 | |
| 				ResponseHeaderTimeout: time.Duration(3 * time.Second),
 | |
| 			},
 | |
| 			Timeout: time.Duration(4 * time.Second),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	req, err := http.NewRequest("GET", host, nil)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if c.BasicUsername != "" || c.BasicPassword != "" {
 | |
| 		req.SetBasicAuth(c.BasicUsername, c.BasicPassword)
 | |
| 	}
 | |
| 
 | |
| 	response, error := c.client.Do(req)
 | |
| 	if error != nil {
 | |
| 		return error
 | |
| 	}
 | |
| 	defer response.Body.Close()
 | |
| 
 | |
| 	if response.StatusCode != 200 {
 | |
| 		return fmt.Errorf("Failed to get stats from couchdb: HTTP responded %d", response.StatusCode)
 | |
| 	}
 | |
| 
 | |
| 	stats := Stats{}
 | |
| 	decoder := json.NewDecoder(response.Body)
 | |
| 	decoder.Decode(&stats)
 | |
| 
 | |
| 	fields := map[string]interface{}{}
 | |
| 
 | |
| 	// for couchdb 2.0 API changes
 | |
| 	requestTime := metaData{
 | |
| 		Current: stats.Couchdb.RequestTime.Current,
 | |
| 		Sum:     stats.Couchdb.RequestTime.Sum,
 | |
| 		Mean:    stats.Couchdb.RequestTime.Mean,
 | |
| 		Stddev:  stats.Couchdb.RequestTime.Stddev,
 | |
| 		Min:     stats.Couchdb.RequestTime.Min,
 | |
| 		Max:     stats.Couchdb.RequestTime.Max,
 | |
| 	}
 | |
| 
 | |
| 	httpdRequestMethodsPut := stats.HttpdRequestMethods.Put
 | |
| 	httpdRequestMethodsGet := stats.HttpdRequestMethods.Get
 | |
| 	httpdRequestMethodsCopy := stats.HttpdRequestMethods.Copy
 | |
| 	httpdRequestMethodsDelete := stats.HttpdRequestMethods.Delete
 | |
| 	httpdRequestMethodsPost := stats.HttpdRequestMethods.Post
 | |
| 	httpdRequestMethodsHead := stats.HttpdRequestMethods.Head
 | |
| 
 | |
| 	httpdStatusCodesStatus200 := stats.HttpdStatusCodes.Status200
 | |
| 	httpdStatusCodesStatus201 := stats.HttpdStatusCodes.Status201
 | |
| 	httpdStatusCodesStatus202 := stats.HttpdStatusCodes.Status202
 | |
| 	httpdStatusCodesStatus301 := stats.HttpdStatusCodes.Status301
 | |
| 	httpdStatusCodesStatus304 := stats.HttpdStatusCodes.Status304
 | |
| 	httpdStatusCodesStatus400 := stats.HttpdStatusCodes.Status400
 | |
| 	httpdStatusCodesStatus401 := stats.HttpdStatusCodes.Status401
 | |
| 	httpdStatusCodesStatus403 := stats.HttpdStatusCodes.Status403
 | |
| 	httpdStatusCodesStatus404 := stats.HttpdStatusCodes.Status404
 | |
| 	httpdStatusCodesStatus405 := stats.HttpdStatusCodes.Status405
 | |
| 	httpdStatusCodesStatus409 := stats.HttpdStatusCodes.Status409
 | |
| 	httpdStatusCodesStatus412 := stats.HttpdStatusCodes.Status412
 | |
| 	httpdStatusCodesStatus500 := stats.HttpdStatusCodes.Status500
 | |
| 	// check if couchdb2.0 is used
 | |
| 	if stats.Couchdb.HttpdRequestMethods.Get.Value != nil {
 | |
| 		requestTime = stats.Couchdb.RequestTime.Value
 | |
| 
 | |
| 		httpdRequestMethodsPut = stats.Couchdb.HttpdRequestMethods.Put
 | |
| 		httpdRequestMethodsGet = stats.Couchdb.HttpdRequestMethods.Get
 | |
| 		httpdRequestMethodsCopy = stats.Couchdb.HttpdRequestMethods.Copy
 | |
| 		httpdRequestMethodsDelete = stats.Couchdb.HttpdRequestMethods.Delete
 | |
| 		httpdRequestMethodsPost = stats.Couchdb.HttpdRequestMethods.Post
 | |
| 		httpdRequestMethodsHead = stats.Couchdb.HttpdRequestMethods.Head
 | |
| 
 | |
| 		httpdStatusCodesStatus200 = stats.Couchdb.HttpdStatusCodes.Status200
 | |
| 		httpdStatusCodesStatus201 = stats.Couchdb.HttpdStatusCodes.Status201
 | |
| 		httpdStatusCodesStatus202 = stats.Couchdb.HttpdStatusCodes.Status202
 | |
| 		httpdStatusCodesStatus301 = stats.Couchdb.HttpdStatusCodes.Status301
 | |
| 		httpdStatusCodesStatus304 = stats.Couchdb.HttpdStatusCodes.Status304
 | |
| 		httpdStatusCodesStatus400 = stats.Couchdb.HttpdStatusCodes.Status400
 | |
| 		httpdStatusCodesStatus401 = stats.Couchdb.HttpdStatusCodes.Status401
 | |
| 		httpdStatusCodesStatus403 = stats.Couchdb.HttpdStatusCodes.Status403
 | |
| 		httpdStatusCodesStatus404 = stats.Couchdb.HttpdStatusCodes.Status404
 | |
| 		httpdStatusCodesStatus405 = stats.Couchdb.HttpdStatusCodes.Status405
 | |
| 		httpdStatusCodesStatus409 = stats.Couchdb.HttpdStatusCodes.Status409
 | |
| 		httpdStatusCodesStatus412 = stats.Couchdb.HttpdStatusCodes.Status412
 | |
| 		httpdStatusCodesStatus500 = stats.Couchdb.HttpdStatusCodes.Status500
 | |
| 	}
 | |
| 
 | |
| 	// CouchDB meta stats:
 | |
| 	c.generateFields(fields, "couchdb_auth_cache_misses", stats.Couchdb.AuthCacheMisses)
 | |
| 	c.generateFields(fields, "couchdb_database_writes", stats.Couchdb.DatabaseWrites)
 | |
| 	c.generateFields(fields, "couchdb_open_databases", stats.Couchdb.OpenDatabases)
 | |
| 	c.generateFields(fields, "couchdb_auth_cache_hits", stats.Couchdb.AuthCacheHits)
 | |
| 	c.generateFields(fields, "couchdb_request_time", requestTime)
 | |
| 	c.generateFields(fields, "couchdb_database_reads", stats.Couchdb.DatabaseReads)
 | |
| 	c.generateFields(fields, "couchdb_open_os_files", stats.Couchdb.OpenOsFiles)
 | |
| 
 | |
| 	// http request methods stats:
 | |
| 	c.generateFields(fields, "httpd_request_methods_put", httpdRequestMethodsPut)
 | |
| 	c.generateFields(fields, "httpd_request_methods_get", httpdRequestMethodsGet)
 | |
| 	c.generateFields(fields, "httpd_request_methods_copy", httpdRequestMethodsCopy)
 | |
| 	c.generateFields(fields, "httpd_request_methods_delete", httpdRequestMethodsDelete)
 | |
| 	c.generateFields(fields, "httpd_request_methods_post", httpdRequestMethodsPost)
 | |
| 	c.generateFields(fields, "httpd_request_methods_head", httpdRequestMethodsHead)
 | |
| 
 | |
| 	// status code stats:
 | |
| 	c.generateFields(fields, "httpd_status_codes_200", httpdStatusCodesStatus200)
 | |
| 	c.generateFields(fields, "httpd_status_codes_201", httpdStatusCodesStatus201)
 | |
| 	c.generateFields(fields, "httpd_status_codes_202", httpdStatusCodesStatus202)
 | |
| 	c.generateFields(fields, "httpd_status_codes_301", httpdStatusCodesStatus301)
 | |
| 	c.generateFields(fields, "httpd_status_codes_304", httpdStatusCodesStatus304)
 | |
| 	c.generateFields(fields, "httpd_status_codes_400", httpdStatusCodesStatus400)
 | |
| 	c.generateFields(fields, "httpd_status_codes_401", httpdStatusCodesStatus401)
 | |
| 	c.generateFields(fields, "httpd_status_codes_403", httpdStatusCodesStatus403)
 | |
| 	c.generateFields(fields, "httpd_status_codes_404", httpdStatusCodesStatus404)
 | |
| 	c.generateFields(fields, "httpd_status_codes_405", httpdStatusCodesStatus405)
 | |
| 	c.generateFields(fields, "httpd_status_codes_409", httpdStatusCodesStatus409)
 | |
| 	c.generateFields(fields, "httpd_status_codes_412", httpdStatusCodesStatus412)
 | |
| 	c.generateFields(fields, "httpd_status_codes_500", httpdStatusCodesStatus500)
 | |
| 
 | |
| 	// httpd stats:
 | |
| 	c.generateFields(fields, "httpd_clients_requesting_changes", stats.Httpd.ClientsRequestingChanges)
 | |
| 	c.generateFields(fields, "httpd_temporary_view_reads", stats.Httpd.TemporaryViewReads)
 | |
| 	c.generateFields(fields, "httpd_requests", stats.Httpd.Requests)
 | |
| 	c.generateFields(fields, "httpd_bulk_requests", stats.Httpd.BulkRequests)
 | |
| 	c.generateFields(fields, "httpd_view_reads", stats.Httpd.ViewReads)
 | |
| 
 | |
| 	tags := map[string]string{
 | |
| 		"server": host,
 | |
| 	}
 | |
| 	accumulator.AddFields("couchdb", fields, tags)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *CouchDB) generateFields(fields map[string]interface{}, prefix string, obj metaData) {
 | |
| 	if obj.Value != nil {
 | |
| 		fields[prefix+"_value"] = *obj.Value
 | |
| 	}
 | |
| 	if obj.Current != nil {
 | |
| 		fields[prefix+"_current"] = *obj.Current
 | |
| 	}
 | |
| 	if obj.Sum != nil {
 | |
| 		fields[prefix+"_sum"] = *obj.Sum
 | |
| 	}
 | |
| 	if obj.Mean != nil {
 | |
| 		fields[prefix+"_mean"] = *obj.Mean
 | |
| 	}
 | |
| 	if obj.Stddev != nil {
 | |
| 		fields[prefix+"_stddev"] = *obj.Stddev
 | |
| 	}
 | |
| 	if obj.Min != nil {
 | |
| 		fields[prefix+"_min"] = *obj.Min
 | |
| 	}
 | |
| 	if obj.Max != nil {
 | |
| 		fields[prefix+"_max"] = *obj.Max
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	inputs.Add("couchdb", func() telegraf.Input {
 | |
| 		return &CouchDB{
 | |
| 			client: &http.Client{
 | |
| 				Transport: &http.Transport{
 | |
| 					ResponseHeaderTimeout: time.Duration(3 * time.Second),
 | |
| 				},
 | |
| 				Timeout: time.Duration(4 * time.Second),
 | |
| 			},
 | |
| 		}
 | |
| 	})
 | |
| }
 |