diff --git a/command/agent/command.go b/command/agent/command.go index 453239677..c4e6e5118 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -588,12 +588,13 @@ func (c *Command) Run(args []string) int { */ inm := metrics.NewInmemSink(10*time.Second, time.Minute) metrics.DefaultInmemSignal(inm) - metricsConf := metrics.DefaultConfig(config.StatsitePrefix) + metricsConf := metrics.DefaultConfig(config.Telemetry.StatsitePrefix) + metricsConf.EnableHostname = !config.Telemetry.DisableHostname // Configure the statsite sink var fanout metrics.FanoutSink - if config.StatsiteAddr != "" { - sink, err := metrics.NewStatsiteSink(config.StatsiteAddr) + if config.Telemetry.StatsiteAddr != "" { + sink, err := metrics.NewStatsiteSink(config.Telemetry.StatsiteAddr) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to start statsite sink. Got: %s", err)) return 1 @@ -602,8 +603,8 @@ func (c *Command) Run(args []string) int { } // Configure the statsd sink - if config.StatsdAddr != "" { - sink, err := metrics.NewStatsdSink(config.StatsdAddr) + if config.Telemetry.StatsdAddr != "" { + sink, err := metrics.NewStatsdSink(config.Telemetry.StatsdAddr) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to start statsd sink. Got: %s", err)) return 1 @@ -612,14 +613,14 @@ func (c *Command) Run(args []string) int { } // Configure the DogStatsd sink - if config.DogStatsdAddr != "" { + if config.Telemetry.DogStatsdAddr != "" { var tags []string - if config.DogStatsdTags != nil { - tags = config.DogStatsdTags + if config.Telemetry.DogStatsdTags != nil { + tags = config.Telemetry.DogStatsdTags } - sink, err := datadog.NewDogStatsdSink(config.DogStatsdAddr, metricsConf.HostName) + sink, err := datadog.NewDogStatsdSink(config.Telemetry.DogStatsdAddr, metricsConf.HostName) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to start DogStatsd sink. Got: %s", err)) return 1 diff --git a/command/agent/config.go b/command/agent/config.go index c9e3464d1..515443954 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -91,6 +91,32 @@ type DNSConfig struct { OnlyPassing bool `mapstructure:"only_passing"` } +// Telemetry is the telemetry configuration for the server +type Telemetry struct { + // StatsiteAddr is the address of a statsite instance. If provided, + // metrics will be streamed to that instance. + StatsiteAddr string `mapstructure:"statsite_address"` + + // StatsdAddr is the address of a statsd instance. If provided, + // metrics will be sent to that instance. + StatsdAddr string `mapstructure:"statsd_address"` + + // StatsitePrefix is the prefix used to write stats values to. By + // default this is set to 'consul'. + StatsitePrefix string `mapstructure:"statsite_prefix"` + + // DisableHostname will disable hostname prefixing for all metrics + DisableHostname bool `mapstructure:"disable_hostname"` + + // DogStatsdAddr is the address of a dogstatsd instance. If provided, + // metrics will be sent to that instance + DogStatsdAddr string `mapstructure:"dogstatsd_addr"` + + // 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"` +} + // Config is the configuration that can be set for an Agent. // Some of this is configurable as CLI flags, but most must // be set using a configuration file. @@ -177,25 +203,7 @@ type Config struct { // the INT signal. Defaults false. This can be changed on reload. SkipLeaveOnInt bool `mapstructure:"skip_leave_on_interrupt"` - // StatsiteAddr is the address of a statsite instance. If provided, - // metrics will be streamed to that instance. - StatsiteAddr string `mapstructure:"statsite_addr"` - - // StatsitePrefix is the prefix used to write stats values to. By - // default this is set to 'consul'. - StatsitePrefix string `mapstructure:"statsite_prefix"` - - // StatsdAddr is the address of a statsd instance. If provided, - // metrics will be sent to that instance. - StatsdAddr string `mapstructure:"statsd_addr"` - - // DogStatsdAddr is the address of a dogstatsd instance. If provided, - // metrics will be sent to that instance - DogStatsdAddr string `mapstructure:"dogstatsd_addr"` - - // 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"` + Telemetry Telemetry `mapstructure:"telemetry"` // Protocol is the Consul protocol version to use. Protocol int `mapstructure:"protocol"` @@ -464,6 +472,10 @@ func (u UnixSocketPermissions) Mode() string { return u.Perms } +func (s *Telemetry) GoString() string { + return fmt.Sprintf("*%#v", *s) +} + // UnixSocketConfig stores information about various unix sockets which // Consul creates and uses for communication. type UnixSocketConfig struct { @@ -504,7 +516,9 @@ func DefaultConfig() *Config { DNSConfig: DNSConfig{ MaxStale: 5 * time.Second, }, - StatsitePrefix: "consul", + Telemetry: Telemetry{ + StatsitePrefix: "consul", + }, SyslogFacility: "LOCAL0", Protocol: consul.ProtocolVersion2Compatible, CheckUpdateInterval: 5 * time.Minute, @@ -613,6 +627,30 @@ func DecodeConfig(r io.Reader) (*Config, error) { } result.Checks = append(result.Checks, check) } + + // A little hacky but upgrades the old stats config directives to the new way + if sub, ok := obj["statsd_addr"]; ok && result.Telemetry.StatsdAddr == "" { + result.Telemetry.StatsdAddr = sub.(string) + } + + if sub, ok := obj["statsite_addr"]; ok && result.Telemetry.StatsiteAddr == "" { + result.Telemetry.StatsiteAddr = sub.(string) + } + + if sub, ok := obj["statsite_prefix"]; ok && result.Telemetry.StatsitePrefix == "" { + result.Telemetry.StatsitePrefix = sub.(string) + } + + if sub, ok := obj["dogstatsd_addr"]; ok && result.Telemetry.DogStatsdAddr == "" { + result.Telemetry.DogStatsdAddr = sub.(string) + } + + if sub, ok := obj["dogstatsd_tags"].([]interface{}); ok && len(result.Telemetry.DogStatsdTags) == 0 { + result.Telemetry.DogStatsdTags = make([]string, len(sub)) + for i := range sub { + result.Telemetry.DogStatsdTags[i] = sub[i].(string) + } + } } // Decode @@ -632,7 +670,11 @@ func DecodeConfig(r io.Reader) (*Config, error) { // Check unused fields and verify that no bad configuration options were // passed to Consul. There are a few additional fields which don't directly // use mapstructure decoding, so we need to account for those as well. - allowedKeys := []string{"service", "services", "check", "checks"} + allowedKeys := []string{ + "service", "services", "check", "checks", "statsd_addr", "statsite_addr", "statsite_prefix", + "dogstatsd_addr", "dogstatsd_tags", + } + var unused []string for _, field := range md.Unused { if !lib.StrContains(allowedKeys, field) { @@ -947,20 +989,23 @@ func MergeConfig(a, b *Config) *Config { if b.SkipLeaveOnInt == true { result.SkipLeaveOnInt = true } - if b.StatsiteAddr != "" { - result.StatsiteAddr = b.StatsiteAddr + if b.Telemetry.DisableHostname == true { + result.Telemetry.DisableHostname = true } - if b.StatsitePrefix != "" { - result.StatsitePrefix = b.StatsitePrefix + if b.Telemetry.StatsdAddr != "" { + result.Telemetry.StatsdAddr = b.Telemetry.StatsdAddr } - if b.StatsdAddr != "" { - result.StatsdAddr = b.StatsdAddr + if b.Telemetry.StatsiteAddr != "" { + result.Telemetry.StatsiteAddr = b.Telemetry.StatsiteAddr } - if b.DogStatsdAddr != "" { - result.DogStatsdAddr = b.DogStatsdAddr + if b.Telemetry.StatsitePrefix != "" { + result.Telemetry.StatsitePrefix = b.Telemetry.StatsitePrefix } - if b.DogStatsdTags != nil { - result.DogStatsdTags = b.DogStatsdTags + if b.Telemetry.DogStatsdAddr != "" { + result.Telemetry.DogStatsdAddr = b.Telemetry.DogStatsdAddr + } + if b.Telemetry.DogStatsdTags != nil { + result.Telemetry.DogStatsdTags = b.Telemetry.DogStatsdTags } if b.EnableDebug { result.EnableDebug = true diff --git a/command/agent/config_test.go b/command/agent/config_test.go index 44da26901..d06feb8cb 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -635,10 +635,10 @@ func TestDecodeConfig(t *testing.T) { t.Fatalf("err: %s", err) } - if config.StatsiteAddr != "127.0.0.1:7250" { + if config.Telemetry.StatsiteAddr != "127.0.0.1:7250" { t.Fatalf("bad: %#v", config) } - if config.StatsdAddr != "127.0.0.1:7251" { + if config.Telemetry.StatsdAddr != "127.0.0.1:7251" { t.Fatalf("bad: %#v", config) } @@ -648,19 +648,19 @@ func TestDecodeConfig(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - if config.DogStatsdAddr != "127.0.0.1:7254" { + if config.Telemetry.DogStatsdAddr != "127.0.0.1:7254" { t.Fatalf("bad: %#v", config) } - if len(config.DogStatsdTags) != 2 { + if len(config.Telemetry.DogStatsdTags) != 2 { t.Fatalf("bad: %#v", config) } - if config.DogStatsdTags[0] != "tag_1:val_1" { + if config.Telemetry.DogStatsdTags[0] != "tag_1:val_1" { t.Fatalf("bad: %#v", config) } - if config.DogStatsdTags[1] != "tag_2:val_2" { + if config.Telemetry.DogStatsdTags[1] != "tag_2:val_2" { t.Fatalf("bad: %#v", config) } @@ -670,7 +670,32 @@ func TestDecodeConfig(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - if config.StatsitePrefix != "my_prefix" { + if config.Telemetry.StatsitePrefix != "my_prefix" { + 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))) + if err != nil { + t.Fatalf("err: %s", err) + } + if config.Telemetry.StatsitePrefix != "my_prefix" { + t.Fatalf("bad: %#v", config) + } + if config.Telemetry.StatsiteAddr != "127.0.0.1:7250" { + t.Fatalf("bad: %#v", config) + } + if config.Telemetry.StatsdAddr != "127.0.0.1:7251" { + t.Fatalf("bad: %#v", config) + } + if config.Telemetry.DisableHostname != true { + t.Fatalf("bad: %#v", config) + } + if config.Telemetry.DogStatsdAddr != "1.1.1.1:111" { + t.Fatalf("bad: %#v", config) + } + if config.Telemetry.DogStatsdTags[0] != "tag_1:val_1" { t.Fatalf("bad: %#v", config) } @@ -1191,6 +1216,14 @@ func TestMergeConfig(t *testing.T) { CheckUpdateIntervalRaw: "8m", RetryIntervalRaw: "10s", RetryIntervalWanRaw: "10s", + Telemetry: Telemetry{ + DisableHostname: false, + StatsdAddr: "nope", + StatsiteAddr: "nope", + StatsitePrefix: "nope", + DogStatsdAddr: "nope", + DogStatsdTags: []string{"nope"}, + }, } b := &Config{ @@ -1269,12 +1302,15 @@ func TestMergeConfig(t *testing.T) { "handler": "foobar", }, }, - DisableRemoteExec: true, - StatsiteAddr: "127.0.0.1:7250", - StatsitePrefix: "stats_prefix", - StatsdAddr: "127.0.0.1:7251", - DogStatsdAddr: "127.0.0.1:7254", - DogStatsdTags: []string{"tag_1:val_1", "tag_2:val_2"}, + DisableRemoteExec: true, + Telemetry: Telemetry{ + StatsiteAddr: "127.0.0.1:7250", + StatsitePrefix: "stats_prefix", + StatsdAddr: "127.0.0.1:7251", + DisableHostname: true, + DogStatsdAddr: "127.0.0.1:7254", + DogStatsdTags: []string{"tag_1:val_1", "tag_2:val_2"}, + }, DisableUpdateCheck: true, DisableAnonymousSignature: true, HTTPAPIResponseHeaders: map[string]string{ diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown index 3b1eded12..f59b3bb8d 100644 --- a/website/source/docs/agent/options.html.markdown +++ b/website/source/docs/agent/options.html.markdown @@ -273,7 +273,10 @@ definitions support being updated during a reload. "type": "checks", "handler": "/usr/bin/health-check-handler.sh" } - ] + ], + "telemetry": { + "statsite_address": "127.0.0.1:2180" + } } ``` @@ -572,29 +575,48 @@ definitions support being updated during a reload. * `start_join_wan` An array of strings specifying addresses of WAN nodes to [`-join-wan`](#_join_wan) upon startup. -* `statsd_addr` This provides the address of a - statsd instance in the format `host:port`. If provided, Consul will send various telemetry information - to that instance for aggregation. This can be used to capture runtime information. This sends UDP packets - only and can be used with statsd or statsite. +* `telemetry` An object describing where to send telemetry to -* `dogstatsd_addr` This provides the - address of a DogStatsD instance in the format `host:port`. DogStatsD is a protocol-compatible flavor of - statsd, with the added ability to decorate metrics with tags and event information. If provided, Consul will - send various telemetry information to that instance for aggregation. This can be used to capture runtime - information. + * `statsd_address` This provides the + address of a statsd instance. If provided, Consul will send various telemetry information to that instance for + aggregation. This can be used to capture runtime information. This sends UDP packets only and can be used with + statsd or statsite. + + * `statsite_address` This provides + the address of a statsite instance. If provided, Consul will stream various telemetry information to that instance + for aggregation. This can be used to capture runtime information. This streams via TCP and can only be used with + statsite. + + * `statsite_prefix` + The prefix used while writing all telemetry data to statsite. By default, this is set to "consul". + + * `dogstatsd_addr` This provides the + address of a DogStatsD instance in the format `host:port`. DogStatsD is a protocol-compatible flavor of + statsd, with the added ability to decorate metrics with tags and event information. If provided, Consul will + send various telemetry information to that instance for aggregation. This can be used to capture runtime + information. + + * `dogstatsd_tags` This provides a list of global tags + that will be added to all telemetry packets sent to DogStatsD. It is a list of strings, where each string + looks like "my_tag_name:my_tag_value". + + * `disable_hostname` + Whether or not to prepend runtime telemetry with the machines hostname, defaults to false. + +* `statsd_addr` Deprecated, see + `statsd_address` -* `dogstatsd_tags` This provides a list of global tags - that will be added to all telemetry packets sent to DogStatsD. It is a list of strings, where each string - looks like "my_tag_name:my_tag_value". +* `statsite_addr` Deprecated, see + `statsite_address` -* `statsite_addr` This provides the address of a - statsite instance in the format `host:port`. If provided, Consul will stream various telemetry information - to that instance for aggregation. This can be used to capture runtime information. This streams via TCP and - can only be used with statsite. +* `statsite_prefix` Deprecated, see + `statsite_prefix` -* `statsite_prefix` - The prefix used while writing all telemetry data to statsite. By default, this - is set to "consul". +* `dogstatsd_addr` Deprecated, see + `dogstatsd_addr` + +* `dogstatsd_tags` Deprecated, see + `dogstatsd_tags` * `syslog_facility` When [`enable_syslog`](#enable_syslog) is provided, this controls to which diff --git a/website/source/docs/agent/telemetry.html.markdown b/website/source/docs/agent/telemetry.html.markdown index 392edd59d..a71d5deb9 100644 --- a/website/source/docs/agent/telemetry.html.markdown +++ b/website/source/docs/agent/telemetry.html.markdown @@ -19,10 +19,10 @@ it will dump the current telemetry information to the agent's `stderr`. This telemetry information can be used for debugging or otherwise getting a better view of what Consul is doing. -Additionally, if the [`statsite_addr` configuration option](/docs/agent/options.html#statsite_addr) -is provided, the telemetry information will be streamed to a -[statsite](http://github.com/armon/statsite) server where it can be -aggregated and flushed to Graphite or any other metrics store. +Additionally, if the [`telemetry` configuration options](/docs/agent/options.html#telemetry) +are provided, the telemetry information will be streamed to a +[statsite](http://github.com/armon/statsite) or [statsd](http://github.com/etsy/statsd) server where +it can be aggregated and flushed to Graphite or any other metrics store. Below is sample output of a telemetry dump: