diff --git a/.gitignore b/.gitignore index 0d07898f..22c18ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,11 +3,6 @@ *.log tmp/ vendor/*/ -!vendor/github.com/ -vendor/github.com/*/ -!vendor/github.com/newrelic/ -vendor/github.com/newrelic/*/ -!vendor/github.com/newrelic/go-telemetry-sdk/ bin/ deploy/local*.yaml dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md index d3dd626d..8eeabdb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased +### Changed +- Upgrade the Go Telemetry SDK to version 0.2.0 to fix an issue with NaN values. + ## 1.2.2 ### Added - Obfuscate the license key when logging the configuration. Running on debug diff --git a/internal/cmd/scraper/scraper.go b/internal/cmd/scraper/scraper.go index c84a32ba..9a074a28 100644 --- a/internal/cmd/scraper/scraper.go +++ b/internal/cmd/scraper/scraper.go @@ -12,9 +12,10 @@ import ( "os" "time" - "github.com/newrelic/go-telemetry-sdk/telemetry" + "github.com/newrelic/newrelic-telemetry-sdk-go/telemetry" "github.com/newrelic/nri-prometheus/internal/integration" "github.com/newrelic/nri-prometheus/internal/pkg/endpoints" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" ) @@ -244,7 +245,12 @@ func Run(cfg *Config) error { Percentiles: cfg.Percentiles, HarvesterOpts: harvesterOpts, } - emitters = append(emitters, integration.NewTelemetryEmitter(c)) + + emitter, err := integration.NewTelemetryEmitter(c) + if err != nil { + return errors.Wrap(err, "could not create new TelemetryEmitter") + } + emitters = append(emitters, emitter) default: logrus.Debugf("unknown emitter: %s", e) continue diff --git a/internal/integration/emitter.go b/internal/integration/emitter.go index 836edeb2..249caa3f 100644 --- a/internal/integration/emitter.go +++ b/internal/integration/emitter.go @@ -12,9 +12,10 @@ import ( "net/url" "time" - "github.com/newrelic/go-telemetry-sdk/cumulative" - "github.com/newrelic/go-telemetry-sdk/telemetry" + "github.com/newrelic/newrelic-telemetry-sdk-go/cumulative" + "github.com/newrelic/newrelic-telemetry-sdk-go/telemetry" "github.com/newrelic/nri-prometheus/internal/histogram" + "github.com/pkg/errors" dto "github.com/prometheus/client_model/go" "github.com/sirupsen/logrus" ) @@ -143,7 +144,7 @@ func TelemetryHarvesterWithProxy(proxyURL *url.URL) TelemetryHarvesterOpt { } // NewTelemetryEmitter returns a new TelemetryEmitter. -func NewTelemetryEmitter(cfg TelemetryEmitterConfig) *TelemetryEmitter { +func NewTelemetryEmitter(cfg TelemetryEmitterConfig) (*TelemetryEmitter, error) { dc := cumulative.NewDeltaCalculator() if cfg.DeltaExpirationAge != 0 { @@ -166,12 +167,17 @@ func NewTelemetryEmitter(cfg TelemetryEmitterConfig) *TelemetryEmitter { cfg.DeltaExpirationAge, ) + harvester, err := telemetry.NewHarvester(cfg.HarvesterOpts...) + if err != nil { + return nil, errors.Wrap(err, "could not create new Harvester") + } + return &TelemetryEmitter{ name: "telemetry", - harvester: telemetry.NewHarvester(cfg.HarvesterOpts...), + harvester: harvester, percentiles: cfg.Percentiles, deltaCalculator: dc, - } + }, nil } // Name returns the emitter name. @@ -261,23 +267,12 @@ func (te *TelemetryEmitter) emitSummary(metric Metric, timestamp time.Time) erro continue } - v := q.GetValue() - if !validNRValue(v) { - err := fmt.Errorf("invalid percentile value for %s: %g", metric.name, v) - if results == nil { - results = err - } else { - results = fmt.Errorf("%v: %w", err, results) - } - continue - } - percentileAttrs := copyAttrs(metric.attributes) percentileAttrs["percentile"] = p te.harvester.RecordMetric(telemetry.Gauge{ Name: metricName, Attributes: percentileAttrs, - Value: v, + Value: q.GetValue(), Timestamp: timestamp, }) } @@ -294,10 +289,8 @@ func (te *TelemetryEmitter) emitHistogram(metric Metric, timestamp time.Time) er return fmt.Errorf("unknown histogram metric type for %q: %T", metric.name, metric.value) } - if validNRValue(hist.GetSampleSum()) { - if m, ok := te.deltaCalculator.CountMetric(metric.name+".sum", metric.attributes, hist.GetSampleSum(), timestamp); ok { - te.harvester.RecordMetric(m) - } + if m, ok := te.deltaCalculator.CountMetric(metric.name+".sum", metric.attributes, hist.GetSampleSum(), timestamp); ok { + te.harvester.RecordMetric(m) } metricName := metric.name + ".buckets" @@ -305,7 +298,7 @@ func (te *TelemetryEmitter) emitHistogram(metric Metric, timestamp time.Time) er for _, b := range hist.GetBucket() { upperBound := b.GetUpperBound() count := float64(b.GetCumulativeCount()) - if !math.IsInf(upperBound, 1) && validNRValue(count) { + if !math.IsInf(upperBound, 1) { bucketAttrs := copyAttrs(metric.attributes) bucketAttrs["histogram.bucket.upperBound"] = upperBound if m, ok := te.deltaCalculator.CountMetric(metricName, bucketAttrs, count, timestamp); ok { @@ -334,16 +327,6 @@ func (te *TelemetryEmitter) emitHistogram(metric Metric, timestamp time.Time) er continue } - if !validNRValue(v) { - err := fmt.Errorf("invalid percentile value for %s: %g", metric.name, v) - if results == nil { - results = err - } else { - results = fmt.Errorf("%v: %w", err, results) - } - continue - } - percentileAttrs := copyAttrs(metric.attributes) percentileAttrs["percentile"] = p te.harvester.RecordMetric(telemetry.Gauge{ @@ -366,11 +349,6 @@ func copyAttrs(attrs map[string]interface{}) map[string]interface{} { return duplicate } -// validNRValue returns if v is a New Relic metric supported float64. -func validNRValue(v float64) bool { - return !math.IsInf(v, 0) && !math.IsNaN(v) -} - // StdoutEmitter emits metrics to stdout. type StdoutEmitter struct { name string diff --git a/internal/integration/emitter_test.go b/internal/integration/emitter_test.go index 61afecb2..5f921afb 100644 --- a/internal/integration/emitter_test.go +++ b/internal/integration/emitter_test.go @@ -5,6 +5,7 @@ package integration import ( "bytes" "compress/gzip" + "context" "crypto/tls" "encoding/json" "fmt" @@ -17,7 +18,6 @@ import ( "strconv" "testing" - "github.com/newrelic/go-telemetry-sdk/telemetry" "github.com/pkg/errors" dto "github.com/prometheus/client_model/go" mpb "github.com/prometheus/client_model/go" @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/newrelic/newrelic-telemetry-sdk-go/telemetry" "github.com/newrelic/nri-prometheus/internal/pkg/labels" "github.com/newrelic/nri-prometheus/internal/pkg/prometheus" ) @@ -67,11 +68,12 @@ func BenchmarkTelemetrySDKEmitter(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - emitter := NewTelemetryEmitter(c) + emitter, err := NewTelemetryEmitter(c) + assert.NoError(b, err) err = emitter.Emit(superMetrics) assert.NoError(b, err) // Need to trigger a manual harvest here otherwise the benchmark is useless. - emitter.harvester.HarvestNow() + emitter.harvester.HarvestNow(context.Background()) } } @@ -217,11 +219,12 @@ func TestTelemetryEmitterEmit(t *testing.T) { Percentiles: []float64{50.0}, } - e := NewTelemetryEmitter(c) + e, err := NewTelemetryEmitter(c) + assert.NoError(t, err) // Emit and force a harvest to clear. assert.NoError(t, e.Emit(metrics)) - e.harvester.HarvestNow() + e.harvester.HarvestNow(context.Background()) // Set new histogram values so counts will be non-zero. hist2, err := newHistogram([]int64{1, 2, 10}) @@ -232,7 +235,7 @@ func TestTelemetryEmitterEmit(t *testing.T) { // Run twice so delta counts are sent. assert.NoError(t, e.Emit(metrics)) - e.harvester.HarvestNow() + e.harvester.HarvestNow(context.Background()) purgeTimestamps(rawMetrics) expectedMetrics := []interface{}{ diff --git a/internal/integration/roundtripper.go b/internal/integration/roundtripper.go index 5fc7e38e..5f4b2cc4 100644 --- a/internal/integration/roundtripper.go +++ b/internal/integration/roundtripper.go @@ -8,10 +8,10 @@ type licenseKeyRoundTripper struct { rt http.RoundTripper } -// RoundTrip wraps the `RoundTrip` method removing the "X-Insert-Key" +// RoundTrip wraps the `RoundTrip` method removing the "Api-Key" // replacing it with "X-License-Key". func (t licenseKeyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - req.Header.Del("X-Insert-Key") + req.Header.Del("Api-Key") req.Header.Add("X-License-Key", t.licenseKey) return t.rt.RoundTrip(req) } diff --git a/internal/integration/roundtripper_test.go b/internal/integration/roundtripper_test.go index c1ae2223..db443691 100644 --- a/internal/integration/roundtripper_test.go +++ b/internal/integration/roundtripper_test.go @@ -21,13 +21,13 @@ func (m *mockedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error func TestRoundTripHeaderDecoration(t *testing.T) { licenseKey := "myLicenseKey" req := &http.Request{Header: make(http.Header)} - req.Header.Add("X-Insert-Key", "insertKey") + req.Header.Add("Api-Key", licenseKey) rt := new(mockedRoundTripper) rt.On("RoundTrip", req).Return().Run(func(args mock.Arguments) { req := args.Get(0).(*http.Request) assert.Equal(t, licenseKey, req.Header.Get("X-License-Key")) - assert.Equal(t, "", req.Header.Get("X-Insert-Key")) + assert.Equal(t, "", req.Header.Get("Api-Key")) }) tr := newLicenseKeyRoundTripper(rt, licenseKey) diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/cumulative/cumulative.go b/vendor/github.com/newrelic/go-telemetry-sdk/cumulative/cumulative.go deleted file mode 100644 index d59bea71..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/cumulative/cumulative.go +++ /dev/null @@ -1,100 +0,0 @@ -// Package cumulative creates Count metrics from cumulative values. -package cumulative - -import ( - "sync" - "time" - - "github.com/newrelic/go-telemetry-sdk/internal" - "github.com/newrelic/go-telemetry-sdk/telemetry" -) - -type metricIdentity struct { - name string - attributesJSON string -} - -type lastValue struct { - when time.Time - value float64 -} - -// DeltaCalculator is used to create Count metrics from cumulative values. -type DeltaCalculator struct { - lock sync.Mutex - datapoints map[metricIdentity]lastValue - lastClean time.Time - expirationCheckInterval time.Duration - expirationAge time.Duration -} - -// NewDeltaCalculator creates a new DeltaCalculator. A single DeltaCalculator -// stores all cumulative values seen in order to compute deltas. -func NewDeltaCalculator() *DeltaCalculator { - return &DeltaCalculator{ - datapoints: make(map[metricIdentity]lastValue), - // These defaults are described in the Set method doc comments. - expirationCheckInterval: 20 * time.Minute, - expirationAge: 20 * time.Minute, - } -} - -// SetExpirationAge configures how old entries must be for expiration. The -// default is twenty minutes. -func (dc *DeltaCalculator) SetExpirationAge(age time.Duration) *DeltaCalculator { - dc.lock.Lock() - defer dc.lock.Unlock() - dc.expirationAge = age - return dc -} - -// SetExpirationCheckInterval configures how often to check for expired entries. -// The default is twenty minutes. -func (dc *DeltaCalculator) SetExpirationCheckInterval(interval time.Duration) *DeltaCalculator { - dc.lock.Lock() - defer dc.lock.Unlock() - dc.expirationCheckInterval = interval - return dc -} - -// CountMetric creates a count metric from the difference between the values and -// timestamps of multiple calls. If this is the first time the name/attributes -// combination has been seen then the `valid` return value will be false. -func (dc *DeltaCalculator) CountMetric(name string, attributes map[string]interface{}, val float64, now time.Time) (count telemetry.Count, valid bool) { - var attributesJSON []byte - if nil != attributes { - attributesJSON = internal.MarshalOrderedAttributes(attributes) - } - dc.lock.Lock() - defer dc.lock.Unlock() - - if now.Sub(dc.lastClean) > dc.expirationCheckInterval { - cutoff := now.Add(-dc.expirationAge) - for k, v := range dc.datapoints { - if v.when.Before(cutoff) { - delete(dc.datapoints, k) - } - } - dc.lastClean = now - } - - id := metricIdentity{name: name, attributesJSON: string(attributesJSON)} - var timestampsOrdered bool - last, ok := dc.datapoints[id] - if ok { - delta := val - last.value - timestampsOrdered = now.After(last.when) - if timestampsOrdered && delta >= 0 { - count.Name = name - count.AttributesJSON = attributesJSON - count.Value = delta - count.Timestamp = last.when - count.Interval = now.Sub(last.when) - valid = true - } - } - if !ok || timestampsOrdered { - dc.datapoints[id] = lastValue{value: val, when: now} - } - return -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/internal/attributes.go b/vendor/github.com/newrelic/go-telemetry-sdk/internal/attributes.go deleted file mode 100644 index 99ac7020..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/internal/attributes.go +++ /dev/null @@ -1,96 +0,0 @@ -package internal - -import ( - "bytes" - "encoding/json" - "fmt" - "sort" -) - -// MarshalAttributes turns attributes into JSON. -func MarshalAttributes(ats map[string]interface{}) []byte { - attrs := Attributes(ats) - buf := &bytes.Buffer{} - attrs.WriteJSON(buf) - return buf.Bytes() -} - -// Attributes is used for marshalling attributes to JSON. -type Attributes map[string]interface{} - -// WriteJSON writes the attributes in JSON. -func (attrs Attributes) WriteJSON(buf *bytes.Buffer) { - w := JSONFieldsWriter{Buf: buf} - w.Buf.WriteByte('{') - for key, val := range attrs { - writeAttribute(&w, key, val) - } - w.Buf.WriteByte('}') -} - -// MarshalOrderedAttributes marshals the given attributes into JSON in -// alphabetical order. -func MarshalOrderedAttributes(attrs map[string]interface{}) []byte { - buf := &bytes.Buffer{} - OrderedAttributes(attrs).WriteJSON(buf) - return buf.Bytes() -} - -// OrderedAttributes turns attributes into JSON in a fixed order. -type OrderedAttributes map[string]interface{} - -// WriteJSON writes the attributes in JSON in a fixed order. -func (attrs OrderedAttributes) WriteJSON(buf *bytes.Buffer) { - keys := make([]string, 0, len(attrs)) - for k := range attrs { - keys = append(keys, k) - } - sort.Strings(keys) - w := JSONFieldsWriter{Buf: buf} - w.Buf.WriteByte('{') - for _, k := range keys { - writeAttribute(&w, k, attrs[k]) - } - w.Buf.WriteByte('}') -} - -func writeAttribute(w *JSONFieldsWriter, key string, val interface{}) { - switch v := val.(type) { - case string: - w.StringField(key, v) - case bool: - if v { - w.RawField(key, json.RawMessage(`true`)) - } else { - w.RawField(key, json.RawMessage(`false`)) - } - case uint8: - w.IntField(key, int64(v)) - case uint16: - w.IntField(key, int64(v)) - case uint32: - w.IntField(key, int64(v)) - case uint64: - w.IntField(key, int64(v)) - case uint: - w.IntField(key, int64(v)) - case uintptr: - w.IntField(key, int64(v)) - case int8: - w.IntField(key, int64(v)) - case int16: - w.IntField(key, int64(v)) - case int32: - w.IntField(key, int64(v)) - case int64: - w.IntField(key, v) - case int: - w.IntField(key, int64(v)) - case float32: - w.FloatField(key, float64(v)) - case float64: - w.FloatField(key, v) - default: - w.StringField(key, fmt.Sprintf("%T", v)) - } -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/internal/compress.go b/vendor/github.com/newrelic/go-telemetry-sdk/internal/compress.go deleted file mode 100644 index 065b61a7..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/internal/compress.go +++ /dev/null @@ -1,32 +0,0 @@ -package internal - -import ( - "bytes" - "compress/gzip" - "io/ioutil" -) - -// Compress gzips the given input. -func Compress(b []byte) (*bytes.Buffer, error) { - var buf bytes.Buffer - w := gzip.NewWriter(&buf) - _, err := w.Write(b) - w.Close() - - if nil != err { - return nil, err - } - - return &buf, nil -} - -// Uncompress un-gzips the given input. -func Uncompress(b []byte) ([]byte, error) { - buf := bytes.NewBuffer(b) - gz, err := gzip.NewReader(buf) - if nil != err { - return nil, err - } - defer gz.Close() - return ioutil.ReadAll(gz) -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/internal/json_writer.go b/vendor/github.com/newrelic/go-telemetry-sdk/internal/json_writer.go deleted file mode 100644 index a80167b2..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/internal/json_writer.go +++ /dev/null @@ -1,72 +0,0 @@ -package internal - -import ( - "bytes" - "encoding/json" - - "github.com/newrelic/go-telemetry-sdk/internal/jsonx" -) - -// JSONWriter is something that can write JSON to a buffer. -type JSONWriter interface { - WriteJSON(buf *bytes.Buffer) -} - -// JSONFieldsWriter helps write JSON objects to a buffer. -type JSONFieldsWriter struct { - Buf *bytes.Buffer - needsComma bool -} - -// AddKey adds the key for a new object field. A comma is prefixed if another -// field has previously been added. -func (w *JSONFieldsWriter) AddKey(key string) { - if w.needsComma { - w.Buf.WriteByte(',') - } else { - w.needsComma = true - } - // defensively assume that the key needs escaping: - jsonx.AppendString(w.Buf, key) - w.Buf.WriteByte(':') -} - -// StringField adds a string field to the object. -func (w *JSONFieldsWriter) StringField(key string, val string) { - w.AddKey(key) - jsonx.AppendString(w.Buf, val) -} - -// IntField adds an int field to the object. -func (w *JSONFieldsWriter) IntField(key string, val int64) { - w.AddKey(key) - jsonx.AppendInt(w.Buf, val) -} - -// FloatField adds a float field to the object. -func (w *JSONFieldsWriter) FloatField(key string, val float64) { - w.AddKey(key) - jsonx.AppendFloat(w.Buf, val) -} - -// BoolField adds a bool field to the object. -func (w *JSONFieldsWriter) BoolField(key string, val bool) { - w.AddKey(key) - if val { - w.Buf.WriteString("true") - } else { - w.Buf.WriteString("false") - } -} - -// RawField adds a raw JSON field to the object. -func (w *JSONFieldsWriter) RawField(key string, val json.RawMessage) { - w.AddKey(key) - w.Buf.Write(val) -} - -// WriterField adds a JSONWriter field to the object. -func (w *JSONFieldsWriter) WriterField(key string, val JSONWriter) { - w.AddKey(key) - val.WriteJSON(w.Buf) -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/internal/jsonx/encode.go b/vendor/github.com/newrelic/go-telemetry-sdk/internal/jsonx/encode.go deleted file mode 100644 index 6495829f..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/internal/jsonx/encode.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package jsonx extends the encoding/json package to encode JSON -// incrementally and without requiring reflection. -package jsonx - -import ( - "bytes" - "encoding/json" - "math" - "reflect" - "strconv" - "unicode/utf8" -) - -var hex = "0123456789abcdef" - -// AppendString escapes s appends it to buf. -func AppendString(buf *bytes.Buffer, s string) { - buf.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { - i++ - continue - } - if start < i { - buf.WriteString(s[start:i]) - } - switch b { - case '\\', '"': - buf.WriteByte('\\') - buf.WriteByte(b) - case '\n': - buf.WriteByte('\\') - buf.WriteByte('n') - case '\r': - buf.WriteByte('\\') - buf.WriteByte('r') - case '\t': - buf.WriteByte('\\') - buf.WriteByte('t') - default: - // This encodes bytes < 0x20 except for \n and \r, - // as well as <, > and &. The latter are escaped because they - // can lead to security holes when user-controlled strings - // are rendered into JSON and served to some browsers. - buf.WriteString(`\u00`) - buf.WriteByte(hex[b>>4]) - buf.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { - if start < i { - buf.WriteString(s[start:i]) - } - buf.WriteString(`\ufffd`) - i += size - start = i - continue - } - // U+2028 is LINE SEPARATOR. - // U+2029 is PARAGRAPH SEPARATOR. - // They are both technically valid characters in JSON strings, - // but don't work in JSONP, which has to be evaluated as JavaScript, - // and can lead to security holes there. It is valid JSON to - // escape them, so we do so unconditionally. - // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. - if c == '\u2028' || c == '\u2029' { - if start < i { - buf.WriteString(s[start:i]) - } - buf.WriteString(`\u202`) - buf.WriteByte(hex[c&0xF]) - i += size - start = i - continue - } - i += size - } - if start < len(s) { - buf.WriteString(s[start:]) - } - buf.WriteByte('"') -} - -// AppendStringArray appends an array of string literals to buf. -func AppendStringArray(buf *bytes.Buffer, a ...string) { - buf.WriteByte('[') - for i, s := range a { - if i > 0 { - buf.WriteByte(',') - } - AppendString(buf, s) - } - buf.WriteByte(']') -} - -// AppendFloat appends a numeric literal representing the value to buf. -func AppendFloat(buf *bytes.Buffer, x float64) error { - var scratch [64]byte - - if math.IsInf(x, 0) || math.IsNaN(x) { - return &json.UnsupportedValueError{ - Value: reflect.ValueOf(x), - Str: strconv.FormatFloat(x, 'g', -1, 64), - } - } - - buf.Write(strconv.AppendFloat(scratch[:0], x, 'g', -1, 64)) - return nil -} - -// AppendFloatArray appends an array of numeric literals to buf. -func AppendFloatArray(buf *bytes.Buffer, a ...float64) error { - buf.WriteByte('[') - for i, x := range a { - if i > 0 { - buf.WriteByte(',') - } - if err := AppendFloat(buf, x); err != nil { - return err - } - } - buf.WriteByte(']') - return nil -} - -// AppendInt appends a numeric literal representing the value to buf. -func AppendInt(buf *bytes.Buffer, x int64) { - var scratch [64]byte - buf.Write(strconv.AppendInt(scratch[:0], x, 10)) -} - -// AppendIntArray appends an array of numeric literals to buf. -func AppendIntArray(buf *bytes.Buffer, a ...int64) { - var scratch [64]byte - - buf.WriteByte('[') - for i, x := range a { - if i > 0 { - buf.WriteByte(',') - } - buf.Write(strconv.AppendInt(scratch[:0], x, 10)) - } - buf.WriteByte(']') -} - -// AppendUint appends a numeric literal representing the value to buf. -func AppendUint(buf *bytes.Buffer, x uint64) { - var scratch [64]byte - buf.Write(strconv.AppendUint(scratch[:0], x, 10)) -} - -// AppendUintArray appends an array of numeric literals to buf. -func AppendUintArray(buf *bytes.Buffer, a ...uint64) { - var scratch [64]byte - - buf.WriteByte('[') - for i, x := range a { - if i > 0 { - buf.WriteByte(',') - } - buf.Write(strconv.AppendUint(scratch[:0], x, 10)) - } - buf.WriteByte(']') -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/internal/version.go b/vendor/github.com/newrelic/go-telemetry-sdk/internal/version.go deleted file mode 100644 index 6290c1de..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/internal/version.go +++ /dev/null @@ -1,17 +0,0 @@ -package internal - -import "net/http" - -const ( - major = "0" - minor = "1" - patch = "0" - - // Version is the full string version of this SDK. - Version = major + "." + minor + "." + patch -) - -// AddUserAgentHeader adds a User-Agent header with the SDK's version. -func AddUserAgentHeader(h http.Header) { - h.Add("User-Agent", "NewRelic-Go-TelemetrySDK/"+Version) -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/README.md b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/README.md deleted file mode 100644 index f99f983d..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# telemetry - -This package provides basic interaction with the New Relic Metrics and Spans -HTTP APIs, automatic batch harvesting on a given schedule, and handling of -errors from the API response. - -## Usage - -Create a Harvester. It will store your metrics and spans and send this data in -the background. - - ```go - harvester := telemetry.NewHarvester( - telemetry.ConfigAPIKey(os.Getenv("NEW_RELIC_API_KEY")), - telemetry.ConfigLicenseKey(os.Getenv("NEW_RELIC_LICENSE_KEY")), - ) - ``` - -Record metrics and/or spans. - - ```go - harvester.RecordMetric(Gauge{ - Name: "Temperature", - Attributes: map[string]interface{}{"zip": "zap"}, - Value: 55.62, - Timestamp: time.Now(), - }) - harvester.RecordSpan(Span{ - GUID: "12345", - TraceID: "67890", - Name: "mySpan", - Timestamp: time.Now(), - Duration: time.Second, - EntityName: "Getting-Started", - Attributes: map[string]interface{}{ - "color": "purple", - }, - }) - ``` - -Data will be sent to New Relic every 5 seconds by default. diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/attributes.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/attributes.go deleted file mode 100644 index 0c94bb55..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/attributes.go +++ /dev/null @@ -1,43 +0,0 @@ -package telemetry - -import ( - "fmt" -) - -func attributeValueValid(val interface{}) bool { - switch val.(type) { - case string, bool, uint8, uint16, uint32, uint64, int8, int16, - int32, int64, float32, float64, uint, int, uintptr: - return true - default: - return false - } -} - -// vetAttributes returns the attributes that are valid. vetAttributes does not -// modify remove any elements from its parameter. -func vetAttributes(attributes map[string]interface{}, errorLogger func(map[string]interface{})) map[string]interface{} { - valid := true - for _, val := range attributes { - if !attributeValueValid(val) { - valid = false - break - } - } - if valid { - return attributes - } - // Note that the map is only copied if elements are to be removed to - // improve performance. - validAttributes := make(map[string]interface{}, len(attributes)) - for key, val := range attributes { - if attributeValueValid(val) { - validAttributes[key] = val - } else if nil != errorLogger { - errorLogger(map[string]interface{}{ - "err": fmt.Sprintf(`attribute "%s" has invalid type %T`, key, val), - }) - } - } - return validAttributes -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/config.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/config.go deleted file mode 100644 index ad89de9f..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/config.go +++ /dev/null @@ -1,127 +0,0 @@ -package telemetry - -import ( - "encoding/json" - "io" - "log" - "net/http" - "time" -) - -// Config customizes the behavior of a Harvester. -type Config struct { - // Client is the http.Client used for making requests. By default it is - // given a Timeout of 5 seconds. - Client *http.Client - // MaxTries is the maximum number of times to attempt to send a payload - // before dropping it. By default, MaxTries is set to 3. - MaxTries int - // RetryBackoff is the amount of time to wait between attempts to send a - // payload. By default, RetryBackoff is set to 1 second. - RetryBackoff time.Duration - // APIKey is required for metrics. - APIKey string - // LicenseKey is required for spans. - LicenseKey string - // CommonAttributes are the attributes to be applied to all metrics that - // use this Config. They are not applied to spans. - CommonAttributes map[string]interface{} - // HarvestPeriod controls how frequently data will be sent to New Relic. - // If HarvestPeriod is zero then NewHarvester will not spawn a goroutine - // to send data and it is incumbent on the consumer to call - // Harvester.HarvestNow when data should be sent. By default, HarvestPeriod - // is set to 5 seconds. - HarvestPeriod time.Duration - // ErrorLogger receives errors that occur in this sdk. - ErrorLogger func(map[string]interface{}) - // DebugLogger receives structured debug log messages. - DebugLogger func(map[string]interface{}) - // MetricsURLOverride overrides the metrics endpoint if not not empty. - MetricsURLOverride string - // SpansURLOverride overrides the spans endpoint if not not empty. - SpansURLOverride string - // BeforeHarvestFunc is a callback function that will be called before a - // harvest occurs. - BeforeHarvestFunc func(*Harvester) -} - -// ConfigAPIKey sets the Config's APIKey which is required for metrics. -func ConfigAPIKey(key string) func(*Config) { - return func(cfg *Config) { - cfg.APIKey = key - } -} - -// ConfigLicenseKey sets the Config's LicenseKey which is required for spans. -func ConfigLicenseKey(key string) func(*Config) { - return func(cfg *Config) { - cfg.LicenseKey = key - } -} - -// ConfigCommonAttributes adds the given attributes to the Config's -// CommonAttributes. -func ConfigCommonAttributes(attributes map[string]interface{}) func(*Config) { - return func(cfg *Config) { - cfg.CommonAttributes = attributes - } -} - -// ConfigHarvestPeriod sets the Config's HarvestPeriod field which controls the -// rate data is reported to New Relic. If it is set to zero then the Harvester -// will never report data unless HarvestNow is called. -func ConfigHarvestPeriod(period time.Duration) func(*Config) { - return func(cfg *Config) { - cfg.HarvestPeriod = period - } -} - -func newBasicLogger(w io.Writer) func(map[string]interface{}) { - flags := log.Ldate | log.Ltime | log.Lmicroseconds - lg := log.New(w, "", flags) - return func(fields map[string]interface{}) { - if js, err := json.Marshal(fields); nil != err { - lg.Println(err.Error()) - } else { - lg.Println(string(js)) - } - } -} - -// ConfigBasicErrorLogger sets the error logger to a simple logger that logs -// to the writer provided. -func ConfigBasicErrorLogger(w io.Writer) func(*Config) { - return func(cfg *Config) { - cfg.ErrorLogger = newBasicLogger(w) - } -} - -// ConfigBasicDebugLogger sets the debug logger to a simple logger that logs -// to the writer provided. -func ConfigBasicDebugLogger(w io.Writer) func(*Config) { - return func(cfg *Config) { - cfg.DebugLogger = newBasicLogger(w) - } -} - -// configTesting is the config function to be used when testing. It sets the -// APIKey and LicenseKey but disables the harvest goroutine. -func configTesting(cfg *Config) { - cfg.APIKey = "api-key" - cfg.LicenseKey = "license-key" - cfg.HarvestPeriod = 0 -} - -func (cfg *Config) logError(fields map[string]interface{}) { - if nil == cfg.ErrorLogger { - return - } - cfg.ErrorLogger(fields) -} - -func (cfg *Config) logDebug(fields map[string]interface{}) { - if nil == cfg.DebugLogger { - return - } - cfg.DebugLogger(fields) -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/doc.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/doc.go deleted file mode 100644 index 5d815ce5..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/doc.go +++ /dev/null @@ -1,10 +0,0 @@ -// Package telemetry is the recommended way of interacting with the New -// Relic Metrics and Spans HTTP APIs. -// -// This package provides basic interaction with the New Relic Metrics and Spans -// HTTP APIs, automatic batch harvesting on a given schedule, and handling of -// errors from the API response. -// -// To aggregate metrics between harvests, use the instrumentation package. -// -package telemetry diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/harvester.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/harvester.go deleted file mode 100644 index 6c91456b..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/harvester.go +++ /dev/null @@ -1,338 +0,0 @@ -package telemetry - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "sync" - "time" - - "github.com/newrelic/go-telemetry-sdk/internal" -) - -// Harvester aggregates and reports metrics and spans. -type Harvester struct { - // These fields are not modified after Harvester creation. They may be - // safely accessed without locking. - config Config - commonAttributesJSON json.RawMessage - - // lock protects the mutable fields below. - lock sync.Mutex - lastHarvest time.Time - rawMetrics []Metric - spans []Span -} - -const ( - defaultTimeout = 5 * time.Second - defaultHarvestPeriod = 5 * time.Second - defaultMaxTries = 3 - defaultRetryBackoff = time.Second -) - -// NewHarvester creates a new harvester. -func NewHarvester(options ...func(*Config)) *Harvester { - cfg := Config{ - Client: &http.Client{ - Timeout: defaultTimeout, - }, - HarvestPeriod: defaultHarvestPeriod, - MaxTries: defaultMaxTries, - RetryBackoff: defaultRetryBackoff, - } - for _, opt := range options { - opt(&cfg) - } - - h := &Harvester{ - config: cfg, - lastHarvest: time.Now(), - } - - // Marshal the common attributes to JSON here to avoid doing it on every - // harvest. This also has the benefit that it avoids race conditions if - // the consumer modifies the CommonAttributes map after calling - // NewHarvester. - if nil != h.config.CommonAttributes { - attrs := vetAttributes(h.config.CommonAttributes, h.config.logError) - attributesJSON, err := json.Marshal(attrs) - if err != nil { - h.config.logError(map[string]interface{}{ - "err": err.Error(), - "message": "error marshaling common attributes", - }) - } else { - h.commonAttributesJSON = attributesJSON - } - h.config.CommonAttributes = nil - } - - spawnHarvester := h.needsHarvestThread() - - h.config.logDebug(map[string]interface{}{ - "event": "harvester created", - "api-key": h.config.APIKey, - "license-key": h.config.LicenseKey, - "harvest-period-seconds": h.config.HarvestPeriod.Seconds(), - "spawn-harvest-goroutine": spawnHarvester, - "metrics-url-override": h.config.MetricsURLOverride, - "spans-url-override": h.config.SpansURLOverride, - "collect-metrics": h.collectMetrics(), - "collect-spans": h.collectSpans(), - "version": internal.Version, - }) - - if spawnHarvester { - go h.harvest() - } - - return h -} - -func (h *Harvester) needsHarvestThread() bool { - if 0 == h.config.HarvestPeriod { - return false - } - if !h.collectMetrics() && !h.collectSpans() { - return false - } - return true -} - -func (h *Harvester) collectMetrics() bool { - if nil == h { - return false - } - if "" == h.config.APIKey { - return false - } - return true -} - -func (h *Harvester) collectSpans() bool { - if nil == h { - return false - } - if "" == h.config.LicenseKey { - return false - } - return true -} - -// RecordSpan records the given span. -func (h *Harvester) RecordSpan(s Span) { - if !h.collectSpans() { - return - } - - // Marshal attributes immediately to avoid holding a reference to a map - // that the consumer could change. - if nil != s.Attributes { - attributes := vetAttributes(s.Attributes, h.config.logError) - var err error - s.AttributesJSON, err = json.Marshal(attributes) - if err != nil { - h.config.logError(map[string]interface{}{ - "err": err.Error(), - "message": "error marshaling attributes", - "span": s.Name, - }) - } - } - s.Attributes = nil - - h.lock.Lock() - defer h.lock.Unlock() - - h.spans = append(h.spans, s) -} - -// RecordMetric adds a fully formed metric. This metric is not aggregated with -// any other metrics and is never dropped. The Timestamp field must be -// specified on Gauge metrics. The Timestamp/Interval fields on Count and -// Summary are optional and will be assumed to be the harvester batch times if -// unset. -func (h *Harvester) RecordMetric(m Metric) { - if !h.collectMetrics() { - return - } - h.lock.Lock() - defer h.lock.Unlock() - - h.rawMetrics = append(h.rawMetrics, m) -} - -type response struct { - statusCode int - body []byte - err error - retryAfter string -} - -func (r response) needsRetry(cfg *Config) (bool, time.Duration) { - switch r.statusCode { - case 202, 200: - // success - return false, 0 - case 400, 403, 404, 405, 411, 413: - // errors that should not retry - return false, 0 - case 429: - // special retry backoff time - if "" != r.retryAfter { - // Honor Retry-After header value in seconds - if d, err := time.ParseDuration(r.retryAfter + "s"); nil == err { - if d > cfg.RetryBackoff { - return true, d - } - } - } - return true, cfg.RetryBackoff - default: - // all other errors should retry - return true, cfg.RetryBackoff - } -} - -func postData(req *http.Request, client *http.Client) response { - resp, err := client.Do(req) - if nil != err { - return response{err: fmt.Errorf("error posting data: %v", err)} - } - defer resp.Body.Close() - - r := response{ - statusCode: resp.StatusCode, - retryAfter: resp.Header.Get("Retry-After"), - } - - // On success, metrics ingest returns 202, span ingest returns 200. - if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusAccepted { - r.body, _ = ioutil.ReadAll(resp.Body) - } else { - r.err = fmt.Errorf("unexpected post response code: %d: %s", - resp.StatusCode, http.StatusText(resp.StatusCode)) - } - - return r -} - -func (h *Harvester) swapOutMetrics(now time.Time) []Request { - if !h.collectMetrics() { - return nil - } - - h.lock.Lock() - lastHarvest := h.lastHarvest - h.lastHarvest = now - rawMetrics := h.rawMetrics - h.rawMetrics = nil - h.lock.Unlock() - - if 0 == len(rawMetrics) { - return nil - } - - batch := &MetricBatch{ - Timestamp: lastHarvest, - Interval: now.Sub(lastHarvest), - AttributesJSON: h.commonAttributesJSON, - Metrics: rawMetrics, - } - reqs, err := batch.NewRequests(h.config.APIKey, h.config.MetricsURLOverride) - if nil != err { - h.config.logError(map[string]interface{}{ - "err": err.Error(), - "message": "error creating requests for metrics", - }) - return nil - } - return reqs -} - -func (h *Harvester) swapOutSpans() []Request { - if !h.collectSpans() { - return nil - } - - h.lock.Lock() - sps := h.spans - h.spans = nil - h.lock.Unlock() - - if nil == sps { - return nil - } - batch := &SpanBatch{Spans: sps} - reqs, err := batch.NewRequests(h.config.LicenseKey, h.config.SpansURLOverride) - if nil != err { - h.config.logError(map[string]interface{}{ - "err": err.Error(), - "message": "error creating requests for spans", - }) - return nil - } - return reqs -} - -func harvestRequest(req Request, cfg *Config) { - var tries int - for { - cfg.logDebug(map[string]interface{}{ - "event": "data post", - "url": req.Request.URL.String(), - "data": jsonString(req.UncompressedBody), - }) - - tries++ - resp := postData(req.Request, cfg.Client) - - if nil != resp.err { - cfg.logError(map[string]interface{}{ - "err": resp.err.Error(), - }) - } else { - cfg.logDebug(map[string]interface{}{ - "event": "data post response", - "status": resp.statusCode, - "body": jsonOrString(resp.body), - }) - } - retry, backoff := resp.needsRetry(cfg) - if !retry { - return - } - if tries >= cfg.MaxTries { - cfg.logError(map[string]interface{}{ - "event": "data post retry limit reached", - "max-tries": cfg.MaxTries, - "message": "dropping data", - }) - return - } - time.Sleep(backoff) - } -} - -// HarvestNow synchronously harvests telemetry data -func (h *Harvester) HarvestNow() { - if nil != h && nil != h.config.BeforeHarvestFunc { - h.config.BeforeHarvestFunc(h) - } - for _, req := range h.swapOutMetrics(time.Now()) { - harvestRequest(req, &h.config) - } - for _, req := range h.swapOutSpans() { - harvestRequest(req, &h.config) - } -} - -// harvest concurrently harvests telemetry data -func (h *Harvester) harvest() { - ticker := time.NewTicker(h.config.HarvestPeriod) - for range ticker.C { - go h.HarvestNow() - } -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/metrics.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/metrics.go deleted file mode 100644 index 622ed789..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/metrics.go +++ /dev/null @@ -1,317 +0,0 @@ -package telemetry - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/newrelic/go-telemetry-sdk/internal" -) - -// Count is the metric type that counts the number of times an event occurred. -// This counter should be reset every time the data is reported, meaning the -// value reported represents the difference in count over the reporting time -// window. -// -// Example possible uses: -// -// * the number of messages put on a topic -// * the number of HTTP requests -// * the number of errors thrown -// * the number of support tickets answered -// -type Count struct { - // Name is the name of this metric. - Name string - // Attributes is a map of attributes for this metric. - Attributes map[string]interface{} - // AttributesJSON is a json.RawMessage of attributes for this metric. It - // will only be sent if Attributes is nil. - AttributesJSON json.RawMessage - // Value is the value of this metric. - Value float64 - // Timestamp is the start time of this metric's interval. Timestamp may - // be unset if it is set on the MetricBatch. - Timestamp time.Time - // Interval is the length of time for this metric. Interval may be - // unset if it is set on the MetricBatch. - Interval time.Duration -} - -// Metric is implemented by Count, Gauge, and Summary. -type Metric interface { - writeJSON(buf *bytes.Buffer) -} - -func writeTimestampInterval(w *internal.JSONFieldsWriter, timestamp time.Time, interval time.Duration) { - if !timestamp.IsZero() { - w.IntField("timestamp", timestamp.UnixNano()/(1000*1000)) - } - if interval != 0 { - w.IntField("interval.ms", interval.Nanoseconds()/(1000*1000)) - } -} - -func (m Count) writeJSON(buf *bytes.Buffer) { - w := internal.JSONFieldsWriter{Buf: buf} - w.Buf.WriteByte('{') - w.StringField("name", m.Name) - w.StringField("type", "count") - w.FloatField("value", m.Value) - writeTimestampInterval(&w, m.Timestamp, m.Interval) - if nil != m.Attributes { - w.WriterField("attributes", internal.Attributes(m.Attributes)) - } else if nil != m.AttributesJSON { - w.RawField("attributes", m.AttributesJSON) - } - w.Buf.WriteByte('}') -} - -// Summary is the metric type used for reporting aggregated information about -// discrete events. It provides the count, average, sum, min and max values -// over time. All fields should be reset to 0 every reporting interval. -// -// Example possible uses: -// -// * the duration and count of spans -// * the duration and count of transactions -// * the time each message spent in a queue -// -type Summary struct { - // Name is the name of this metric. - Name string - // Attributes is a map of attributes for this metric. - Attributes map[string]interface{} - // AttributesJSON is a json.RawMessage of attributes for this metric. It - // will only be sent if Attributes is nil. - AttributesJSON json.RawMessage - // Count is the count of occurrences of this metric for this time period. - Count float64 - // Sum is the sum of all occurrences of this metric for this time period. - Sum float64 - // Min is the smallest value recorded of this metric for this time period. - Min float64 - // Max is the largest value recorded of this metric for this time period. - Max float64 - // Timestamp is the start time of this metric's interval. Timestamp may - // be unset if it is set on the MetricBatch. - Timestamp time.Time - // Interval is the length of time for this metric. Interval may be - // unset if it is set on the MetricBatch. - Interval time.Duration -} - -func (m Summary) writeJSON(buf *bytes.Buffer) { - w := internal.JSONFieldsWriter{Buf: buf} - buf.WriteByte('{') - - w.StringField("name", m.Name) - w.StringField("type", "summary") - - w.AddKey("value") - buf.WriteByte('{') - vw := internal.JSONFieldsWriter{Buf: buf} - vw.FloatField("sum", m.Sum) - vw.FloatField("count", m.Count) - vw.FloatField("min", m.Min) - vw.FloatField("max", m.Max) - buf.WriteByte('}') - - writeTimestampInterval(&w, m.Timestamp, m.Interval) - if nil != m.Attributes { - w.WriterField("attributes", internal.Attributes(m.Attributes)) - } else if nil != m.AttributesJSON { - w.RawField("attributes", m.AttributesJSON) - } - buf.WriteByte('}') -} - -// Gauge is the metric type that records a value that can increase or decrease. -// It generally represents the value for something at a particular moment in -// time. One typically records a Gauge value on a set interval. -// -// Example possible uses: -// -// * the temperature in a room -// * the amount of memory currently in use for a process -// * the bytes per second flowing into Kafka at this exact moment in time -// * the current speed of your car -// -type Gauge struct { - // Name is the name of this metric. - Name string - // Attributes is a map of attributes for this metric. - Attributes map[string]interface{} - // AttributesJSON is a json.RawMessage of attributes for this metric. It - // will only be sent if Attributes is nil. - AttributesJSON json.RawMessage - // Value is the value of this metric. - Value float64 - // Timestamp is the time at which this metric was gathered. - Timestamp time.Time -} - -func (m Gauge) writeJSON(buf *bytes.Buffer) { - w := internal.JSONFieldsWriter{Buf: buf} - buf.WriteByte('{') - w.StringField("name", m.Name) - w.StringField("type", "gauge") - w.FloatField("value", m.Value) - writeTimestampInterval(&w, m.Timestamp, 0) - if nil != m.Attributes { - w.WriterField("attributes", internal.Attributes(m.Attributes)) - } else if nil != m.AttributesJSON { - w.RawField("attributes", m.AttributesJSON) - } - buf.WriteByte('}') -} - -// MetricBatch represents a single batch of metrics to report to New Relic. -// -// Timestamp/Interval are optional and can be used to represent the start and -// duration of the batch as a whole. Individual Count and Summary metrics may -// provide Timestamp/Interval fields which will take priority over the batch -// Timestamp/Interval. This is not the case for Gauge metrics which each require -// a Timestamp. -// -// Attributes are any attributes that should be applied to all metrics in this -// batch. Each metric type also accepts an Attributes field. -type MetricBatch struct { - // Timestamp is the start time of all metrics in this MetricBatch. This value - // can be overridden by setting Timestamp on any particular metric. - // Timestamp must be set here or on all metrics. - Timestamp time.Time - // Interval is the length of time for all metrics in this MetricBatch. This - // value can be overriden by setting Interval on any particular Count or - // Summary metric. Interval must be set to a non-zero value here or on - // all Count and Summary metrics. - Interval time.Duration - // Attributes is a map of attributes to apply to all metrics in this MetricBatch. - // They are included in addition to any attributes set on any particular - // metric. - Attributes map[string]interface{} - // AttributesJSON is a json.RawMessage of attributes to apply to all - // metrics in this MetricBatch. It will only be sent if the Attributes field on - // this MetricBatch is nil. These attributes are included in addition to any - // attributes on any particular metric. - AttributesJSON json.RawMessage - // Metrics is the slice of metrics to send with this MetricBatch. - Metrics []Metric -} - -// AddMetric adds a Count, Gauge, or Summary metric to a MetricBatch. -func (batch *MetricBatch) AddMetric(metric Metric) { - batch.Metrics = append(batch.Metrics, metric) -} - -type metricsArray []Metric - -func (ma metricsArray) WriteJSON(buf *bytes.Buffer) { - buf.WriteByte('[') - for idx, m := range ma { - if idx > 0 { - buf.WriteByte(',') - } - m.writeJSON(buf) - } - buf.WriteByte(']') -} - -type commonAttributes MetricBatch - -func (c commonAttributes) WriteJSON(buf *bytes.Buffer) { - buf.WriteByte('{') - w := internal.JSONFieldsWriter{Buf: buf} - writeTimestampInterval(&w, c.Timestamp, c.Interval) - if nil != c.Attributes { - w.WriterField("attributes", internal.Attributes(c.Attributes)) - } else if nil != c.AttributesJSON { - w.RawField("attributes", c.AttributesJSON) - } - buf.WriteByte('}') -} - -func (batch *MetricBatch) writeJSON(buf *bytes.Buffer) { - buf.WriteByte('[') - buf.WriteByte('{') - w := internal.JSONFieldsWriter{Buf: buf} - w.WriterField("common", commonAttributes(*batch)) - w.WriterField("metrics", metricsArray(batch.Metrics)) - buf.WriteByte('}') - buf.WriteByte(']') -} - -// split will split the MetricBatch into 2 equal parts, returning a slice of MetricBatches. -// If the number of metrics in the original is 0 or 1 then nil is returned. -func (batch *MetricBatch) split() []requestsBuilder { - if len(batch.Metrics) < 2 { - return nil - } - - half := len(batch.Metrics) / 2 - mb1 := *batch - mb1.Metrics = batch.Metrics[:half] - mb2 := *batch - mb2.Metrics = batch.Metrics[half:] - - return []requestsBuilder{ - requestsBuilder(&mb1), - requestsBuilder(&mb2), - } -} - -const ( - defaultURL = "https://metric-api.newrelic.com/metric/v1" -) - -// NewRequests creates new Requests from the MetricBatch. The requests can be -// sent with an http.Client. -// -// NewRequest returns the requests or an error if there was one. Each Request -// has an UncompressedBody field that is useful in debugging or testing. -// -// Possible response codes to be expected when sending the request to New -// Relic: -// -// 202 for success -// 403 for an auth failure -// 404 for a bad path -// 405 for anything but POST -// 411 if the Content-Length header is not included -// 413 for a payload that is too large -// 400 for a generally invalid request -// 429 Too Many Requests -// -func (batch *MetricBatch) NewRequests(apiKey string, urlOverride string) ([]Request, error) { - return newRequests(batch, apiKey, urlOverride, maxCompressedSizeBytes) -} - -func (batch *MetricBatch) newRequest(apiKey, urlOverride string) (Request, error) { - buf := &bytes.Buffer{} - batch.writeJSON(buf) - uncompressed := buf.Bytes() - - u := defaultURL - if "" != urlOverride { - u = urlOverride - } - compressed, err := internal.Compress(uncompressed) - if nil != err { - return Request{}, fmt.Errorf("error compressing metric data: %v", err) - } - req, err := http.NewRequest("POST", u, compressed) - if nil != err { - return Request{}, fmt.Errorf("error creating metric request: %v", err) - } - req.Header.Add("Content-Type", "application/json") - req.Header.Add("X-Insert-Key", apiKey) - req.Header.Add("Content-Encoding", "gzip") - internal.AddUserAgentHeader(req.Header) - return Request{ - Request: req, - UncompressedBody: uncompressed, - compressedBodyLength: compressed.Len(), - }, nil -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/request.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/request.go deleted file mode 100644 index b67b6d4e..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/request.go +++ /dev/null @@ -1,55 +0,0 @@ -package telemetry - -import ( - "encoding/json" - "fmt" - "net/http" -) - -const ( - maxCompressedSizeBytes = 1 << 20 -) - -// Request contains an http.Request and the UncompressedBody which is provided -// for logging. -type Request struct { - Request *http.Request - UncompressedBody json.RawMessage - - compressedBodyLength int -} - -type requestsBuilder interface { - newRequest(key, urlOverride string) (Request, error) - split() []requestsBuilder -} - -var ( - errUnableToSplit = fmt.Errorf("unable to split large payload further") -) - -func newRequests(batch requestsBuilder, key, urlOverride string, maxCompressedSize int) ([]Request, error) { - req, err := batch.newRequest(key, urlOverride) - if nil != err { - return nil, err - } - - if req.compressedBodyLength <= maxCompressedSize { - return []Request{req}, nil - } - - var reqs []Request - batches := batch.split() - if nil == batches { - return nil, errUnableToSplit - } - - for _, b := range batches { - rs, err := newRequests(b, key, urlOverride, maxCompressedSize) - if nil != err { - return nil, err - } - reqs = append(reqs, rs...) - } - return reqs, nil -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/spans.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/spans.go deleted file mode 100644 index a16556ac..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/spans.go +++ /dev/null @@ -1,158 +0,0 @@ -package telemetry - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/newrelic/go-telemetry-sdk/internal" -) - -// Span is a distributed tracing span. -type Span struct { - // GUID is a unique identifier for this span. - GUID string - // TraceID is a unique identifier shared by all spans within a single - // trace. - TraceID string - // Name is the name of this span. - Name string - // ParentID is the span id of the previous caller of this span. This - // can be empty if this is the first span. - ParentID string - // Timestamp is when this span started. - Timestamp time.Time - // Duration is the duration of this span. This field will be reported - // in milliseconds. - Duration time.Duration - // EntityName is the name of the service that created this span. - EntityName string - // Attributes is a map of user specified tags on this span. The map - // values can be any of bool, number, or string. - Attributes map[string]interface{} - // AttributesJSON is a json.RawMessage of attributes for this metric. It - // will only be sent if Attributes is nil. - AttributesJSON json.RawMessage -} - -func (s *Span) writeJSON(buf *bytes.Buffer) { - w := internal.JSONFieldsWriter{Buf: buf} - buf.WriteByte('{') - w.StringField("guid", s.GUID) - w.StringField("traceId", s.TraceID) - w.StringField("name", s.Name) - if "" != s.ParentID { - w.StringField("parentId", s.ParentID) - } - w.IntField("timestamp", s.Timestamp.UnixNano()/(1000*1000)) - w.FloatField("durationMs", s.Duration.Seconds()*1000.0) - w.StringField("entityName", s.EntityName) - - if nil != s.Attributes { - w.WriterField("tags", internal.Attributes(s.Attributes)) - } else if nil != s.AttributesJSON { - w.RawField("tags", s.AttributesJSON) - } - buf.WriteByte('}') -} - -// SpanBatch represents a single batch of spans to report to New Relic. -type SpanBatch struct { - Spans []Span -} - -// AddSpan appends a span to the SpanBatch. -func (batch *SpanBatch) AddSpan(s Span) { - batch.Spans = append(batch.Spans, s) -} - -// split will split the SpanBatch into 2 equally sized batches. -// If the number of spans in the original is 0 or 1 then nil is returned. -func (batch *SpanBatch) split() []requestsBuilder { - if len(batch.Spans) < 2 { - return nil - } - - half := len(batch.Spans) / 2 - b1 := *batch - b1.Spans = batch.Spans[:half] - b2 := *batch - b2.Spans = batch.Spans[half:] - - return []requestsBuilder{ - requestsBuilder(&b1), - requestsBuilder(&b2), - } -} - -func (batch *SpanBatch) writeJSON(buf *bytes.Buffer) { - buf.WriteByte('{') - buf.WriteString(`"spans":`) - buf.WriteByte('[') - for idx, s := range batch.Spans { - if idx > 0 { - buf.WriteByte(',') - } - s.writeJSON(buf) - } - buf.WriteByte(']') - buf.WriteByte('}') -} - -const ( - defaultSpanURL = "https://collector.newrelic.com/agent_listener/invoke_raw_method" -) - -func getSpansURL(licenseKey, urlOverride string) string { - u := defaultSpanURL - if "" != urlOverride { - u = urlOverride - } - return u + "?method=external_span_data&protocol_version=1&license_key=" + licenseKey -} - -// NewRequests creates new requests from the SpanBatch. The request can be -// sent with an http.Client. -// -// NewRequest returns requests or an error if there was one. Each Request -// has an UncompressedBody field that is useful in debugging or testing. -// -// Possible response codes to be expected when sending the request to New -// Relic: -// -// 202 for success -// 403 for an auth failure -// 404 for a bad path -// 405 for anything but POST -// 411 if the Content-Length header is not included -// 413 for a payload that is too large -// 400 for a generally invalid request -// 429 Too Many Requests -// -func (batch *SpanBatch) NewRequests(licenseKey, urlOverride string) ([]Request, error) { - return newRequests(batch, licenseKey, urlOverride, maxCompressedSizeBytes) -} - -func (batch *SpanBatch) newRequest(licenseKey, urlOverride string) (Request, error) { - buf := &bytes.Buffer{} - batch.writeJSON(buf) - uncompressed := buf.Bytes() - compressed, err := internal.Compress(uncompressed) - if nil != err { - return Request{}, fmt.Errorf("error compressing span data: %v", err) - } - req, err := http.NewRequest("POST", getSpansURL(licenseKey, urlOverride), compressed) - if nil != err { - return Request{}, fmt.Errorf("error creating span request: %v", err) - } - - req.Header.Add("Content-Encoding", "gzip") - internal.AddUserAgentHeader(req.Header) - return Request{ - Request: req, - UncompressedBody: uncompressed, - compressedBodyLength: compressed.Len(), - }, nil -} diff --git a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/utilities.go b/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/utilities.go deleted file mode 100644 index 5c371a0b..00000000 --- a/vendor/github.com/newrelic/go-telemetry-sdk/telemetry/utilities.go +++ /dev/null @@ -1,25 +0,0 @@ -package telemetry - -import "encoding/json" - -// jsonOrString returns its input as a jsonString if it is valid JSON, and as a -// string otherwise. -func jsonOrString(d []byte) interface{} { - var js json.RawMessage - if err := json.Unmarshal(d, &js); err == nil { - return jsonString(d) - } - return string(d) -} - -// jsonString assists in debug logging: The debug map could be marshalled as -// JSON or just printed directly. -type jsonString string - -// MarshalJSON returns the JSONString unmodified without any escaping. -func (js jsonString) MarshalJSON() ([]byte, error) { - if "" == js { - return []byte("null"), nil - } - return []byte(js), nil -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 0dc98cd4..fe1e7596 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -236,38 +236,6 @@ "revision": "94122c33edd36123c84d5368cfb2b69df93a0ec8", "revisionTime": "2018-07-18T01:23:57Z" }, - { - "checksumSHA1": "mKCHYPwX7mTxRBTlwlYBUn9QDDc=", - "path": "github.com/newrelic/go-telemetry-sdk/cumulative", - "revision": "42866549b5d0688e98b8f37ee071446c6aa0a782", - "revisionTime": "2019-06-19T20:43:02Z", - "version": "master", - "versionExact": "master" - }, - { - "checksumSHA1": "L3Z0y1K3kRs71Nsd1LHW7AOnJ7g=", - "path": "github.com/newrelic/go-telemetry-sdk/internal", - "revision": "42866549b5d0688e98b8f37ee071446c6aa0a782", - "revisionTime": "2019-06-19T20:43:02Z", - "version": "master", - "versionExact": "master" - }, - { - "checksumSHA1": "W1C+4VspdD1UgzXxFhdfg7z/3o8=", - "path": "github.com/newrelic/go-telemetry-sdk/internal/jsonx", - "revision": "42866549b5d0688e98b8f37ee071446c6aa0a782", - "revisionTime": "2019-06-19T20:43:02Z", - "version": "master", - "versionExact": "master" - }, - { - "checksumSHA1": "Ailk35r1fyKnnFtdhd/iNNq7qYY=", - "path": "github.com/newrelic/go-telemetry-sdk/telemetry", - "revision": "42866549b5d0688e98b8f37ee071446c6aa0a782", - "revisionTime": "2019-06-19T20:43:02Z", - "version": "master", - "versionExact": "master" - }, { "checksumSHA1": "ZfId5ZYn7jmL0xTxooHcxrLlXoA=", "path": "github.com/newrelic/infra-integrations-sdk/args", @@ -324,6 +292,30 @@ "version": "v3.3.1", "versionExact": "v3.3.1" }, + { + "checksumSHA1": "M+zjdsTy/wwN6ADwgULWcQvrSDw=", + "path": "github.com/newrelic/newrelic-telemetry-sdk-go/cumulative", + "revision": "790ff853d12b16f5be4d55e0c8f0f5c6d02f1808", + "revisionTime": "2020-01-16T22:44:29Z" + }, + { + "checksumSHA1": "jtO36ovM9ym+PzFaEefUx4ZDnqM=", + "path": "github.com/newrelic/newrelic-telemetry-sdk-go/internal", + "revision": "790ff853d12b16f5be4d55e0c8f0f5c6d02f1808", + "revisionTime": "2020-01-16T22:44:29Z" + }, + { + "checksumSHA1": "YCAebHsw0flhMo1vDFRNYpkeIrM=", + "path": "github.com/newrelic/newrelic-telemetry-sdk-go/internal/jsonx", + "revision": "790ff853d12b16f5be4d55e0c8f0f5c6d02f1808", + "revisionTime": "2020-01-16T22:44:29Z" + }, + { + "checksumSHA1": "LfELF431N1EWtkNLvWI5XfCrMTs=", + "path": "github.com/newrelic/newrelic-telemetry-sdk-go/telemetry", + "revision": "790ff853d12b16f5be4d55e0c8f0f5c6d02f1808", + "revisionTime": "2020-01-16T22:44:29Z" + }, { "checksumSHA1": "NzzMfGmmPoTGVwLZDkj3FXXDvCA=", "path": "github.com/pelletier/go-toml", @@ -468,6 +460,18 @@ "revision": "9e1dfc121bca96d392da5d00591953bdb54ab306", "revisionTime": "2018-06-26T19:55:58Z" }, + { + "checksumSHA1": "mCZlyemHWuVUdQpnAk7fOzOKLIA=", + "path": "github.com/stretchr/testify/assert", + "revision": "518a1491c71301fe87e8db87ee5a2aa07757c7ff", + "revisionTime": "2018-10-05T20:37:33Z" + }, + { + "checksumSHA1": "zeMUWi7jlOpBF2nw1IHTz+aV5vs=", + "path": "github.com/stretchr/testify/mock", + "revision": "518a1491c71301fe87e8db87ee5a2aa07757c7ff", + "revisionTime": "2018-10-05T20:37:33Z" + }, { "checksumSHA1": "O2YLSEYMatcGYVCIAny/iMi2aLc=", "path": "github.com/subosito/gotenv",