feat(dynatrace-output): remove special handling from counters (#9675)

Co-authored-by: Armin Ruech <armin.ruech@dynatrace.com>
This commit is contained in:
Daniel Dyla 2021-09-08 14:31:42 -04:00 committed by GitHub
parent ba1484cb75
commit 95ef674456
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 73 deletions

2
go.mod
View File

@ -85,7 +85,7 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
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-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect

4
go.sum
View File

@ -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/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.2.0/go.mod h1:qw0E9EJ0PnSlhWawDNuqE0zhc1hqOBUCFIAj3dd9DNw=
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.3.0 h1:q2Ayh9s6Cr75bS5URiOUAoyFXemgKQaBJphbhAaJHCY=
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.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=

View File

@ -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.
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
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
@ -65,7 +67,7 @@ You can learn more about how to use the Dynatrace API [here](https://www.dynatra
prefix = "telegraf"
## Flag for skipping the tls certificate check, just for testing purposes, should be false by default
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 = [ ]
## Optional dimensions to be added to every metric
@ -119,7 +121,7 @@ insecure_skip_verify = 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
additional_counters = [ ]

View File

@ -69,7 +69,7 @@ const sampleConfig = `
## Connection timeout, defaults to "5s" if not set.
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 = [ ]
## 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))
}
metricType := tm.Type()
for _, field := range tm.FieldList() {
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 {
// 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 {
if metricType == telegraf.Counter {
func (d *Dynatrace) getTypeOption(metric telegraf.Metric, field *telegraf.Field) dtMetric.MetricOption {
metricName := metric.Name() + "." + field.Key
for _, i := range d.AddCounterMetrics {
if metricName != i {
continue
}
switch v := field.Value.(type) {
case float64:
return dtMetric.WithFloatCounterValueTotal(v)
return dtMetric.WithFloatCounterValueDelta(v)
case uint64:
return dtMetric.WithIntCounterValueTotal(int64(v))
return dtMetric.WithIntCounterValueDelta(int64(v))
case int64:
return dtMetric.WithIntCounterValueTotal(v)
return dtMetric.WithIntCounterValueDelta(v)
default:
return nil
}

View File

@ -2,10 +2,13 @@ package dynatrace
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"regexp"
"sort"
"strings"
"testing"
"time"
@ -123,26 +126,37 @@ func TestMissingAPIToken(t *testing.T) {
}
func TestSendMetrics(t *testing.T) {
expected := []string{}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// check the encoded result
bodyBytes, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
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 {
t.Errorf("Metric encoding failed. expected: %#v but got: %#v", expected, bodyString)
lines := strings.Split(bodyString, "\n")
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)
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)
}))
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()
require.NoError(t, err)
err = d.Connect()
@ -150,22 +164,43 @@ func TestSendMetrics(t *testing.T) {
// 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(
"mymeasurement",
"simple_metric",
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),
)
// 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(
"mymeasurement",
"counter_type",
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),
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)
require.NoError(t, err)
@ -475,47 +510,6 @@ func TestStaticDimensionsOverrideMetric(t *testing.T) {
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
type loggerStub struct {