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-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
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/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=
|
||||
|
|
|
|||
|
|
@ -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 = [ ]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue