From 87423307a8fdf94f2c4f59f95ea6839e91a6aec2 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Tue, 1 Nov 2016 12:45:37 -0700 Subject: [PATCH] Update `circonus-labs/circonus-gometrics`. --- .../circonus-gometrics/DEVINFO.md | 69 --------- .../circonus-labs/circonus-gometrics/LICENSE | 28 ++++ .../circonus-gometrics/README.md | 133 ++++++++++++----- .../circonus-gometrics/api/api.go | 88 +++++++----- .../circonus-gometrics/api/broker.go | 17 ++- .../circonus-gometrics/api/check.go | 44 ++++-- .../circonus-gometrics/api/checkbundle.go | 39 +++-- .../circonus-gometrics/checkmgr/broker.go | 6 +- .../circonus-gometrics/checkmgr/cert.go | 7 +- .../circonus-gometrics/checkmgr/check.go | 75 +++++++++- .../circonus-gometrics/checkmgr/checkmgr.go | 54 ++++--- .../circonus-gometrics/checkmgr/metrics.go | 136 +++++++++++++++--- .../circonus-gometrics/circonus-gometrics.go | 81 ++++++++--- .../circonus-gometrics/counter.go | 4 + .../circonus-labs/circonus-gometrics/gauge.go | 48 ++++++- .../circonus-gometrics/histogram.go | 20 +-- .../circonus-gometrics/metrics.go | 15 ++ .../circonus-gometrics/submit.go | 56 ++++++-- .../circonus-labs/circonus-gometrics/text.go | 4 + .../circonus-labs/circonus-gometrics/tools.go | 4 + .../circonus-labs/circonus-gometrics/util.go | 34 ++++- vendor/vendor.json | 18 +-- 22 files changed, 717 insertions(+), 263 deletions(-) delete mode 100644 vendor/github.com/circonus-labs/circonus-gometrics/DEVINFO.md create mode 100644 vendor/github.com/circonus-labs/circonus-gometrics/LICENSE create mode 100644 vendor/github.com/circonus-labs/circonus-gometrics/metrics.go diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/DEVINFO.md b/vendor/github.com/circonus-labs/circonus-gometrics/DEVINFO.md deleted file mode 100644 index b41e8a04e..000000000 --- a/vendor/github.com/circonus-labs/circonus-gometrics/DEVINFO.md +++ /dev/null @@ -1,69 +0,0 @@ -# Setting up dev/test environment - -Get go installed and environment configured - -```sh - -cd $GOPATH -mkdir -pv src/github.com/{hashicorp,armon,circonus-labs} - -cd $GOPATH/src/github.com/hashicorp -git clone https://github.com/maier/consul.git - -cd $GOPATH/src/github.com/armon -git clone https://github.com/maier/go-metrics.git - -cd $GOPATH/src/github.com/circonus-labs -git clone https://github.com/maier/circonus-gometrics.git - - -cd $GOPATH/src/github.com/hashicorp/consul -make dev -``` - -In `$GOPATH/src/github.com/hashicorp/consul/bin` is the binary just created. - -Create a consul configuration file somewhere (e.g. ~/testconfig.json) and add any applicable configuration settings. As an example: - -```json -{ - "datacenter": "mcfl", - "server": true, - "log_level": "debug", - "telemetry": { - "statsd_address": "127.0.0.1:8125", - "circonus_api_token": "...", - "circonus_api_host": "..." - } -} -``` - -StatsD was used as a check to see what metrics consul was sending and what metrics circonus was receiving. So, it can safely be elided. - -Fill in appropriate cirocnus specific settings: - -* circonus_api_token - required -* circonus_api_app - optional, default is circonus-gometrics -* circonus_api_host - optional, default is api.circonus.com (for dev stuff yon can use "http://..." to circumvent ssl) -* circonus_submission_url - optional -* circonus_submission_interval - optional, seconds, defaults to 10 seconds -* circonus_check_id - optional -* circonus_broker_id - optional (unless you want to use the public one, then add it) - -The actual circonus-gometrics package has more configuraiton options, the above are exposed in the consul configuration. - -CirconusMetrics.InstanceId is derived from consul's config.NodeName and config.Datacenter -CirconusMetrics.SearchTag is hardcoded as 'service:consul' - -The defaults are taken for other options. - ---- - -To run after creating the config: - -`$GOPATH/src/github.com/hashicorp/consul/bin/consul agent -dev -config-file ` - -or, to add the ui (localhost:8500) - -`$GOPATH/src/github.com/hashicorp/consul/bin/consul agent -dev -ui -config-file ` - diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/LICENSE b/vendor/github.com/circonus-labs/circonus-gometrics/LICENSE new file mode 100644 index 000000000..761798c3b --- /dev/null +++ b/vendor/github.com/circonus-labs/circonus-gometrics/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2016, Circonus, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name Circonus, Inc. nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/README.md b/vendor/github.com/circonus-labs/circonus-gometrics/README.md index f0889037b..77daae05b 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/README.md +++ b/vendor/github.com/circonus-labs/circonus-gometrics/README.md @@ -24,17 +24,37 @@ import ( func main() { - log.Println("Configuring cgm") + logger := log.New(os.Stdout, "", log.LstdFlags) + + logger.Println("Configuring cgm") cmc := &cgm.Config{} // Interval at which metrics are submitted to Circonus, default: 10 seconds - cmc.Interval = "10s" // 10 seconds + // cmc.Interval = "10s" // 10 seconds + // Enable debug messages, default: false - cmc.Debug = false + cmc.Debug = true + // Send debug messages to specific log.Logger instance // default: if debug stderr, else, discard - //cmc.CheckManager.Log = ... + cmc.Log = logger + + // Reset counter metrics after each submission, default: "true" + // Change to "false" to retain (and continue submitting) the last value. + // cmc.ResetCounters = "true" + + // Reset gauge metrics after each submission, default: "true" + // Change to "false" to retain (and continue submitting) the last value. + // cmc.ResetGauges = "true" + + // Reset histogram metrics after each submission, default: "true" + // Change to "false" to retain (and continue submitting) the last value. + // cmc.ResetHistograms = "true" + + // Reset text metrics after each submission, default: "true" + // Change to "false" to retain (and continue submitting) the last value. + // cmc.ResetText = "true" // Circonus API configuration options // @@ -53,10 +73,12 @@ func main() { // otherwise: if an applicable check is NOT specified or found, an // attempt will be made to automatically create one // - // Pre-existing httptrap check submission_url + // Submission URL for an existing [httptrap] check cmc.CheckManager.Check.SubmissionURL = os.Getenv("CIRCONUS_SUBMISION_URL") - // Pre-existing httptrap check id (check not check bundle) - cmc.CheckManager.Check.ID = "" + + // ID of an existing [httptrap] check (note: check id not check bundle id) + cmc.CheckManager.Check.ID = os.Getenv("CIRCONUS_CHECK_ID") + // if neither a submission url nor check id are provided, an attempt will be made to find an existing // httptrap check by using the circonus api to search for a check matching the following criteria: // an active check, @@ -68,49 +90,63 @@ func main() { // default: 'hostname':'program name' // note: for a persistent instance that is ephemeral or transient where metric continuity is // desired set this explicitly so that the current hostname will not be used. - cmc.CheckManager.Check.InstanceID = "" - // Search tag - a specific tag which, when coupled with the instanceId serves to identify the - // origin and/or grouping of the metrics - // default: service:application name (e.g. service:consul) - cmc.CheckManager.Check.SearchTag = "" + // cmc.CheckManager.Check.InstanceID = "" + + // Search tag - specific tag(s) used in conjunction with isntanceId to search for an + // existing check. comma separated string of tags (spaces will be removed, no commas + // in tag elements). + // default: service:application name (e.g. service:consul service:nomad etc.) + // cmc.CheckManager.Check.SearchTag = "" + // Check secret, default: generated when a check needs to be created - cmc.CheckManager.Check.Secret = "" - // Check tags, array of strings, additional tags to add to a new check, default: none - //cmc.CheckManager.Check.Tags = []string{"category:tagname"} + // cmc.CheckManager.Check.Secret = "" + + // Additional tag(s) to add when *creating* a check. comma separated string + // of tags (spaces will be removed, no commas in tag elements). + // (e.g. group:abc or service_role:agent,group:xyz). + // default: none + // cmc.CheckManager.Check.Tags = "" + // max amount of time to to hold on to a submission url // when a given submission fails (due to retries) if the // time the url was last updated is > than this, the trap // url will be refreshed (e.g. if the broker is changed // in the UI) default 5 minutes - cmc.CheckManager.Check.MaxURLAge = "5m" + // cmc.CheckManager.Check.MaxURLAge = "5m" + // custom display name for check, default: "InstanceId /cgm" - cmc.CheckManager.Check.DisplayName = "" + // cmc.CheckManager.Check.DisplayName = "" + // force metric activation - if a metric has been disabled via the UI // the default behavior is to *not* re-activate the metric; this setting // overrides the behavior and will re-activate the metric when it is // encountered. "(true|false)", default "false" - cmc.CheckManager.Check.ForceMetricActivation = "false" + // cmc.CheckManager.Check.ForceMetricActivation = "false" // Broker configuration options // // Broker ID of specific broker to use, default: random enterprise broker or // Circonus default if no enterprise brokers are available. // default: only used if set - cmc.CheckManager.Broker.ID = "" - // used to select a broker with the same tag (e.g. can be used to dictate that a broker - // serving a specific location should be used. "dc:sfo", "location:new_york", "zone:us-west") - // if more than one broker has the tag, one will be selected randomly from the resulting list - // default: not used unless != "" - cmc.CheckManager.Broker.SelectTag = "" + // cmc.CheckManager.Broker.ID = "" + + // used to select a broker with the same tag(s) (e.g. can be used to dictate that a broker + // serving a specific location should be used. "dc:sfo", "loc:nyc,dc:nyc01", "zone:us-west") + // if more than one broker has the tag(s), one will be selected randomly from the resulting + // list. comma separated string of tags (spaces will be removed, no commas in tag elements). + // default: none + // cmc.CheckManager.Broker.SelectTag = "" + // longest time to wait for a broker connection (if latency is > the broker will // be considered invalid and not available for selection.), default: 500 milliseconds - cmc.CheckManager.Broker.MaxResponseTime = "500ms" - // if broker Id or SelectTag are not specified, a broker will be selected randomly + // cmc.CheckManager.Broker.MaxResponseTime = "500ms" + + // note: if broker Id or SelectTag are not specified, a broker will be selected randomly // from the list of brokers available to the api token. enterprise brokers take precedence // viable brokers are "active", have the "httptrap" module enabled, are reachable and respond // within MaxResponseTime. - log.Println("Creating new cgm instance") + logger.Println("Creating new cgm instance") metrics, err := cgm.NewCirconusMetrics(cmc) if err != nil { @@ -120,23 +156,44 @@ func main() { src := rand.NewSource(time.Now().UnixNano()) rnd := rand.New(src) - log.Println("Starting cgm internal auto-flush timer") + logger.Println("Starting cgm internal auto-flush timer") metrics.Start() - log.Println("Starting to send metrics") + logger.Println("Adding ctrl-c trap") + c := make(chan os.Signal, 2) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + logger.Println("Received CTRL-C, flushing outstanding metrics before exit") + metrics.Flush() + os.Exit(0) + }() - // number of "sets" of metrics to send (a minute worth) + // Add metric tags (append to any existing tags on specified metric) + metrics.AddMetricTags("foo", []string{"cgm:test"}) + metrics.AddMetricTags("baz", []string{"cgm:test"}) + + logger.Println("Starting to send metrics") + + // number of "sets" of metrics to send max := 60 for i := 1; i < max; i++ { - log.Printf("\tmetric set %d of %d", i, 60) - metrics.Timing("ding", rnd.Float64()*10) - metrics.Increment("dong") - metrics.Gauge("dang", 10) - time.Sleep(1000 * time.Millisecond) + logger.Printf("\tmetric set %d of %d", i, 60) + + metrics.Timing("foo", rnd.Float64()*10) + metrics.Increment("bar") + metrics.Gauge("baz", 10) + + if i == 35 { + // Set metric tags (overwrite current tags on specified metric) + metrics.SetMetricTags("baz", []string{"cgm:reset_test", "cgm:test2"}) + } + + time.Sleep(time.Second) } - log.Println("Flushing any outstanding metrics manually") + logger.Println("Flushing any outstanding metrics manually") metrics.Flush() } @@ -163,7 +220,7 @@ import ( func main() { cmc := &cgm.Config{} cmc.CheckManager.API.TokenKey = os.Getenv("CIRCONUS_API_TOKEN") - + metrics, err := cgm.NewCirconusMetrics(cmc) if err != nil { panic(err) @@ -177,3 +234,5 @@ func main() { } ``` + +Unless otherwise noted, the source files are distributed under the BSD-style license found in the LICENSE file. diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/api/api.go b/vendor/github.com/circonus-labs/circonus-gometrics/api/api.go index 30fe7fbb0..f640c54d0 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/api/api.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/api/api.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + // Package api provides methods for interacting with the Circonus API package api @@ -20,9 +24,9 @@ const ( // a few sensible defaults defaultAPIURL = "https://api.circonus.com/v2" defaultAPIApp = "circonus-gometrics" - minRetryWait = 10 * time.Millisecond - maxRetryWait = 50 * time.Millisecond - maxRetries = 3 + minRetryWait = 1 * time.Second + maxRetryWait = 15 * time.Second + maxRetries = 4 // equating to 1 + maxRetries total attempts ) // TokenKeyType - Circonus API Token key @@ -43,8 +47,11 @@ type URLType string // SearchQueryType search query type SearchQueryType string -// SearchTagType search/select tag type -type SearchTagType string +// SearchFilterType search filter +type SearchFilterType string + +// TagType search/select/custom tag(s) type +type TagType []string // Config options for Circonus API type Config struct { @@ -99,12 +106,13 @@ func NewAPI(ac *Config) (*API, error) { a := &API{apiURL, key, app, ac.Debug, ac.Log} + a.Debug = ac.Debug + a.Log = ac.Log + if a.Debug && a.Log == nil { + a.Log = log.New(os.Stderr, "", log.LstdFlags) + } if a.Log == nil { - if a.Debug { - a.Log = log.New(os.Stderr, "", log.LstdFlags) - } else { - a.Log = log.New(ioutil.Discard, "", log.LstdFlags) - } + a.Log = log.New(ioutil.Discard, "", log.LstdFlags) } return a, nil @@ -152,40 +160,56 @@ func (a *API) apiCall(reqMethod string, reqPath string, data []byte) ([]byte, er req.Header.Add("X-Circonus-Auth-Token", string(a.key)) req.Header.Add("X-Circonus-App-Name", string(a.app)) + // keep last HTTP error in the event of retry failure + var lastHTTPError error + retryPolicy := func(resp *http.Response, err error) (bool, error) { + if err != nil { + lastHTTPError = err + return true, err + } + // Check the response code. We retry on 500-range responses to allow + // the server time to recover, as 500's are typically not permanent + // errors and may relate to outages on the server side. This will catch + // invalid response codes as well, like 0 and 999. + // Retry on 429 (rate limit) as well. + if resp.StatusCode == 0 || resp.StatusCode >= 500 || resp.StatusCode == 429 { + body, readErr := ioutil.ReadAll(resp.Body) + if readErr != nil { + lastHTTPError = fmt.Errorf("- last HTTP error: %d %+v", resp.StatusCode, readErr) + } else { + lastHTTPError = fmt.Errorf("- last HTTP error: %d %s", resp.StatusCode, string(body)) + } + return true, nil + } + return false, nil + } + client := retryablehttp.NewClient() client.RetryWaitMin = minRetryWait client.RetryWaitMax = maxRetryWait client.RetryMax = maxRetries - client.Logger = a.Log + // retryablehttp only groks log or no log + // but, outputs everything as [DEBUG] messages + if a.Debug { + client.Logger = a.Log + } else { + client.Logger = log.New(ioutil.Discard, "", log.LstdFlags) + } + + client.CheckRetry = retryPolicy resp, err := client.Do(req) if err != nil { - stdClient := &http.Client{} - dataReader.Seek(0, 0) - stdRequest, _ := http.NewRequest(reqMethod, reqURL, dataReader) - stdRequest.Header.Add("Accept", "application/json") - stdRequest.Header.Add("X-Circonus-Auth-Token", string(a.key)) - stdRequest.Header.Add("X-Circonus-App-Name", string(a.app)) - res, errSC := stdClient.Do(stdRequest) - if errSC != nil { - return nil, fmt.Errorf("[ERROR] fetching %s: %s", reqURL, errSC) + if lastHTTPError != nil { + return nil, lastHTTPError } - - if res != nil && res.Body != nil { - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - if a.Debug { - a.Log.Printf("[DEBUG] %v\n", string(body)) - } - return nil, fmt.Errorf("[ERROR] %s", string(body)) - } - - return nil, fmt.Errorf("[ERROR] fetching %s: %s", reqURL, err) + return nil, fmt.Errorf("[ERROR] %s: %+v", reqURL, err) } + defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("[ERROR] reading body %+v", err) + return nil, fmt.Errorf("[ERROR] reading response %+v", err) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/api/broker.go b/vendor/github.com/circonus-labs/circonus-gometrics/api/broker.go index f04dbf60f..90d5f3259 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/api/broker.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/api/broker.go @@ -1,8 +1,13 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package api import ( "encoding/json" "fmt" + "strings" ) // BrokerDetail instance attributes @@ -51,8 +56,8 @@ func (a *API) FetchBrokerByCID(cid CIDType) (*Broker, error) { } // FetchBrokerListByTag return list of brokers with a specific tag -func (a *API) FetchBrokerListByTag(searchTag SearchTagType) ([]Broker, error) { - query := SearchQueryType(fmt.Sprintf("f__tags_has=%s", searchTag)) +func (a *API) FetchBrokerListByTag(searchTag TagType) ([]Broker, error) { + query := SearchQueryType(fmt.Sprintf("f__tags_has=%s", strings.Replace(strings.Join(searchTag, ","), ",", "&f__tags_has=", -1))) return a.BrokerSearch(query) } @@ -66,7 +71,9 @@ func (a *API) BrokerSearch(query SearchQueryType) ([]Broker, error) { } var brokers []Broker - json.Unmarshal(result, &brokers) + if err := json.Unmarshal(result, &brokers); err != nil { + return nil, err + } return brokers, nil } @@ -79,7 +86,9 @@ func (a *API) FetchBrokerList() ([]Broker, error) { } var response []Broker - json.Unmarshal(result, &response) + if err := json.Unmarshal(result, &response); err != nil { + return nil, err + } return response, nil } diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/api/check.go b/vendor/github.com/circonus-labs/circonus-gometrics/api/check.go index 89b833731..0887caf3d 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/api/check.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/api/check.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package api import ( @@ -36,7 +40,9 @@ func (a *API) FetchCheckByCID(cid CIDType) (*Check, error) { } check := new(Check) - json.Unmarshal(result, check) + if err := json.Unmarshal(result, check); err != nil { + return nil, err + } return check, nil } @@ -52,21 +58,20 @@ func (a *API) FetchCheckBySubmissionURL(submissionURL URLType) (*Check, error) { // valid trap url: scheme://host[:port]/module/httptrap/UUID/secret // does it smell like a valid trap url path - if u.Path[:17] != "/module/httptrap/" { + if !strings.Contains(u.Path, "/module/httptrap/") { return nil, fmt.Errorf("[ERROR] Invalid submission URL '%s', unrecognized path", submissionURL) } - // extract uuid/secret - pathParts := strings.Split(u.Path[17:len(u.Path)], "/") + // extract uuid + pathParts := strings.Split(strings.Replace(u.Path, "/module/httptrap/", "", 1), "/") if len(pathParts) != 2 { return nil, fmt.Errorf("[ERROR] Invalid submission URL '%s', UUID not where expected", submissionURL) } - uuid := pathParts[0] - query := SearchQueryType(fmt.Sprintf("f__check_uuid=%s", uuid)) + filter := SearchFilterType(fmt.Sprintf("f__check_uuid=%s", uuid)) - checks, err := a.CheckSearch(query) + checks, err := a.CheckFilterSearch(filter) if err != nil { return nil, err } @@ -93,9 +98,9 @@ func (a *API) FetchCheckBySubmissionURL(submissionURL URLType) (*Check, error) { } -// CheckSearch returns a list of checks matching a query/filter +// CheckSearch returns a list of checks matching a search query func (a *API) CheckSearch(query SearchQueryType) ([]Check, error) { - queryURL := fmt.Sprintf("/check?%s", string(query)) + queryURL := fmt.Sprintf("/check?search=%s", string(query)) result, err := a.Get(queryURL) if err != nil { @@ -103,7 +108,26 @@ func (a *API) CheckSearch(query SearchQueryType) ([]Check, error) { } var checks []Check - json.Unmarshal(result, &checks) + if err := json.Unmarshal(result, &checks); err != nil { + return nil, err + } + + return checks, nil +} + +// CheckFilterSearch returns a list of checks matching a filter +func (a *API) CheckFilterSearch(filter SearchFilterType) ([]Check, error) { + filterURL := fmt.Sprintf("/check?%s", string(filter)) + + result, err := a.Get(filterURL) + if err != nil { + return nil, err + } + + var checks []Check + if err := json.Unmarshal(result, &checks); err != nil { + return nil, err + } return checks, nil } diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/api/checkbundle.go b/vendor/github.com/circonus-labs/circonus-gometrics/api/checkbundle.go index 247bf9f49..e5faae0fb 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/api/checkbundle.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/api/checkbundle.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package api import ( @@ -10,14 +14,22 @@ type CheckBundleConfig struct { AsyncMetrics bool `json:"async_metrics"` Secret string `json:"secret"` SubmissionURL string `json:"submission_url"` + ReverseSecret string `json:"reverse:secret_key"` + HTTPVersion string `json:"http_version,omitempty"` + Method string `json:"method,omitempty"` + Payload string `json:"payload,omitempty"` + Port string `json:"port,omitempty"` + ReadLimit string `json:"read_limit,omitempty"` + URL string `json:"url,omitempty"` } // CheckBundleMetric individual metric configuration type CheckBundleMetric struct { - Name string `json:"name"` - Type string `json:"type"` - Units string `json:"units"` - Status string `json:"status"` + Name string `json:"name"` + Type string `json:"type"` + Units string `json:"units"` + Status string `json:"status"` + Tags []string `json:"tags"` } // CheckBundle definition @@ -28,7 +40,7 @@ type CheckBundle struct { Created int `json:"_created,omitempty"` LastModified int `json:"_last_modified,omitempty"` LastModifedBy string `json:"_last_modifed_by,omitempty"` - ReverseConnectUrls []string `json:"_reverse_connection_urls,omitempty"` + ReverseConnectURLs []string `json:"_reverse_connection_urls"` Brokers []string `json:"brokers"` Config CheckBundleConfig `json:"config"` DisplayName string `json:"display_name"` @@ -57,7 +69,9 @@ func (a *API) FetchCheckBundleByCID(cid CIDType) (*CheckBundle, error) { } checkBundle := &CheckBundle{} - json.Unmarshal(result, checkBundle) + if err := json.Unmarshal(result, checkBundle); err != nil { + return nil, err + } return checkBundle, nil } @@ -73,9 +87,8 @@ func (a *API) CheckBundleSearch(searchCriteria SearchQueryType) ([]CheckBundle, } var results []CheckBundle - err = json.Unmarshal(response, &results) - if err != nil { - return nil, fmt.Errorf("[ERROR] Parsing JSON response %+v", err) + if err := json.Unmarshal(response, &results); err != nil { + return nil, err } return results, nil @@ -94,8 +107,7 @@ func (a *API) CreateCheckBundle(config CheckBundle) (*CheckBundle, error) { } checkBundle := &CheckBundle{} - err = json.Unmarshal(response, checkBundle) - if err != nil { + if err := json.Unmarshal(response, checkBundle); err != nil { return nil, err } @@ -105,7 +117,7 @@ func (a *API) CreateCheckBundle(config CheckBundle) (*CheckBundle, error) { // UpdateCheckBundle updates a check bundle configuration func (a *API) UpdateCheckBundle(config *CheckBundle) (*CheckBundle, error) { if a.Debug { - a.Log.Printf("[DEBUG] Updating check bundle with new metrics.") + a.Log.Printf("[DEBUG] Updating check bundle.") } cfgJSON, err := json.Marshal(config) @@ -119,8 +131,7 @@ func (a *API) UpdateCheckBundle(config *CheckBundle) (*CheckBundle, error) { } checkBundle := &CheckBundle{} - err = json.Unmarshal(response, checkBundle) - if err != nil { + if err := json.Unmarshal(response, checkBundle); err != nil { return nil, err } diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/broker.go b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/broker.go index 0213c0ac4..9c48024da 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/broker.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/broker.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package checkmgr import ( @@ -75,7 +79,7 @@ func (cm *CheckManager) selectBroker() (*api.Broker, error) { var brokerList []api.Broker var err error - if cm.brokerSelectTag != "" { + if len(cm.brokerSelectTag) > 0 { brokerList, err = cm.apih.FetchBrokerListByTag(cm.brokerSelectTag) if err != nil { return nil, err diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/cert.go b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/cert.go index c5a6736f2..c10ffd12b 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/cert.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/cert.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package checkmgr import ( @@ -70,8 +74,7 @@ func (cm *CheckManager) fetchCert() ([]byte, error) { } cadata := new(CACert) - err = json.Unmarshal(response, cadata) - if err != nil { + if err := json.Unmarshal(response, cadata); err != nil { return nil, err } diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/check.go b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/check.go index 4eefa936e..9ccbb060e 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/check.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/check.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package checkmgr import ( @@ -13,6 +17,50 @@ import ( "github.com/circonus-labs/circonus-gometrics/api" ) +// UpdateCheck determines if the check needs to be updated (new metrics, tags, etc.) +func (cm *CheckManager) UpdateCheck(newMetrics map[string]*api.CheckBundleMetric) { + // only if check manager is enabled + if !cm.enabled { + return + } + + // only if checkBundle has been populated + if cm.checkBundle == nil { + return + } + + cm.addNewMetrics(newMetrics) + + if len(cm.metricTags) > 0 { + for metricName, metricTags := range cm.metricTags { + // note: if a tag has been added (queued) for a metric which never gets sent + // the tags will be discarded. (setting tags does not *create* metrics.) + cm.AddMetricTags(metricName, metricTags, false) + cm.mtmu.Lock() + delete(cm.metricTags, metricName) + cm.mtmu.Unlock() + } + } + + if cm.forceCheckUpdate { + + newCheckBundle, err := cm.apih.UpdateCheckBundle(cm.checkBundle) + if err != nil { + cm.Log.Printf("[ERROR] updating check bundle %v", err) + return + } + + cm.forceCheckUpdate = false + cm.cbmu.Lock() + cm.checkBundle = newCheckBundle + cm.cbmu.Unlock() + cm.inventoryMetrics() + + cm.cbmu.Unlock() + } + +} + // Initialize CirconusMetrics instance. Attempt to find a check otherwise create one. // use cases: // @@ -28,6 +76,8 @@ func (cm *CheckManager) initializeTrapURL() error { cm.trapmu.Lock() defer cm.trapmu.Unlock() + // special case short-circuit: just send to a url, no check management + // up to user to ensure that if url is https that it will work (e.g. not self-signed) if cm.checkSubmissionURL != "" { if !cm.enabled { cm.trapURL = cm.checkSubmissionURL @@ -50,6 +100,9 @@ func (cm *CheckManager) initializeTrapURL() error { if err != nil { return err } + if !check.Active { + return fmt.Errorf("[ERROR] Check ID %v is not active", check.Cid) + } // extract check id from check object returned from looking up using submission url // set m.CheckId to the id // set m.SubmissionUrl to "" to prevent trying to search on it going forward @@ -71,10 +124,13 @@ func (cm *CheckManager) initializeTrapURL() error { if err != nil { return err } + if !check.Active { + return fmt.Errorf("[ERROR] Check ID %v is not active", check.Cid) + } } else { searchCriteria := fmt.Sprintf( "(active:1)(host:\"%s\")(type:\"%s\")(tags:%s)", - cm.checkInstanceID, cm.checkType, cm.checkSearchTag) + cm.checkInstanceID, cm.checkType, strings.Join(cm.checkSearchTag, ",")) checkBundle, err = cm.checkBundleSearch(searchCriteria) if err != nil { return err @@ -112,8 +168,19 @@ func (cm *CheckManager) initializeTrapURL() error { cm.checkBundle = checkBundle cm.inventoryMetrics() - // url to which metrics should be PUT - cm.trapURL = api.URLType(checkBundle.Config.SubmissionURL) + // determine the trap url to which metrics should be PUT + if checkBundle.Type == "httptrap" { + cm.trapURL = api.URLType(checkBundle.Config.SubmissionURL) + } else { + // build a submission_url for non-httptrap checks out of mtev_reverse url + if len(checkBundle.ReverseConnectURLs) == 0 { + return fmt.Errorf("%s is not an HTTPTRAP check and no reverse connection urls found", checkBundle.Checks[0]) + } + mtevURL := checkBundle.ReverseConnectURLs[0] + mtevURL = strings.Replace(mtevURL, "mtev_reverse", "https", 1) + mtevURL = strings.Replace(mtevURL, "check", "module/httptrap", 1) + cm.trapURL = api.URLType(fmt.Sprintf("%s/%s", mtevURL, checkBundle.Config.ReverseSecret)) + } // used when sending as "ServerName" get around certs not having IP SANS // (cert created with server name as CN but IP used in trap url) @@ -181,7 +248,7 @@ func (cm *CheckManager) createNewCheck() (*api.CheckBundle, *api.Broker, error) Notes: "", Period: 60, Status: statusActive, - Tags: append([]string{string(cm.checkSearchTag)}, cm.checkTags...), + Tags: append(cm.checkSearchTag, cm.checkTags...), Target: string(cm.checkInstanceID), Timeout: 10, Type: string(cm.checkType), diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/checkmgr.go b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/checkmgr.go index 1237ba1d4..b179aa760 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/checkmgr.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/checkmgr.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + // Package checkmgr provides a check management interace to circonus-gometrics package checkmgr @@ -12,6 +16,7 @@ import ( "os" "path" "strconv" + "strings" "sync" "time" @@ -54,7 +59,7 @@ type CheckConfig struct { // used to search for a check to use // used as check.target when creating a check InstanceID string - // unique check searching tag + // unique check searching tag (or tags) // used to search for a check to use (combined with instanceid) // used as a regular tag when creating a check SearchTag string @@ -64,7 +69,7 @@ type CheckConfig struct { Secret string // additional tags to add to a check (when creating a check) // these tags will not be added to an existing check - Tags []string + Tags string // max amount of time to to hold on to a submission url // when a given submission fails (due to retries) if the // time the url was last updated is > than this, the trap @@ -83,8 +88,8 @@ type CheckConfig struct { type BrokerConfig struct { // a specific broker id (numeric portion of cid) ID string - // a tag that can be used to select 1-n brokers from which to select - // when creating a new check (e.g. datacenter:abc) + // one or more tags used to select 1-n brokers from which to select + // when creating a new check (e.g. datacenter:abc or loc:dfw,dc:abc) SelectTag string // for a broker to be considered viable it must respond to a // connection attempt within this amount of time e.g. 200ms, 2s, 1m @@ -114,7 +119,7 @@ type CheckInstanceIDType string type CheckSecretType string // CheckTagsType check tags -type CheckTagsType []string +type CheckTagsType string // CheckDisplayNameType check display name type CheckDisplayNameType string @@ -133,20 +138,26 @@ type CheckManager struct { checkType CheckTypeType checkID api.IDType checkInstanceID CheckInstanceIDType - checkSearchTag api.SearchTagType + checkSearchTag api.TagType checkSecret CheckSecretType - checkTags CheckTagsType + checkTags api.TagType checkSubmissionURL api.URLType checkDisplayName CheckDisplayNameType forceMetricActivation bool + forceCheckUpdate bool + + // metric tags + metricTags map[string][]string + mtmu sync.Mutex // broker brokerID api.IDType - brokerSelectTag api.SearchTagType + brokerSelectTag api.TagType brokerMaxResponseTime time.Duration // state checkBundle *api.CheckBundle + cbmu sync.Mutex availableMetrics map[string]bool trapURL api.URLType trapCN BrokerCNType @@ -174,14 +185,12 @@ func NewCheckManager(cfg *Config) (*CheckManager, error) { } cm.Debug = cfg.Debug - cm.Log = cfg.Log + if cm.Debug && cm.Log == nil { + cm.Log = log.New(os.Stderr, "", log.LstdFlags) + } if cm.Log == nil { - if cm.Debug { - cm.Log = log.New(os.Stderr, "", log.LstdFlags) - } else { - cm.Log = log.New(ioutil.Discard, "", log.LstdFlags) - } + cm.Log = log.New(ioutil.Discard, "", log.LstdFlags) } if cfg.Check.SubmissionURL != "" { @@ -229,9 +238,7 @@ func NewCheckManager(cfg *Config) (*CheckManager, error) { cm.checkInstanceID = CheckInstanceIDType(cfg.Check.InstanceID) cm.checkDisplayName = CheckDisplayNameType(cfg.Check.DisplayName) - cm.checkSearchTag = api.SearchTagType(cfg.Check.SearchTag) cm.checkSecret = CheckSecretType(cfg.Check.Secret) - cm.checkTags = cfg.Check.Tags fma := defaultForceMetricActivation if cfg.Check.ForceMetricActivation != "" { @@ -252,8 +259,14 @@ func NewCheckManager(cfg *Config) (*CheckManager, error) { cm.checkInstanceID = CheckInstanceIDType(fmt.Sprintf("%s:%s", hn, an)) } - if cm.checkSearchTag == "" { - cm.checkSearchTag = api.SearchTagType(fmt.Sprintf("service:%s", an)) + if cfg.Check.SearchTag == "" { + cm.checkSearchTag = []string{fmt.Sprintf("service:%s", an)} + } else { + cm.checkSearchTag = strings.Split(strings.Replace(cfg.Check.SearchTag, " ", "", -1), ",") + } + + if cfg.Check.Tags != "" { + cm.checkTags = strings.Split(strings.Replace(cfg.Check.Tags, " ", "", -1), ",") } if cm.checkDisplayName == "" { @@ -282,7 +295,9 @@ func NewCheckManager(cfg *Config) (*CheckManager, error) { } cm.brokerID = api.IDType(id) - cm.brokerSelectTag = api.SearchTagType(cfg.Broker.SelectTag) + if cfg.Broker.SelectTag != "" { + cm.brokerSelectTag = strings.Split(strings.Replace(cfg.Broker.SelectTag, " ", "", -1), ",") + } dur = cfg.Broker.MaxResponseTime if dur == "" { @@ -296,6 +311,7 @@ func NewCheckManager(cfg *Config) (*CheckManager, error) { // metrics cm.availableMetrics = make(map[string]bool) + cm.metricTags = make(map[string][]string) if err := cm.initializeTrapURL(); err != nil { return nil, err diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/metrics.go b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/metrics.go index 046386823..f93a6f2a1 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/metrics.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/checkmgr/metrics.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package checkmgr import ( @@ -25,44 +29,106 @@ func (cm *CheckManager) ActivateMetric(name string) bool { return false } -// AddNewMetrics updates a check bundle with new metrics -func (cm *CheckManager) AddNewMetrics(newMetrics map[string]*api.CheckBundleMetric) { - // only if check manager is enabled - if !cm.enabled { - return +// AddMetricTags updates check bundle metrics with tags +func (cm *CheckManager) AddMetricTags(metricName string, tags []string, appendTags bool) bool { + tagsUpdated := false + + if len(tags) == 0 { + return tagsUpdated } - // only if checkBundle has been populated - if cm.checkBundle == nil { - return + metricFound := false + + for metricIdx, metric := range cm.checkBundle.Metrics { + if metric.Name == metricName { + metricFound = true + numNewTags := countNewTags(metric.Tags, tags) + + if numNewTags == 0 { + if appendTags { + break // no new tags to add + } else if len(metric.Tags) == len(tags) { + break // no new tags and old/new same length + } + } + + if appendTags { + metric.Tags = append(metric.Tags, tags...) + } else { + metric.Tags = tags + } + + cm.cbmu.Lock() + cm.checkBundle.Metrics[metricIdx] = metric + cm.cbmu.Unlock() + + tagsUpdated = true + } } - newCheckBundle := cm.checkBundle - numCurrMetrics := len(newCheckBundle.Metrics) + if tagsUpdated { + if cm.Debug { + action := "Set" + if appendTags { + action = "Added" + } + cm.Log.Printf("[DEBUG] %s metric tag(s) %s %v\n", action, metricName, tags) + } + cm.cbmu.Lock() + cm.forceCheckUpdate = true + cm.cbmu.Unlock() + } else { + if !metricFound { + if _, exists := cm.metricTags[metricName]; !exists { + if cm.Debug { + cm.Log.Printf("[DEBUG] Queing metric tag(s) %s %v\n", metricName, tags) + } + // queue the tags, the metric is new (e.g. not in the check yet) + cm.mtmu.Lock() + cm.metricTags[metricName] = append(cm.metricTags[metricName], tags...) + cm.mtmu.Unlock() + } + } + } + + return tagsUpdated +} + +// addNewMetrics updates a check bundle with new metrics +func (cm *CheckManager) addNewMetrics(newMetrics map[string]*api.CheckBundleMetric) bool { + updatedCheckBundle := false + + if cm.checkBundle == nil || len(newMetrics) == 0 { + return updatedCheckBundle + } + + cm.cbmu.Lock() + + numCurrMetrics := len(cm.checkBundle.Metrics) numNewMetrics := len(newMetrics) - if numCurrMetrics+numNewMetrics >= cap(newCheckBundle.Metrics) { + if numCurrMetrics+numNewMetrics >= cap(cm.checkBundle.Metrics) { nm := make([]api.CheckBundleMetric, numCurrMetrics+numNewMetrics) - copy(nm, newCheckBundle.Metrics) - newCheckBundle.Metrics = nm + copy(nm, cm.checkBundle.Metrics) + cm.checkBundle.Metrics = nm } - newCheckBundle.Metrics = newCheckBundle.Metrics[0 : numCurrMetrics+numNewMetrics] + cm.checkBundle.Metrics = cm.checkBundle.Metrics[0 : numCurrMetrics+numNewMetrics] i := 0 for _, metric := range newMetrics { - newCheckBundle.Metrics[numCurrMetrics+i] = *metric + cm.checkBundle.Metrics[numCurrMetrics+i] = *metric i++ + updatedCheckBundle = true } - checkBundle, err := cm.apih.UpdateCheckBundle(newCheckBundle) - if err != nil { - cm.Log.Printf("[ERROR] updating check bundle with new metrics %v", err) - return + if updatedCheckBundle { + cm.forceCheckUpdate = true } - cm.checkBundle = checkBundle - cm.inventoryMetrics() + cm.cbmu.Unlock() + + return updatedCheckBundle } // inventoryMetrics creates list of active metrics in check bundle @@ -73,3 +139,31 @@ func (cm *CheckManager) inventoryMetrics() { } cm.availableMetrics = availableMetrics } + +// countNewTags returns a count of new tags which do not exist in the current list of tags +func countNewTags(currTags []string, newTags []string) int { + if len(newTags) == 0 { + return 0 + } + + if len(currTags) == 0 { + return len(newTags) + } + + newTagCount := 0 + + for _, newTag := range newTags { + found := false + for _, currTag := range currTags { + if newTag == currTag { + found = true + break + } + } + if !found { + newTagCount++ + } + } + + return newTagCount +} diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/circonus-gometrics.go b/vendor/github.com/circonus-labs/circonus-gometrics/circonus-gometrics.go index b27af5289..eb15f3aaf 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/circonus-gometrics.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/circonus-gometrics.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + // Package circonusgometrics provides instrumentation for your applications in the form // of counters, gauges and histograms and allows you to publish them to // Circonus @@ -30,6 +34,7 @@ import ( "io/ioutil" "log" "os" + "strconv" "sync" "time" @@ -43,8 +48,12 @@ const ( // Config options for circonus-gometrics type Config struct { - Log *log.Logger - Debug bool + Log *log.Logger + Debug bool + ResetCounters string // reset/delete counters on flush (default true) + ResetGauges string // reset/delete gauges on flush (default true) + ResetHistograms string // reset/delete histograms on flush (default true) + ResetText string // reset/delete text on flush (default true) // API, Check and Broker configuration options CheckManager checkmgr.Config @@ -55,12 +64,16 @@ type Config struct { // CirconusMetrics state type CirconusMetrics struct { - Log *log.Logger - Debug bool - flushInterval time.Duration - flushing bool - flushmu sync.Mutex - check *checkmgr.CheckManager + Log *log.Logger + Debug bool + resetCounters bool + resetGauges bool + resetHistograms bool + resetText bool + flushInterval time.Duration + flushing bool + flushmu sync.Mutex + check *checkmgr.CheckManager counters map[string]uint64 cm sync.Mutex @@ -68,7 +81,7 @@ type CirconusMetrics struct { counterFuncs map[string]func() uint64 cfm sync.Mutex - gauges map[string]int64 + gauges map[string]string gm sync.Mutex gaugeFuncs map[string]func() int64 @@ -94,7 +107,7 @@ func NewCirconusMetrics(cfg *Config) (*CirconusMetrics, error) { cm := &CirconusMetrics{ counters: make(map[string]uint64), counterFuncs: make(map[string]func() uint64), - gauges: make(map[string]int64), + gauges: make(map[string]string), gaugeFuncs: make(map[string]func() int64), histograms: make(map[string]*Histogram), text: make(map[string]string), @@ -102,12 +115,10 @@ func NewCirconusMetrics(cfg *Config) (*CirconusMetrics, error) { } cm.Debug = cfg.Debug - if cm.Debug { - if cfg.Log == nil { - cm.Log = log.New(os.Stderr, "", log.LstdFlags) - } else { - cm.Log = cfg.Log - } + cm.Log = cfg.Log + + if cm.Debug && cfg.Log == nil { + cm.Log = log.New(os.Stderr, "", log.LstdFlags) } if cm.Log == nil { cm.Log = log.New(ioutil.Discard, "", log.LstdFlags) @@ -124,6 +135,36 @@ func NewCirconusMetrics(cfg *Config) (*CirconusMetrics, error) { } cm.flushInterval = dur + var setting bool + + cm.resetCounters = true + if cfg.ResetCounters != "" { + if setting, err = strconv.ParseBool(cfg.ResetCounters); err == nil { + cm.resetCounters = setting + } + } + + cm.resetGauges = true + if cfg.ResetGauges != "" { + if setting, err = strconv.ParseBool(cfg.ResetGauges); err == nil { + cm.resetGauges = setting + } + } + + cm.resetHistograms = true + if cfg.ResetHistograms != "" { + if setting, err = strconv.ParseBool(cfg.ResetHistograms); err == nil { + cm.resetHistograms = setting + } + } + + cm.resetText = true + if cfg.ResetText != "" { + if setting, err = strconv.ParseBool(cfg.ResetText); err == nil { + cm.resetText = setting + } + } + cfg.CheckManager.Debug = cm.Debug cfg.CheckManager.Log = cm.Log @@ -242,7 +283,13 @@ func (m *CirconusMetrics) Flush() { } } - m.submit(output, newMetrics) + if len(output) > 0 { + m.submit(output, newMetrics) + } else { + if m.Debug { + m.Log.Println("[DEBUG] No metrics to send, skipping") + } + } m.flushmu.Lock() m.flushing = false diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/counter.go b/vendor/github.com/circonus-labs/circonus-gometrics/counter.go index ef3a85c01..2b34961f1 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/counter.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/counter.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics // A Counter is a monotonically increasing unsigned integer. diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/gauge.go b/vendor/github.com/circonus-labs/circonus-gometrics/gauge.go index 3fdf7120b..b44236959 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/gauge.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/gauge.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics // A Gauge is an instantaneous measurement of a value. @@ -5,16 +9,20 @@ package circonusgometrics // Use a gauge to track metrics which increase and decrease (e.g., amount of // free memory). +import ( + "fmt" +) + // Gauge sets a gauge to a value -func (m *CirconusMetrics) Gauge(metric string, val int64) { +func (m *CirconusMetrics) Gauge(metric string, val interface{}) { m.SetGauge(metric, val) } // SetGauge sets a gauge to a value -func (m *CirconusMetrics) SetGauge(metric string, val int64) { +func (m *CirconusMetrics) SetGauge(metric string, val interface{}) { m.gm.Lock() defer m.gm.Unlock() - m.gauges[metric] = val + m.gauges[metric] = m.gaugeValString(val) } // RemoveGauge removes a gauge @@ -37,3 +45,37 @@ func (m *CirconusMetrics) RemoveGaugeFunc(metric string) { defer m.gfm.Unlock() delete(m.gaugeFuncs, metric) } + +// gaugeValString converts an interface value (of a supported type) to a string +func (m *CirconusMetrics) gaugeValString(val interface{}) string { + vs := "" + switch v := val.(type) { + default: + // ignore it, unsupported type + case int: + vs = fmt.Sprintf("%d", v) + case int8: + vs = fmt.Sprintf("%d", v) + case int16: + vs = fmt.Sprintf("%d", v) + case int32: + vs = fmt.Sprintf("%d", v) + case int64: + vs = fmt.Sprintf("%d", v) + case uint: + vs = fmt.Sprintf("%d", v) + case uint8: + vs = fmt.Sprintf("%d", v) + case uint16: + vs = fmt.Sprintf("%d", v) + case uint32: + vs = fmt.Sprintf("%d", v) + case uint64: + vs = fmt.Sprintf("%d", v) + case float32: + vs = fmt.Sprintf("%f", v) + case float64: + vs = fmt.Sprintf("%f", v) + } + return vs +} diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/histogram.go b/vendor/github.com/circonus-labs/circonus-gometrics/histogram.go index cbdd5c692..0ba1a3b23 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/histogram.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/histogram.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics import ( @@ -25,19 +29,20 @@ func (m *CirconusMetrics) RecordValue(metric string, val float64) { // SetHistogramValue adds a value to a histogram func (m *CirconusMetrics) SetHistogramValue(metric string, val float64) { - m.NewHistogram(metric) + hist := m.NewHistogram(metric) - m.histograms[metric].rw.Lock() - defer m.histograms[metric].rw.Unlock() - - m.histograms[metric].hist.RecordValue(val) + m.hm.Lock() + hist.rw.Lock() + hist.hist.RecordValue(val) + hist.rw.Unlock() + m.hm.Unlock() } // RemoveHistogram removes a histogram func (m *CirconusMetrics) RemoveHistogram(metric string) { m.hm.Lock() - defer m.hm.Unlock() delete(m.histograms, metric) + m.hm.Unlock() } // NewHistogram returns a histogram instance. @@ -67,7 +72,6 @@ func (h *Histogram) Name() string { // RecordValue records the given value to a histogram instance func (h *Histogram) RecordValue(v float64) { h.rw.Lock() - defer h.rw.Unlock() - h.hist.RecordValue(v) + h.rw.Unlock() } diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/metrics.go b/vendor/github.com/circonus-labs/circonus-gometrics/metrics.go new file mode 100644 index 000000000..85812f195 --- /dev/null +++ b/vendor/github.com/circonus-labs/circonus-gometrics/metrics.go @@ -0,0 +1,15 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package circonusgometrics + +// SetMetricTags sets the tags for the named metric and flags a check update is needed +func (m *CirconusMetrics) SetMetricTags(name string, tags []string) bool { + return m.check.AddMetricTags(name, tags, false) +} + +// AddMetricTags appends tags to any existing tags for the named metric and flags a check update is needed +func (m *CirconusMetrics) AddMetricTags(name string, tags []string) bool { + return m.check.AddMetricTags(name, tags, true) +} diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/submit.go b/vendor/github.com/circonus-labs/circonus-gometrics/submit.go index fa2f9eab0..a8692c26c 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/submit.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/submit.go @@ -1,9 +1,14 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics import ( "bytes" "encoding/json" "errors" + "fmt" "io/ioutil" "log" "net" @@ -16,9 +21,9 @@ import ( ) func (m *CirconusMetrics) submit(output map[string]interface{}, newMetrics map[string]*api.CheckBundleMetric) { - if len(newMetrics) > 0 { - m.check.AddNewMetrics(newMetrics) - } + + // update check if there are any new metrics or, if metric tags have been added since last submit + m.check.UpdateCheck(newMetrics) str, err := json.Marshal(output) if err != nil { @@ -49,8 +54,32 @@ func (m *CirconusMetrics) trapCall(payload []byte) (int, error) { if err != nil { return 0, err } + req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") + // keep last HTTP error in the event of retry failure + var lastHTTPError error + retryPolicy := func(resp *http.Response, err error) (bool, error) { + if err != nil { + lastHTTPError = err + return true, err + } + // Check the response code. We retry on 500-range responses to allow + // the server time to recover, as 500's are typically not permanent + // errors and may relate to outages on the server side. This will catch + // invalid response codes as well, like 0 and 999. + if resp.StatusCode == 0 || resp.StatusCode >= 500 { + body, readErr := ioutil.ReadAll(resp.Body) + if readErr != nil { + lastHTTPError = fmt.Errorf("- last HTTP error: %d %+v", resp.StatusCode, readErr) + } else { + lastHTTPError = fmt.Errorf("- last HTTP error: %d %s", resp.StatusCode, string(body)) + } + return true, nil + } + return false, nil + } + client := retryablehttp.NewClient() if trap.URL.Scheme == "https" { client.HTTPClient.Transport = &http.Transport{ @@ -78,10 +107,17 @@ func (m *CirconusMetrics) trapCall(payload []byte) (int, error) { DisableCompression: true, } } - client.RetryWaitMin = 10 * time.Millisecond - client.RetryWaitMax = 50 * time.Millisecond + client.RetryWaitMin = 1 * time.Second + client.RetryWaitMax = 5 * time.Second client.RetryMax = 3 - client.Logger = m.Log + // retryablehttp only groks log or no log + // but, outputs everything as [DEBUG] messages + if m.Debug { + client.Logger = m.Log + } else { + client.Logger = log.New(ioutil.Discard, "", log.LstdFlags) + } + client.CheckRetry = retryPolicy attempts := -1 client.RequestLogHook = func(logger *log.Logger, req *http.Request, retryNumber int) { @@ -90,6 +126,9 @@ func (m *CirconusMetrics) trapCall(payload []byte) (int, error) { resp, err := client.Do(req) if err != nil { + if lastHTTPError != nil { + return 0, fmt.Errorf("[ERROR] submitting: %+v %+v", err, lastHTTPError) + } if attempts == client.RetryMax { m.check.RefreshTrap() } @@ -103,9 +142,8 @@ func (m *CirconusMetrics) trapCall(payload []byte) (int, error) { } var response map[string]interface{} - err = json.Unmarshal(body, &response) - if err != nil { - m.Log.Printf("[ERROR] parsing body, proceeding. %s\n", err) + if err := json.Unmarshal(body, &response); err != nil { + m.Log.Printf("[ERROR] parsing body, proceeding. %v (%s)\n", err, body) } if resp.StatusCode != 200 { diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/text.go b/vendor/github.com/circonus-labs/circonus-gometrics/text.go index f9064dc6e..eb2d12a87 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/text.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/text.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics // A Text metric is an arbitrary string diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/tools.go b/vendor/github.com/circonus-labs/circonus-gometrics/tools.go index c00820c6c..73259a7b1 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/tools.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/tools.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics import ( diff --git a/vendor/github.com/circonus-labs/circonus-gometrics/util.go b/vendor/github.com/circonus-labs/circonus-gometrics/util.go index 8b84b3270..b5e9f4777 100644 --- a/vendor/github.com/circonus-labs/circonus-gometrics/util.go +++ b/vendor/github.com/circonus-labs/circonus-gometrics/util.go @@ -1,3 +1,7 @@ +// Copyright 2016 Circonus, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package circonusgometrics import ( @@ -29,7 +33,7 @@ func (m *CirconusMetrics) Reset() { m.counters = make(map[string]uint64) m.counterFuncs = make(map[string]func() uint64) - m.gauges = make(map[string]int64) + m.gauges = make(map[string]string) m.gaugeFuncs = make(map[string]func() int64) m.histograms = make(map[string]*Histogram) m.text = make(map[string]string) @@ -37,7 +41,7 @@ func (m *CirconusMetrics) Reset() { } // snapshot returns a copy of the values of all registered counters and gauges. -func (m *CirconusMetrics) snapshot() (c map[string]uint64, g map[string]int64, h map[string]*circonusllhist.Histogram, t map[string]string) { +func (m *CirconusMetrics) snapshot() (c map[string]uint64, g map[string]string, h map[string]*circonusllhist.Histogram, t map[string]string) { m.cm.Lock() defer m.cm.Unlock() @@ -68,18 +72,21 @@ func (m *CirconusMetrics) snapshot() (c map[string]uint64, g map[string]int64, h c[n] = f() } - g = make(map[string]int64, len(m.gauges)+len(m.gaugeFuncs)) + //g = make(map[string]int64, len(m.gauges)+len(m.gaugeFuncs)) + g = make(map[string]string, len(m.gauges)+len(m.gaugeFuncs)) for n, v := range m.gauges { g[n] = v } for n, f := range m.gaugeFuncs { - g[n] = f() + g[n] = m.gaugeValString(f()) } h = make(map[string]*circonusllhist.Histogram, len(m.histograms)) for n, hist := range m.histograms { + hist.rw.Lock() h[n] = hist.hist.CopyAndReset() + hist.rw.Unlock() } t = make(map[string]string, len(m.text)+len(m.textFuncs)) @@ -91,5 +98,24 @@ func (m *CirconusMetrics) snapshot() (c map[string]uint64, g map[string]int64, h t[n] = f() } + if m.resetCounters { + m.counters = make(map[string]uint64) + m.counterFuncs = make(map[string]func() uint64) + } + + if m.resetGauges { + m.gauges = make(map[string]string) + m.gaugeFuncs = make(map[string]func() int64) + } + + if m.resetHistograms { + m.histograms = make(map[string]*Histogram) + } + + if m.resetText { + m.text = make(map[string]string) + m.textFuncs = make(map[string]func() string) + } + return } diff --git a/vendor/vendor.json b/vendor/vendor.json index e1e958378..a79b1592f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -200,22 +200,22 @@ "revisionTime": "2016-07-17T15:07:09Z" }, { - "checksumSHA1": "qNHg4l2+bg50XQUR094857jPOoQ=", + "checksumSHA1": "szvY4u7TlXkrQ3PW8wmyJaIFy0U=", "path": "github.com/circonus-labs/circonus-gometrics", - "revision": "4d03e5666abdf78da571fcb930201f06b0c46662", - "revisionTime": "2016-07-21T19:08:58Z" + "revision": "f8e2e2ff37c349aed537b70c9430ded0ea473248", + "revisionTime": "2016-11-01T19:28:51Z" }, { - "checksumSHA1": "IFiYTxu8jshL4A8BCttUaDhp1m4=", + "checksumSHA1": "dCnZd/wjQjTnUBTBEDpe0Y2pvTA=", "path": "github.com/circonus-labs/circonus-gometrics/api", - "revision": "4d03e5666abdf78da571fcb930201f06b0c46662", - "revisionTime": "2016-07-21T19:08:58Z" + "revision": "f8e2e2ff37c349aed537b70c9430ded0ea473248", + "revisionTime": "2016-11-01T19:28:51Z" }, { - "checksumSHA1": "+9vcRzlTdvEjH/Uf8fKC5MXdjNw=", + "checksumSHA1": "B0hN7c1dJ8n3MoZLktuWJoaYxRs=", "path": "github.com/circonus-labs/circonus-gometrics/checkmgr", - "revision": "4d03e5666abdf78da571fcb930201f06b0c46662", - "revisionTime": "2016-07-21T19:08:58Z" + "revision": "f8e2e2ff37c349aed537b70c9430ded0ea473248", + "revisionTime": "2016-11-01T19:28:51Z" }, { "checksumSHA1": "C4Z7+l5GOpOCW5DcvNYzheGvQRE=",