Add Circonus support for Telemetry metrics

This commit is contained in:
matt maier 2016-07-18 09:34:43 -04:00
parent 548fb6eb3f
commit fb76256c26
3 changed files with 219 additions and 1 deletions

View File

@ -15,6 +15,7 @@ import (
"time"
"github.com/armon/go-metrics"
"github.com/armon/go-metrics/circonus"
"github.com/armon/go-metrics/datadog"
"github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/watch"
@ -716,6 +717,37 @@ func (c *Command) Run(args []string) int {
fanout = append(fanout, sink)
}
if config.Telemetry.CirconusAPIToken != "" || config.Telemetry.CirconusCheckSubmissionURL != "" {
cfg := &circonus.Config{}
cfg.Interval = config.Telemetry.CirconusSubmitInterval
cfg.CheckManager.API.TokenKey = config.Telemetry.CirconusAPIToken
cfg.CheckManager.API.TokenApp = config.Telemetry.CirconusAPIApp
cfg.CheckManager.API.URL = config.Telemetry.CirconusAPIURL
cfg.CheckManager.Check.SubmissionURL = config.Telemetry.CirconusCheckSubmissionURL
cfg.CheckManager.Check.ID = config.Telemetry.CirconusCheckID
cfg.CheckManager.Check.ForceMetricActivation = config.Telemetry.CirconusCheckForceMetricActivation
cfg.CheckManager.Check.InstanceID = config.Telemetry.CirconusCheckInstanceID
cfg.CheckManager.Check.SearchTag = config.Telemetry.CirconusCheckSearchTag
cfg.CheckManager.Broker.ID = config.Telemetry.CirconusBrokerID
cfg.CheckManager.Broker.SelectTag = config.Telemetry.CirconusBrokerSelectTag
if cfg.CheckManager.Check.InstanceID == "" {
cfg.CheckManager.Check.InstanceID = fmt.Sprintf("%s:%s", config.NodeName, config.Datacenter)
}
if cfg.CheckManager.Check.SearchTag == "" {
cfg.CheckManager.Check.SearchTag = "service:consul"
}
sink, err := circonus.NewCirconusSink(cfg)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to start Circonus sink. Got: %s", err))
return 1
}
sink.Start()
fanout = append(fanout, sink)
}
// Initialize the global sink
if len(fanout) > 0 {
fanout = append(fanout, inm)

View File

@ -130,6 +130,70 @@ type Telemetry struct {
// DogStatsdTags are the global tags that should be sent with each packet to dogstatsd
// It is a list of strings, where each string looks like "my_tag_name:my_tag_value"
DogStatsdTags []string `mapstructure:"dogstatsd_tags"`
// Circonus: see https://github.com/circonus-labs/circonus-gometrics
// for more details on the various configuration options.
// Valid configuration combinations:
// - CirconusAPIToken
// metric management enabled (search for existing check or create a new one)
// - CirconusSubmissionUrl
// metric management disabled (use check with specified submission_url,
// broker must be using a public SSL certificate)
// - CirconusAPIToken + CirconusCheckSubmissionURL
// metric management enabled (use check with specified submission_url)
// - CirconusAPIToken + CirconusCheckID
// metric management enabled (use check with specified id)
// CirconusAPIToken is a valid API Token used to create/manage check. If provided,
// metric management is enabled.
// Default: none
CirconusAPIToken string `mapstructure:"circonus_api_token"`
// CirconusAPIApp is an app name associated with API token.
// Default: "circonus-gometrics"
CirconusAPIApp string `mapstructure:"circonus_api_app"`
// CirconusAPIURL is the base URL to use for contacting the Circonus API.
// Default: "https://api.circonus.com/v2"
CirconusAPIURL string `mapstructure:"circonus_api_url"`
// CirconusSubmitInterval is the interval at which metrics are submitted to Circonus.
// Default: 10s
CirconusSubmitInterval string `mapstructure:"circonus_submission_interval"`
// CirconusCheckSubmissionURL is the check.config.submission_url field from a
// previously created HTTPTRAP check.
// Default: none
CirconusCheckSubmissionURL string `mapstructure:"circonus_submission_url"`
// CirconusCheckID is the check id (not check bundle id) from a previously created
// HTTPTRAP check. The numeric portion of the check._cid field.
// Default: none
CirconusCheckID string `mapstructure:"circonus_check_id"`
// CirconusCheckForceMetricActivation will force enabling metrics, as they are encountered,
// if the metric already exists and is NOT active. If check management is enabled, the default
// behavior is to add new metrics as they are encoutered. If the metric already exists in the
// check, it will *NOT* be activated. This setting overrides that behavior.
// Default: "false"
CirconusCheckForceMetricActivation string `mapstructure:"circonus_check_force_metric_activation"`
// CirconusCheckInstanceID serves to uniquely identify the metrics comming from this "instance".
// It can be used to maintain metric continuity with transient or ephemeral instances as
// they move around within an infrastructure.
// Default: hostname:app
CirconusCheckInstanceID string `mapstructure:"circonus_check_instance_id"`
// CirconusCheckSearchTag is a special tag which, when coupeled with the instance id, helps to
// narrow down the search results when neither a Submission URL or Check ID is provided.
// Default: service:app (e.g. service:consul)
CirconusCheckSearchTag string `mapstructure:"circonus_check_search_tag"`
// CirconusBrokerID is an explicit broker to use when creating a new check. The numeric portion
// of broker._cid. If metric management is enabled and neither a Submission URL nor Check ID
// is provided, an attempt will be made to search for an existing check using Instance ID and
// Search Tag. If one is not found, a new HTTPTRAP check will be created.
// Default: use Select Tag if provided, otherwise, a random Enterprise Broker associated
// with the specified API token or the default Circonus Broker.
// Default: none
CirconusBrokerID string `mapstructure:"circonus_broker_id"`
// CirconusBrokerSelectTag is a special tag which will be used to select a broker when
// a Broker ID is not provided. The best use of this is to as a hint for which broker
// should be used based on *where* this particular instance is running.
// (e.g. a specific geo location or datacenter, dc:sfo)
// Default: none
CirconusBrokerSelectTag string `mapstructure:"circonus_broker_search_tag"`
}
// Config is the configuration that can be set for an Agent.
@ -690,6 +754,47 @@ func DecodeConfig(r io.Reader) (*Config, error) {
result.Telemetry.DogStatsdTags[i] = sub[i].(string)
}
}
if sub, ok := obj["circonus_api_token"]; ok && result.Telemetry.CirconusAPIToken == "" {
result.Telemetry.CirconusAPIToken = sub.(string)
}
if sub, ok := obj["circonus_api_app"]; ok && result.Telemetry.CirconusAPIApp == "" {
result.Telemetry.CirconusAPIApp = sub.(string)
}
if sub, ok := obj["circonus_api_url"]; ok && result.Telemetry.CirconusAPIURL == "" {
result.Telemetry.CirconusAPIURL = sub.(string)
}
if sub, ok := obj["circonus_submission_url"]; ok && result.Telemetry.CirconusCheckSubmissionURL == "" {
result.Telemetry.CirconusCheckSubmissionURL = sub.(string)
}
if sub, ok := obj["circonus_submit_interval"]; ok && result.Telemetry.CirconusSubmitInterval == "" {
result.Telemetry.CirconusSubmitInterval = sub.(string)
}
if sub, ok := obj["circonus_check_id"]; ok && result.Telemetry.CirconusCheckID == "" {
result.Telemetry.CirconusCheckID = sub.(string)
}
if sub, ok := obj["circonus_check_force_metric_activation"]; ok && result.Telemetry.CirconusCheckForceMetricActivation == "" {
result.Telemetry.CirconusCheckForceMetricActivation = sub.(string)
}
if sub, ok := obj["circonus_check_instance_id"]; ok && result.Telemetry.CirconusCheckInstanceID == "" {
result.Telemetry.CirconusCheckInstanceID = sub.(string)
}
if sub, ok := obj["circonus_check_search_tag"]; ok && result.Telemetry.CirconusCheckSearchTag == "" {
result.Telemetry.CirconusCheckSearchTag = sub.(string)
}
if sub, ok := obj["circonus_broker_id"]; ok && result.Telemetry.CirconusBrokerID == "" {
result.Telemetry.CirconusBrokerID = sub.(string)
}
if sub, ok := obj["circonus_broker_select_tag"]; ok && result.Telemetry.CirconusBrokerSelectTag == "" {
result.Telemetry.CirconusBrokerSelectTag = sub.(string)
}
}
// Decode
@ -711,7 +816,10 @@ func DecodeConfig(r io.Reader) (*Config, error) {
// use mapstructure decoding, so we need to account for those as well.
allowedKeys := []string{
"service", "services", "check", "checks", "statsd_addr", "statsite_addr", "statsite_prefix",
"dogstatsd_addr", "dogstatsd_tags",
"dogstatsd_addr", "dogstatsd_tags", "circonus_api_token", "circonus_api_app",
"circonus_api_url", "circonus_submission_url", "circonus_submit_interval",
"circonus_check_id", "circonus_check_force_metric_activation", "circonus_check_instance_id",
"circonus_check_search_tag", "circonus_broker_id", "circonus_broker_select_tag",
}
var unused []string
@ -1071,6 +1179,39 @@ func MergeConfig(a, b *Config) *Config {
if b.Telemetry.DogStatsdTags != nil {
result.Telemetry.DogStatsdTags = b.Telemetry.DogStatsdTags
}
if b.Telemetry.CirconusAPIToken != "" {
result.Telemetry.CirconusAPIToken = b.Telemetry.CirconusAPIToken
}
if b.Telemetry.CirconusAPIApp != "" {
result.Telemetry.CirconusAPIApp = b.Telemetry.CirconusAPIApp
}
if b.Telemetry.CirconusAPIURL != "" {
result.Telemetry.CirconusAPIURL = b.Telemetry.CirconusAPIURL
}
if b.Telemetry.CirconusCheckSubmissionURL != "" {
result.Telemetry.CirconusCheckSubmissionURL = b.Telemetry.CirconusCheckSubmissionURL
}
if b.Telemetry.CirconusSubmitInterval != "" {
result.Telemetry.CirconusSubmitInterval = b.Telemetry.CirconusSubmitInterval
}
if b.Telemetry.CirconusCheckID != "" {
result.Telemetry.CirconusCheckID = b.Telemetry.CirconusCheckID
}
if b.Telemetry.CirconusCheckForceMetricActivation != "" {
result.Telemetry.CirconusCheckForceMetricActivation = b.Telemetry.CirconusCheckForceMetricActivation
}
if b.Telemetry.CirconusCheckInstanceID != "" {
result.Telemetry.CirconusCheckInstanceID = b.Telemetry.CirconusCheckInstanceID
}
if b.Telemetry.CirconusCheckSearchTag != "" {
result.Telemetry.CirconusCheckSearchTag = b.Telemetry.CirconusCheckSearchTag
}
if b.Telemetry.CirconusBrokerID != "" {
result.Telemetry.CirconusBrokerID = b.Telemetry.CirconusBrokerID
}
if b.Telemetry.CirconusBrokerSelectTag != "" {
result.Telemetry.CirconusBrokerSelectTag = b.Telemetry.CirconusBrokerSelectTag
}
if b.EnableDebug {
result.EnableDebug = true
}

View File

@ -725,6 +725,51 @@ func TestDecodeConfig(t *testing.T) {
t.Fatalf("bad: %#v", config)
}
// Circonus settings
input = `{"circonus_api_token": "12345678-1234-1234-12345678", "circonus_api_app": "testApp",
"circonus_api_url": "https://api.host.foo/v2", "circonus_submit_interval": "15s",
"circonus_submission_url": "https://submit.host.bar:123/one/two/three",
"circonus_check_id": "12345", "circonus_check_force_metric_activation": "true",
"circonus_check_instance_id": "a:b", "circonus_check_search_tag": "c:d",
"circonus_broker_id": "6789", "circonus_broker_select_tag": "e:f"}`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil {
t.Fatalf("err: %s", err)
}
if config.Telemetry.CirconusAPIToken != "12345678-1234-1234-12345678" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusAPIApp != "testApp" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusAPIURL != "https://api.host.foo/v2" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusSubmitInterval != "15s" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusCheckSubmissionURL != "https://submit.host.bar:123/one/two/three" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusCheckID != "12345" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusCheckForceMetricActivation != "true" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusCheckInstanceID != "a:b" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusCheckSearchTag != "c:d" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusBrokerID != "6789" {
t.Fatalf("bad: %#v", config)
}
if config.Telemetry.CirconusBrokerSelectTag != "e:f" {
t.Fatalf("bad: %#v", config)
}
// New telemetry
input = `{"telemetry": { "statsite_prefix": "my_prefix", "statsite_address": "127.0.0.1:7250", "statsd_address":"127.0.0.1:7251", "disable_hostname": true, "dogstatsd_addr": "1.1.1.1:111", "dogstatsd_tags": [ "tag_1:val_1" ] } }`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))