feat(dynatrace-output): remove special handling from counters (#9675)
Co-authored-by: Armin Ruech <armin.ruech@dynatrace.com>
This commit is contained in:
parent
ba1484cb75
commit
95ef674456
2
go.mod
2
go.mod
|
|
@ -85,7 +85,7 @@ require (
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/doclambda/protobufquery v0.0.0-20210317203640-88ffabe06a60
|
github.com/doclambda/protobufquery v0.0.0-20210317203640-88ffabe06a60
|
||||||
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.2.0
|
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.3.0
|
||||||
github.com/eapache/go-resiliency v1.2.0 // indirect
|
github.com/eapache/go-resiliency v1.2.0 // indirect
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
|
||||||
github.com/eapache/queue v1.1.0 // indirect
|
github.com/eapache/queue v1.1.0 // indirect
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -507,8 +507,8 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.2.0 h1:TEG5Jj7RYM2JBCUH3nLqCmSZy6srnaefvXxjUTCuHyA=
|
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.3.0 h1:q2Ayh9s6Cr75bS5URiOUAoyFXemgKQaBJphbhAaJHCY=
|
||||||
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.2.0/go.mod h1:qw0E9EJ0PnSlhWawDNuqE0zhc1hqOBUCFIAj3dd9DNw=
|
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.3.0/go.mod h1:qw0E9EJ0PnSlhWawDNuqE0zhc1hqOBUCFIAj3dd9DNw=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
|
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
|
||||||
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
This plugin sends Telegraf metrics to [Dynatrace](https://www.dynatrace.com) via the [Dynatrace Metrics API V2](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/). It may be run alongside the Dynatrace OneAgent for automatic authentication or it may be run standalone on a host without a OneAgent by specifying a URL and API Token.
|
This plugin sends Telegraf metrics to [Dynatrace](https://www.dynatrace.com) via the [Dynatrace Metrics API V2](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/). It may be run alongside the Dynatrace OneAgent for automatic authentication or it may be run standalone on a host without a OneAgent by specifying a URL and API Token.
|
||||||
More information on the plugin can be found in the [Dynatrace documentation](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/telegraf/).
|
More information on the plugin can be found in the [Dynatrace documentation](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/telegraf/).
|
||||||
|
All metrics are reported as gauges, unless they are specified to be delta counters using the `additional_counters` config option (see below).
|
||||||
|
See the [Dynatrace Metrics ingestion protocol documentation](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/metric-ingestion-protocol) for details on the types defined there.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
You will either need a Dynatrace OneAgent (version 1.201 or higher) installed on the same host as Telegraf; or a Dynatrace environment with version 1.202 or higher. Monotonic counters (e.g. `diskio.reads`, `system.uptime`) require Dynatrace 208 or later.
|
You will either need a Dynatrace OneAgent (version 1.201 or higher) installed on the same host as Telegraf; or a Dynatrace environment with version 1.202 or higher.
|
||||||
|
|
||||||
- Telegraf minimum version: Telegraf 1.16
|
- Telegraf minimum version: Telegraf 1.16
|
||||||
|
|
||||||
|
|
@ -65,7 +67,7 @@ You can learn more about how to use the Dynatrace API [here](https://www.dynatra
|
||||||
prefix = "telegraf"
|
prefix = "telegraf"
|
||||||
## Flag for skipping the tls certificate check, just for testing purposes, should be false by default
|
## Flag for skipping the tls certificate check, just for testing purposes, should be false by default
|
||||||
insecure_skip_verify = false
|
insecure_skip_verify = false
|
||||||
## If you want to convert values represented as gauges to counters, add the metric names here
|
## If you want metrics to be treated and reported as delta counters, add the metric names here
|
||||||
additional_counters = [ ]
|
additional_counters = [ ]
|
||||||
|
|
||||||
## Optional dimensions to be added to every metric
|
## Optional dimensions to be added to every metric
|
||||||
|
|
@ -119,7 +121,7 @@ insecure_skip_verify = false
|
||||||
|
|
||||||
*required*: `false`
|
*required*: `false`
|
||||||
|
|
||||||
If you want to convert values represented as gauges to counters, add the metric names here.
|
If you want a metric to be treated and reported as a delta counter, add its name to this list.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
additional_counters = [ ]
|
additional_counters = [ ]
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ const sampleConfig = `
|
||||||
## Connection timeout, defaults to "5s" if not set.
|
## Connection timeout, defaults to "5s" if not set.
|
||||||
timeout = "5s"
|
timeout = "5s"
|
||||||
|
|
||||||
## If you want to convert values represented as gauges to counters, add the metric names here
|
## If you want metrics to be treated and reported as delta counters, add the metric names here
|
||||||
additional_counters = [ ]
|
additional_counters = [ ]
|
||||||
|
|
||||||
## Optional dimensions to be added to every metric
|
## Optional dimensions to be added to every metric
|
||||||
|
|
@ -122,16 +122,10 @@ func (d *Dynatrace) Write(metrics []telegraf.Metric) error {
|
||||||
dims = append(dims, dimensions.NewDimension(tag.Key, tag.Value))
|
dims = append(dims, dimensions.NewDimension(tag.Key, tag.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
metricType := tm.Type()
|
|
||||||
for _, field := range tm.FieldList() {
|
for _, field := range tm.FieldList() {
|
||||||
metricName := tm.Name() + "." + field.Key
|
metricName := tm.Name() + "." + field.Key
|
||||||
for _, i := range d.AddCounterMetrics {
|
|
||||||
if metricName == i {
|
|
||||||
metricType = telegraf.Counter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typeOpt := getTypeOption(metricType, field)
|
typeOpt := d.getTypeOption(tm, field)
|
||||||
|
|
||||||
if typeOpt == nil {
|
if typeOpt == nil {
|
||||||
// Unsupported type. Log only once per unsupported metric name
|
// Unsupported type. Log only once per unsupported metric name
|
||||||
|
|
@ -267,15 +261,19 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTypeOption(metricType telegraf.ValueType, field *telegraf.Field) dtMetric.MetricOption {
|
func (d *Dynatrace) getTypeOption(metric telegraf.Metric, field *telegraf.Field) dtMetric.MetricOption {
|
||||||
if metricType == telegraf.Counter {
|
metricName := metric.Name() + "." + field.Key
|
||||||
|
for _, i := range d.AddCounterMetrics {
|
||||||
|
if metricName != i {
|
||||||
|
continue
|
||||||
|
}
|
||||||
switch v := field.Value.(type) {
|
switch v := field.Value.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
return dtMetric.WithFloatCounterValueTotal(v)
|
return dtMetric.WithFloatCounterValueDelta(v)
|
||||||
case uint64:
|
case uint64:
|
||||||
return dtMetric.WithIntCounterValueTotal(int64(v))
|
return dtMetric.WithIntCounterValueDelta(int64(v))
|
||||||
case int64:
|
case int64:
|
||||||
return dtMetric.WithIntCounterValueTotal(v)
|
return dtMetric.WithIntCounterValueDelta(v)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,13 @@ package dynatrace
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -123,26 +126,37 @@ func TestMissingAPIToken(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendMetrics(t *testing.T) {
|
func TestSendMetrics(t *testing.T) {
|
||||||
|
expected := []string{}
|
||||||
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// check the encoded result
|
// check the encoded result
|
||||||
bodyBytes, err := ioutil.ReadAll(r.Body)
|
bodyBytes, err := ioutil.ReadAll(r.Body)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
bodyString := string(bodyBytes)
|
bodyString := string(bodyBytes)
|
||||||
expected := "mymeasurement.myfield,dt.metrics.source=telegraf gauge,3.14 1289430000000\nmymeasurement.value,dt.metrics.source=telegraf count,3.14 1289430000000"
|
|
||||||
if bodyString != expected {
|
lines := strings.Split(bodyString, "\n")
|
||||||
t.Errorf("Metric encoding failed. expected: %#v but got: %#v", expected, bodyString)
|
|
||||||
|
sort.Strings(lines)
|
||||||
|
sort.Strings(expected)
|
||||||
|
|
||||||
|
expectedString := strings.Join(expected, "\n")
|
||||||
|
foundString := strings.Join(lines, "\n")
|
||||||
|
if foundString != expectedString {
|
||||||
|
t.Errorf("Metric encoding failed. expected: %#v but got: %#v", expectedString, foundString)
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
err = json.NewEncoder(w).Encode(`{"linesOk":10,"linesInvalid":0,"error":null}`)
|
err = json.NewEncoder(w).Encode(fmt.Sprintf(`{"linesOk":%d,"linesInvalid":0,"error":null}`, len(lines)))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}))
|
}))
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
d := &Dynatrace{}
|
d := &Dynatrace{
|
||||||
|
URL: ts.URL,
|
||||||
|
APIToken: "123",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
AddCounterMetrics: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
d.URL = ts.URL
|
|
||||||
d.APIToken = "123"
|
|
||||||
d.Log = testutil.Logger{}
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = d.Connect()
|
err = d.Connect()
|
||||||
|
|
@ -150,22 +164,43 @@ func TestSendMetrics(t *testing.T) {
|
||||||
|
|
||||||
// Init metrics
|
// Init metrics
|
||||||
|
|
||||||
|
// Simple metrics are exported as a gauge unless in additional_counters
|
||||||
|
expected = append(expected, "simple_metric.value,dt.metrics.source=telegraf gauge,3.14 1289430000000")
|
||||||
|
expected = append(expected, "simple_metric.counter,dt.metrics.source=telegraf count,delta=5 1289430000000")
|
||||||
|
d.AddCounterMetrics = append(d.AddCounterMetrics, "simple_metric.counter")
|
||||||
m1 := metric.New(
|
m1 := metric.New(
|
||||||
"mymeasurement",
|
"simple_metric",
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
map[string]interface{}{"myfield": float64(3.14)},
|
map[string]interface{}{"value": float64(3.14), "counter": 5},
|
||||||
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Even if Type() returns counter, all metrics are treated as a gauge unless explicitly added to additional_counters
|
||||||
|
expected = append(expected, "counter_type.value,dt.metrics.source=telegraf gauge,3.14 1289430000000")
|
||||||
|
expected = append(expected, "counter_type.counter,dt.metrics.source=telegraf count,delta=5 1289430000000")
|
||||||
|
d.AddCounterMetrics = append(d.AddCounterMetrics, "counter_type.counter")
|
||||||
m2 := metric.New(
|
m2 := metric.New(
|
||||||
"mymeasurement",
|
"counter_type",
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
map[string]interface{}{"value": float64(3.14)},
|
map[string]interface{}{"value": float64(3.14), "counter": 5},
|
||||||
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||||
telegraf.Counter,
|
telegraf.Counter,
|
||||||
)
|
)
|
||||||
|
|
||||||
metrics := []telegraf.Metric{m1, m2}
|
expected = append(expected, "complex_metric.int,dt.metrics.source=telegraf gauge,1 1289430000000")
|
||||||
|
expected = append(expected, "complex_metric.int64,dt.metrics.source=telegraf gauge,2 1289430000000")
|
||||||
|
expected = append(expected, "complex_metric.float,dt.metrics.source=telegraf gauge,3 1289430000000")
|
||||||
|
expected = append(expected, "complex_metric.float64,dt.metrics.source=telegraf gauge,4 1289430000000")
|
||||||
|
expected = append(expected, "complex_metric.true,dt.metrics.source=telegraf gauge,1 1289430000000")
|
||||||
|
expected = append(expected, "complex_metric.false,dt.metrics.source=telegraf gauge,0 1289430000000")
|
||||||
|
m3 := metric.New(
|
||||||
|
"complex_metric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"int": 1, "int64": int64(2), "float": 3.0, "float64": float64(4.0), "true": true, "false": false},
|
||||||
|
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||||
|
)
|
||||||
|
|
||||||
|
metrics := []telegraf.Metric{m1, m2, m3}
|
||||||
|
|
||||||
err = d.Write(metrics)
|
err = d.Write(metrics)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -475,47 +510,6 @@ func TestStaticDimensionsOverrideMetric(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendCounterMetricWithoutTags(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
// check the encoded result
|
|
||||||
bodyBytes, err := ioutil.ReadAll(r.Body)
|
|
||||||
require.NoError(t, err)
|
|
||||||
bodyString := string(bodyBytes)
|
|
||||||
expected := "mymeasurement.value,dt.metrics.source=telegraf gauge,32 1289430000000"
|
|
||||||
if bodyString != expected {
|
|
||||||
t.Errorf("Metric encoding failed. expected: %#v but got: %#v", expected, bodyString)
|
|
||||||
}
|
|
||||||
err = json.NewEncoder(w).Encode(`{"linesOk":1,"linesInvalid":0,"error":null}`)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}))
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
d := &Dynatrace{}
|
|
||||||
|
|
||||||
d.URL = ts.URL
|
|
||||||
d.APIToken = "123"
|
|
||||||
d.Log = testutil.Logger{}
|
|
||||||
err := d.Init()
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = d.Connect()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Init metrics
|
|
||||||
|
|
||||||
m1 := metric.New(
|
|
||||||
"mymeasurement",
|
|
||||||
map[string]string{},
|
|
||||||
map[string]interface{}{"value": 32},
|
|
||||||
time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC),
|
|
||||||
)
|
|
||||||
|
|
||||||
metrics := []telegraf.Metric{m1}
|
|
||||||
|
|
||||||
err = d.Write(metrics)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var warnfCalledTimes int
|
var warnfCalledTimes int
|
||||||
|
|
||||||
type loggerStub struct {
|
type loggerStub struct {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue