Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
49
plugins/outputs/newrelic/README.md
Normal file
49
plugins/outputs/newrelic/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# New Relic Output Plugin
|
||||
|
||||
This plugins writes metrics to [New Relic Insights][newrelic] using the
|
||||
[Metrics API][metrics_api]. To use this plugin you have to obtain an
|
||||
[Insights API Key][insights_api_key].
|
||||
|
||||
⭐ Telegraf v1.15.0
|
||||
🏷️ applications
|
||||
💻 all
|
||||
|
||||
[newrelic]: https://newrelic.com
|
||||
[metrics_api]: https://docs.newrelic.com/docs/data-ingest-apis/get-data-new-relic/metric-api/introduction-metric-api
|
||||
[insights_api_key]: https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys#user-api-key
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Send metrics to New Relic metrics endpoint
|
||||
[[outputs.newrelic]]
|
||||
## The 'insights_key' parameter requires a NR license key.
|
||||
## New Relic recommends you create one
|
||||
## with a convenient name such as TELEGRAF_INSERT_KEY.
|
||||
## reference: https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key
|
||||
# insights_key = "New Relic License Key Here"
|
||||
|
||||
## Prefix to add to add to metric name for easy identification.
|
||||
## This is very useful if your metric names are ambiguous.
|
||||
# metric_prefix = ""
|
||||
|
||||
## Timeout for writes to the New Relic API.
|
||||
# timeout = "15s"
|
||||
|
||||
## HTTP Proxy override. If unset use values from the standard
|
||||
## proxy environment variables to determine proxy, if any.
|
||||
# http_proxy = "http://corporate.proxy:3128"
|
||||
|
||||
## Metric URL override to enable geographic location endpoints.
|
||||
# If not set use values from the standard
|
||||
# metric_url = "https://metric-api.newrelic.com/metric/v1"
|
||||
```
|
179
plugins/outputs/newrelic/newrelic.go
Normal file
179
plugins/outputs/newrelic/newrelic.go
Normal file
|
@ -0,0 +1,179 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package newrelic
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/newrelic/newrelic-telemetry-sdk-go/cumulative"
|
||||
"github.com/newrelic/newrelic-telemetry-sdk-go/telemetry"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/outputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
// NewRelic nr structure
|
||||
type NewRelic struct {
|
||||
InsightsKey string `toml:"insights_key"`
|
||||
MetricPrefix string `toml:"metric_prefix"`
|
||||
Timeout config.Duration `toml:"timeout"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
MetricURL string `toml:"metric_url"`
|
||||
|
||||
harvestor *telemetry.Harvester
|
||||
dc *cumulative.DeltaCalculator
|
||||
savedErrors map[int]interface{}
|
||||
errorCount int
|
||||
client http.Client
|
||||
}
|
||||
|
||||
func (*NewRelic) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
// Connect to the Output
|
||||
func (nr *NewRelic) Connect() error {
|
||||
if nr.InsightsKey == "" {
|
||||
return errors.New("'insights_key' is a required for newrelic")
|
||||
}
|
||||
err := nr.initClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nr.harvestor, err = telemetry.NewHarvester(telemetry.ConfigAPIKey(nr.InsightsKey),
|
||||
telemetry.ConfigHarvestPeriod(0),
|
||||
func(cfg *telemetry.Config) {
|
||||
cfg.Product = "NewRelic-Telegraf-Plugin"
|
||||
cfg.ProductVersion = "1.0"
|
||||
cfg.HarvestTimeout = time.Duration(nr.Timeout)
|
||||
cfg.Client = &nr.client
|
||||
cfg.ErrorLogger = func(e map[string]interface{}) {
|
||||
var errorString string
|
||||
for k, v := range e {
|
||||
errorString += fmt.Sprintf("%s = %s ", k, v)
|
||||
}
|
||||
nr.errorCount++
|
||||
nr.savedErrors[nr.errorCount] = errorString
|
||||
}
|
||||
if nr.MetricURL != "" {
|
||||
cfg.MetricsURLOverride = nr.MetricURL
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to newrelic: %w", err)
|
||||
}
|
||||
|
||||
nr.dc = cumulative.NewDeltaCalculator()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close any connections to the Output
|
||||
func (nr *NewRelic) Close() error {
|
||||
nr.errorCount = 0
|
||||
nr.client.CloseIdleConnections()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write takes in group of points to be written to the Output
|
||||
func (nr *NewRelic) Write(metrics []telegraf.Metric) error {
|
||||
nr.errorCount = 0
|
||||
nr.savedErrors = make(map[int]interface{})
|
||||
|
||||
for _, metric := range metrics {
|
||||
// create tag map
|
||||
tags := make(map[string]interface{})
|
||||
for _, tag := range metric.TagList() {
|
||||
tags[tag.Key] = tag.Value
|
||||
}
|
||||
for _, field := range metric.FieldList() {
|
||||
var mvalue float64
|
||||
var mname string
|
||||
if nr.MetricPrefix != "" {
|
||||
mname = nr.MetricPrefix + "." + metric.Name() + "." + field.Key
|
||||
} else {
|
||||
mname = metric.Name() + "." + field.Key
|
||||
}
|
||||
switch n := field.Value.(type) {
|
||||
case int64:
|
||||
mvalue = float64(n)
|
||||
case uint64:
|
||||
mvalue = float64(n)
|
||||
case float64:
|
||||
mvalue = n
|
||||
case bool:
|
||||
mvalue = float64(0)
|
||||
if n {
|
||||
mvalue = float64(1)
|
||||
}
|
||||
case string:
|
||||
// Do not log everytime we encounter string
|
||||
// we just skip
|
||||
continue
|
||||
default:
|
||||
return fmt.Errorf("undefined field type: %T", field.Value)
|
||||
}
|
||||
|
||||
switch metric.Type() {
|
||||
case telegraf.Counter:
|
||||
if counter, ok := nr.dc.CountMetric(mname, tags, mvalue, metric.Time()); ok {
|
||||
nr.harvestor.RecordMetric(counter)
|
||||
}
|
||||
default:
|
||||
nr.harvestor.RecordMetric(telemetry.Gauge{
|
||||
Timestamp: metric.Time(),
|
||||
Value: mvalue,
|
||||
Name: mname,
|
||||
Attributes: tags})
|
||||
}
|
||||
}
|
||||
}
|
||||
// By default, the Harvester sends metrics and spans to the New Relic
|
||||
// backend every 5 seconds. You can force data to be sent at any time
|
||||
// using HarvestNow.
|
||||
nr.harvestor.HarvestNow(context.Background())
|
||||
|
||||
// Check if we encountered errors
|
||||
if nr.errorCount != 0 {
|
||||
return fmt.Errorf("unable to harvest metrics %s ", nr.savedErrors[nr.errorCount])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
outputs.Add("newrelic", func() telegraf.Output {
|
||||
return &NewRelic{
|
||||
Timeout: config.Duration(time.Second * 15),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (nr *NewRelic) initClient() error {
|
||||
if nr.HTTPProxy == "" {
|
||||
nr.client = http.Client{}
|
||||
return nil
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(nr.HTTPProxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
}
|
||||
|
||||
nr.client = http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
return nil
|
||||
}
|
193
plugins/outputs/newrelic/newrelic_test.go
Normal file
193
plugins/outputs/newrelic/newrelic_test.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
package newrelic
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/newrelic/newrelic-telemetry-sdk-go/telemetry"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
nr := &NewRelic{
|
||||
MetricPrefix: "Test",
|
||||
InsightsKey: "12345",
|
||||
Timeout: config.Duration(time.Second * 5),
|
||||
}
|
||||
|
||||
err := nr.Connect()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = nr.Write(testutil.MockMetrics())
|
||||
require.Contains(t, err.Error(), "unable to harvest metrics")
|
||||
}
|
||||
|
||||
func TestNewRelic_Write(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metrics []telegraf.Metric
|
||||
auditMessage string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test: Basic mock metric write",
|
||||
metrics: testutil.MockMetrics(),
|
||||
wantErr: false,
|
||||
auditMessage: `"metrics":[{"name":"test1.value","type":"gauge","value":1,"timestamp":1257894000000,"attributes":{"tag1":"value1"}}]`,
|
||||
},
|
||||
{
|
||||
name: "Test: Test string ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric("value1", "test_String"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: "",
|
||||
},
|
||||
{
|
||||
name: "Test: Test int64 ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric(int64(15), "test_int64"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: `"metrics":[{"name":"test_int64.value","type":"gauge","value":15,"timestamp":1257894000000,"attributes":{"tag1":"value1"}}]`,
|
||||
},
|
||||
{
|
||||
name: "Test: Test uint64 ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric(uint64(20), "test_uint64"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: `"metrics":[{"name":"test_uint64.value","type":"gauge","value":20,"timestamp":1257894000000,"attributes":{"tag1":"value1"}}]`,
|
||||
},
|
||||
{
|
||||
name: "Test: Test bool true ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric(bool(true), "test_bool_true"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: `"metrics":[{"name":"test_bool_true.value","type":"gauge","value":1,"timestamp":1257894000000,"attributes":{"tag1":"value1"}}]`,
|
||||
},
|
||||
{
|
||||
name: "Test: Test bool false ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric(bool(false), "test_bool_false"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: `"metrics":[{"name":"test_bool_false.value","type":"gauge","value":0,"timestamp":1257894000000,"attributes":{"tag1":"value1"}}]`,
|
||||
},
|
||||
{
|
||||
name: "Test: Test max float64 ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric(math.MaxFloat64, "test_maxfloat64"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: `"metrics":[{"name":"test_maxfloat64.value","type":"gauge","value":1.7976931348623157e+308,` +
|
||||
`"timestamp":1257894000000,"attributes":{"tag1":"value1"}}]`,
|
||||
},
|
||||
{
|
||||
name: "Test: Test NAN ",
|
||||
metrics: []telegraf.Metric{
|
||||
testutil.TestMetric(math.NaN, "test_NaN"),
|
||||
},
|
||||
wantErr: false,
|
||||
auditMessage: ``,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var auditLog map[string]interface{}
|
||||
nr := &NewRelic{}
|
||||
var err error
|
||||
nr.harvestor, err = telemetry.NewHarvester(
|
||||
telemetry.ConfigHarvestPeriod(0),
|
||||
func(cfg *telemetry.Config) {
|
||||
cfg.APIKey = "dummyTestKey"
|
||||
cfg.HarvestPeriod = 0
|
||||
cfg.HarvestTimeout = 0
|
||||
cfg.AuditLogger = func(e map[string]interface{}) {
|
||||
auditLog = e
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = nr.Write(tt.metrics)
|
||||
require.NoError(t, err)
|
||||
if auditLog["data"] != nil {
|
||||
require.Contains(t, auditLog["data"], tt.auditMessage)
|
||||
} else {
|
||||
require.Contains(t, "", tt.auditMessage)
|
||||
}
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewRelic.Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRelic_Connect(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
newrelic *NewRelic
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test: No Insights key",
|
||||
newrelic: &NewRelic{
|
||||
MetricPrefix: "prefix",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Test: Insights key",
|
||||
newrelic: &NewRelic{
|
||||
InsightsKey: "12312133",
|
||||
MetricPrefix: "prefix",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test: Only Insights key",
|
||||
newrelic: &NewRelic{
|
||||
InsightsKey: "12312133",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test: Insights key and Timeout",
|
||||
newrelic: &NewRelic{
|
||||
InsightsKey: "12312133",
|
||||
Timeout: config.Duration(time.Second * 5),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test: HTTP Proxy",
|
||||
newrelic: &NewRelic{
|
||||
InsightsKey: "12121212",
|
||||
HTTPProxy: "https://my.proxy",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Test: Metric URL ",
|
||||
newrelic: &NewRelic{
|
||||
InsightsKey: "12121212",
|
||||
MetricURL: "https://test.nr.com",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
nr := tt.newrelic
|
||||
if err := nr.Connect(); (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewRelic.Connect() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
22
plugins/outputs/newrelic/sample.conf
Normal file
22
plugins/outputs/newrelic/sample.conf
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Send metrics to New Relic metrics endpoint
|
||||
[[outputs.newrelic]]
|
||||
## The 'insights_key' parameter requires a NR license key.
|
||||
## New Relic recommends you create one
|
||||
## with a convenient name such as TELEGRAF_INSERT_KEY.
|
||||
## reference: https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key
|
||||
# insights_key = "New Relic License Key Here"
|
||||
|
||||
## Prefix to add to add to metric name for easy identification.
|
||||
## This is very useful if your metric names are ambiguous.
|
||||
# metric_prefix = ""
|
||||
|
||||
## Timeout for writes to the New Relic API.
|
||||
# timeout = "15s"
|
||||
|
||||
## HTTP Proxy override. If unset use values from the standard
|
||||
## proxy environment variables to determine proxy, if any.
|
||||
# http_proxy = "http://corporate.proxy:3128"
|
||||
|
||||
## Metric URL override to enable geographic location endpoints.
|
||||
# If not set use values from the standard
|
||||
# metric_url = "https://metric-api.newrelic.com/metric/v1"
|
Loading…
Add table
Add a link
Reference in a new issue