fix(inputs.cloudwatch): Option to produce dense metrics (#15317)
This commit is contained in:
parent
c3c6189a1b
commit
1a00a48d54
|
|
@ -102,6 +102,13 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
## Metric Statistic Namespaces (required)
|
## Metric Statistic Namespaces (required)
|
||||||
namespaces = ["AWS/ELB"]
|
namespaces = ["AWS/ELB"]
|
||||||
|
|
||||||
|
## Metric Format
|
||||||
|
## This determines the format of the produces metrics. 'sparse', the default
|
||||||
|
## will produce a unique field for each statistic. 'dense' will report all
|
||||||
|
## statistics will be in a field called value and have a metric_name tag
|
||||||
|
## defining the name of the statistic. See the plugin README for examples.
|
||||||
|
# metric_format = "sparse"
|
||||||
|
|
||||||
## Maximum requests per second. Note that the global default AWS rate limit
|
## Maximum requests per second. Note that the global default AWS rate limit
|
||||||
## is 50 reqs/sec, so if you define multiple namespaces, these should add up
|
## is 50 reqs/sec, so if you define multiple namespaces, these should add up
|
||||||
## to a maximum of 50.
|
## to a maximum of 50.
|
||||||
|
|
@ -215,13 +222,51 @@ Each CloudWatch Namespace monitored records a measurement with fields for each
|
||||||
available Metric Statistic. Namespace and Metrics are represented in [snake
|
available Metric Statistic. Namespace and Metrics are represented in [snake
|
||||||
case](https://en.wikipedia.org/wiki/Snake_case)
|
case](https://en.wikipedia.org/wiki/Snake_case)
|
||||||
|
|
||||||
|
### Sparse Metrics
|
||||||
|
|
||||||
|
By default, metrics generated by this plugin are sparse. Use the `metric_format`
|
||||||
|
option to override this setting.
|
||||||
|
|
||||||
|
Sparse metrics produce a set of fields for every AWS Metric.
|
||||||
|
|
||||||
- cloudwatch_{namespace}
|
- cloudwatch_{namespace}
|
||||||
|
- Fields
|
||||||
- {metric}_sum (metric Sum value)
|
- {metric}_sum (metric Sum value)
|
||||||
- {metric}_average (metric Average value)
|
- {metric}_average (metric Average value)
|
||||||
- {metric}_minimum (metric Minimum value)
|
- {metric}_minimum (metric Minimum value)
|
||||||
- {metric}_maximum (metric Maximum value)
|
- {metric}_maximum (metric Maximum value)
|
||||||
- {metric}_sample_count (metric SampleCount value)
|
- {metric}_sample_count (metric SampleCount value)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
cloudwatch_aws_usage,class=None,resource=GetSecretValue,service=Secrets\ Manager,type=API call_count_maximum=1,call_count_minimum=1,call_count_sum=8,call_count_sample_count=8,call_count_average=1 1715097720000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dense Metrics
|
||||||
|
|
||||||
|
Dense metrics are generated when `metric_format` is set to `dense`.
|
||||||
|
|
||||||
|
Dense metrics use the same fields over and over for every AWS Metric and
|
||||||
|
differentiate between AWS Metrics using a tag called `metric_name` with the AWS
|
||||||
|
Metric name:
|
||||||
|
|
||||||
|
- cloudwatch_{namespace}
|
||||||
|
- Tags
|
||||||
|
- metric_name (AWS Metric name)
|
||||||
|
- Fields
|
||||||
|
- sum (metric Sum value)
|
||||||
|
- average (metric Average value)
|
||||||
|
- minimum (metric Minimum value)
|
||||||
|
- maximum (metric Maximum value)
|
||||||
|
- sample_count (metric SampleCount value)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
cloudwatch_aws_usage,class=None,resource=GetSecretValue,service=Secrets\ Manager,metric_name=call_count,type=API sum=6,sample_count=6,average=1,maximum=1,minimum=1 1715097840000000000
|
||||||
|
```
|
||||||
|
|
||||||
### Tags
|
### Tags
|
||||||
|
|
||||||
Each measurement is tagged with the following identifiers to uniquely identify
|
Each measurement is tagged with the following identifiers to uniquely identify
|
||||||
|
|
@ -274,6 +319,8 @@ aws cloudwatch get-metric-data \
|
||||||
|
|
||||||
## Example Output
|
## Example Output
|
||||||
|
|
||||||
|
See the discussion above about sparse vs dense metrics for more details.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
cloudwatch_aws_elb,load_balancer_name=p-example,region=us-east-1 latency_average=0.004810798017284538,latency_maximum=0.1100282669067383,latency_minimum=0.0006084442138671875,latency_sample_count=4029,latency_sum=19.382705211639404 1459542420000000000
|
cloudwatch_aws_elb,load_balancer_name=p-example,region=us-east-1 latency_average=0.004810798017284538,latency_maximum=0.1100282669067383,latency_minimum=0.0006084442138671875,latency_sample_count=4029,latency_sum=19.382705211639404 1459542420000000000
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -48,7 +49,7 @@ type CloudWatch struct {
|
||||||
RecentlyActive string `toml:"recently_active"`
|
RecentlyActive string `toml:"recently_active"`
|
||||||
BatchSize int `toml:"batch_size"`
|
BatchSize int `toml:"batch_size"`
|
||||||
IncludeLinkedAccounts bool `toml:"include_linked_accounts"`
|
IncludeLinkedAccounts bool `toml:"include_linked_accounts"`
|
||||||
|
MetricFormat string `toml:"metric_format"`
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
client cloudwatchClient
|
client cloudwatchClient
|
||||||
|
|
@ -98,6 +99,14 @@ func (c *CloudWatch) Init() error {
|
||||||
c.Namespaces = append(c.Namespaces, c.Namespace)
|
c.Namespaces = append(c.Namespaces, c.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch c.MetricFormat {
|
||||||
|
case "":
|
||||||
|
c.MetricFormat = "sparse"
|
||||||
|
case "dense", "sparse":
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid metric_format: %s", c.MetricFormat)
|
||||||
|
}
|
||||||
|
|
||||||
err := c.initializeCloudWatch()
|
err := c.initializeCloudWatch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -462,10 +471,24 @@ func (c *CloudWatch) aggregateMetrics(
|
||||||
tags["region"] = c.Region
|
tags["region"] = c.Region
|
||||||
|
|
||||||
for i := range result.Values {
|
for i := range result.Values {
|
||||||
|
if c.MetricFormat == "dense" {
|
||||||
|
// Remove the IDs from the result ID to get the statistic type
|
||||||
|
// e.g. "average" from "average_0_0"
|
||||||
|
re := regexp.MustCompile(`_\d+_\d+$`)
|
||||||
|
statisticType := re.ReplaceAllString(*result.Id, "")
|
||||||
|
|
||||||
|
// Remove the statistic type from the label to get the AWS Metric name
|
||||||
|
// e.g. "CPUUtilization" from "CPUUtilization_average"
|
||||||
|
re = regexp.MustCompile(`_?` + regexp.QuoteMeta(statisticType) + `$`)
|
||||||
|
tags["metric_name"] = re.ReplaceAllString(*result.Label, "")
|
||||||
|
|
||||||
|
grouper.Add(namespace, tags, result.Timestamps[i], statisticType, result.Values[i])
|
||||||
|
} else {
|
||||||
grouper.Add(namespace, tags, result.Timestamps[i], *result.Label, result.Values[i])
|
grouper.Add(namespace, tags, result.Timestamps[i], *result.Label, result.Values[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, metric := range grouper.Metrics() {
|
for _, metric := range grouper.Metrics() {
|
||||||
acc.AddMetric(metric)
|
acc.AddMetric(metric)
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,44 @@ func TestGather(t *testing.T) {
|
||||||
acc.AssertContainsTaggedFields(t, "cloudwatch_aws_elb", fields, tags)
|
acc.AssertContainsTaggedFields(t, "cloudwatch_aws_elb", fields, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGatherDenseMetric(t *testing.T) {
|
||||||
|
duration, _ := time.ParseDuration("1m")
|
||||||
|
internalDuration := config.Duration(duration)
|
||||||
|
c := &CloudWatch{
|
||||||
|
CredentialConfig: internalaws.CredentialConfig{
|
||||||
|
Region: "us-east-1",
|
||||||
|
},
|
||||||
|
Namespace: "AWS/ELB",
|
||||||
|
Delay: internalDuration,
|
||||||
|
Period: internalDuration,
|
||||||
|
RateLimit: 200,
|
||||||
|
BatchSize: 500,
|
||||||
|
MetricFormat: "dense",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
|
require.NoError(t, c.Init())
|
||||||
|
c.client = &mockGatherCloudWatchClient{}
|
||||||
|
require.NoError(t, acc.GatherError(c.Gather))
|
||||||
|
|
||||||
|
fields := map[string]interface{}{}
|
||||||
|
fields["minimum"] = 0.1
|
||||||
|
fields["maximum"] = 0.3
|
||||||
|
fields["average"] = 0.2
|
||||||
|
fields["sum"] = 123.0
|
||||||
|
fields["sample_count"] = 100.0
|
||||||
|
|
||||||
|
tags := map[string]string{}
|
||||||
|
tags["region"] = "us-east-1"
|
||||||
|
tags["load_balancer_name"] = "p-example1"
|
||||||
|
tags["metric_name"] = "latency"
|
||||||
|
|
||||||
|
require.True(t, acc.HasMeasurement("cloudwatch_aws_elb"))
|
||||||
|
acc.AssertContainsTaggedFields(t, "cloudwatch_aws_elb", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultiAccountGather(t *testing.T) {
|
func TestMultiAccountGather(t *testing.T) {
|
||||||
duration, _ := time.ParseDuration("1m")
|
duration, _ := time.ParseDuration("1m")
|
||||||
internalDuration := config.Duration(duration)
|
internalDuration := config.Duration(duration)
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,13 @@
|
||||||
## Metric Statistic Namespaces (required)
|
## Metric Statistic Namespaces (required)
|
||||||
namespaces = ["AWS/ELB"]
|
namespaces = ["AWS/ELB"]
|
||||||
|
|
||||||
|
## Metric Format
|
||||||
|
## This determines the format of the produces metrics. 'sparse', the default
|
||||||
|
## will produce a unique field for each statistic. 'dense' will report all
|
||||||
|
## statistics will be in a field called value and have a metric_name tag
|
||||||
|
## defining the name of the statistic. See the plugin README for examples.
|
||||||
|
# metric_format = "sparse"
|
||||||
|
|
||||||
## Maximum requests per second. Note that the global default AWS rate limit
|
## Maximum requests per second. Note that the global default AWS rate limit
|
||||||
## is 50 reqs/sec, so if you define multiple namespaces, these should add up
|
## is 50 reqs/sec, so if you define multiple namespaces, these should add up
|
||||||
## to a maximum of 50.
|
## to a maximum of 50.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue