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
85
metric/deserialize.go
Normal file
85
metric/deserialize.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// storage for tracking data that can't be serialized to disk
|
||||
var (
|
||||
// grouped tracking metrics means that ID->Data association is not one to one,
|
||||
// many metrics could be associated with one tracking ID so we cannot just
|
||||
// clear this every time in FromBytes.
|
||||
trackingStore = make(map[telegraf.TrackingID]telegraf.TrackingData)
|
||||
mu = sync.Mutex{}
|
||||
|
||||
// ErrSkipTracking indicates that tracking information could not be found after
|
||||
// deserializing a metric from bytes. In this case we should skip the metric
|
||||
// and continue as if it does not exist.
|
||||
ErrSkipTracking = errors.New("metric tracking data not found")
|
||||
)
|
||||
|
||||
type serializedMetric struct {
|
||||
M telegraf.Metric
|
||||
TID telegraf.TrackingID
|
||||
}
|
||||
|
||||
func ToBytes(m telegraf.Metric) ([]byte, error) {
|
||||
var sm serializedMetric
|
||||
if um, ok := m.(telegraf.UnwrappableMetric); ok {
|
||||
sm.M = um.Unwrap()
|
||||
} else {
|
||||
sm.M = m
|
||||
}
|
||||
|
||||
if tm, ok := m.(telegraf.TrackingMetric); ok {
|
||||
sm.TID = tm.TrackingID()
|
||||
|
||||
mu.Lock()
|
||||
trackingStore[sm.TID] = tm.TrackingData()
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := gob.NewEncoder(&buf)
|
||||
if err := encoder.Encode(&sm); err != nil {
|
||||
return nil, fmt.Errorf("failed to encode metric to bytes: %w", err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func FromBytes(b []byte) (telegraf.Metric, error) {
|
||||
buf := bytes.NewBuffer(b)
|
||||
decoder := gob.NewDecoder(buf)
|
||||
|
||||
var sm *serializedMetric
|
||||
if err := decoder.Decode(&sm); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode metric from bytes: %w", err)
|
||||
}
|
||||
|
||||
m := sm.M
|
||||
if sm.TID != 0 {
|
||||
mu.Lock()
|
||||
td := trackingStore[sm.TID]
|
||||
if td == nil {
|
||||
mu.Unlock()
|
||||
return nil, ErrSkipTracking
|
||||
}
|
||||
rc := td.RefCount()
|
||||
if rc <= 1 {
|
||||
// only 1 metric left referencing this tracking ID, we can remove here since no subsequent metrics
|
||||
// read can use this ID. If another metric in a metric group with this ID gets added later, it will
|
||||
// simply be added back into the tracking store again.
|
||||
trackingStore[sm.TID] = nil
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
m = rebuildTrackingMetric(m, td)
|
||||
}
|
||||
return m, nil
|
||||
}
|
7
metric/init.go
Normal file
7
metric/init.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package metric
|
||||
|
||||
import "encoding/gob"
|
||||
|
||||
func Init() {
|
||||
gob.RegisterName("metric.metric", &metric{})
|
||||
}
|
387
metric/metric.go
Normal file
387
metric/metric.go
Normal file
|
@ -0,0 +1,387 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
type metric struct {
|
||||
MetricName string
|
||||
MetricTags []*telegraf.Tag
|
||||
MetricFields []*telegraf.Field
|
||||
MetricTime time.Time
|
||||
|
||||
MetricType telegraf.ValueType
|
||||
}
|
||||
|
||||
func New(
|
||||
name string,
|
||||
tags map[string]string,
|
||||
fields map[string]interface{},
|
||||
tm time.Time,
|
||||
tp ...telegraf.ValueType,
|
||||
) telegraf.Metric {
|
||||
var vtype telegraf.ValueType
|
||||
if len(tp) > 0 {
|
||||
vtype = tp[0]
|
||||
} else {
|
||||
vtype = telegraf.Untyped
|
||||
}
|
||||
|
||||
m := &metric{
|
||||
MetricName: name,
|
||||
MetricTags: nil,
|
||||
MetricFields: nil,
|
||||
MetricTime: tm,
|
||||
MetricType: vtype,
|
||||
}
|
||||
|
||||
if len(tags) > 0 {
|
||||
m.MetricTags = make([]*telegraf.Tag, 0, len(tags))
|
||||
for k, v := range tags {
|
||||
m.MetricTags = append(m.MetricTags,
|
||||
&telegraf.Tag{Key: k, Value: v})
|
||||
}
|
||||
sort.Slice(m.MetricTags, func(i, j int) bool { return m.MetricTags[i].Key < m.MetricTags[j].Key })
|
||||
}
|
||||
|
||||
if len(fields) > 0 {
|
||||
m.MetricFields = make([]*telegraf.Field, 0, len(fields))
|
||||
for k, v := range fields {
|
||||
v := convertField(v)
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
m.MetricFields = append(m.MetricFields, &telegraf.Field{Key: k, Value: v})
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// FromMetric returns a deep copy of the metric with any tracking information
|
||||
// removed.
|
||||
func FromMetric(other telegraf.Metric) telegraf.Metric {
|
||||
m := &metric{
|
||||
MetricName: other.Name(),
|
||||
MetricTags: make([]*telegraf.Tag, len(other.TagList())),
|
||||
MetricFields: make([]*telegraf.Field, len(other.FieldList())),
|
||||
MetricTime: other.Time(),
|
||||
MetricType: other.Type(),
|
||||
}
|
||||
|
||||
for i, tag := range other.TagList() {
|
||||
m.MetricTags[i] = &telegraf.Tag{Key: tag.Key, Value: tag.Value}
|
||||
}
|
||||
|
||||
for i, field := range other.FieldList() {
|
||||
m.MetricFields[i] = &telegraf.Field{Key: field.Key, Value: field.Value}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *metric) String() string {
|
||||
return fmt.Sprintf("%s %v %v %d", m.MetricName, m.Tags(), m.Fields(), m.MetricTime.UnixNano())
|
||||
}
|
||||
|
||||
func (m *metric) Name() string {
|
||||
return m.MetricName
|
||||
}
|
||||
|
||||
func (m *metric) Tags() map[string]string {
|
||||
tags := make(map[string]string, len(m.MetricTags))
|
||||
for _, tag := range m.MetricTags {
|
||||
tags[tag.Key] = tag.Value
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func (m *metric) TagList() []*telegraf.Tag {
|
||||
return m.MetricTags
|
||||
}
|
||||
|
||||
func (m *metric) Fields() map[string]interface{} {
|
||||
fields := make(map[string]interface{}, len(m.MetricFields))
|
||||
for _, field := range m.MetricFields {
|
||||
fields[field.Key] = field.Value
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (m *metric) FieldList() []*telegraf.Field {
|
||||
return m.MetricFields
|
||||
}
|
||||
|
||||
func (m *metric) Time() time.Time {
|
||||
return m.MetricTime
|
||||
}
|
||||
|
||||
func (m *metric) Type() telegraf.ValueType {
|
||||
return m.MetricType
|
||||
}
|
||||
|
||||
func (m *metric) SetName(name string) {
|
||||
m.MetricName = name
|
||||
}
|
||||
|
||||
func (m *metric) AddPrefix(prefix string) {
|
||||
m.MetricName = prefix + m.MetricName
|
||||
}
|
||||
|
||||
func (m *metric) AddSuffix(suffix string) {
|
||||
m.MetricName = m.MetricName + suffix
|
||||
}
|
||||
|
||||
func (m *metric) AddTag(key, value string) {
|
||||
for i, tag := range m.MetricTags {
|
||||
if key > tag.Key {
|
||||
continue
|
||||
}
|
||||
|
||||
if key == tag.Key {
|
||||
tag.Value = value
|
||||
return
|
||||
}
|
||||
|
||||
m.MetricTags = append(m.MetricTags, nil)
|
||||
copy(m.MetricTags[i+1:], m.MetricTags[i:])
|
||||
m.MetricTags[i] = &telegraf.Tag{Key: key, Value: value}
|
||||
return
|
||||
}
|
||||
|
||||
m.MetricTags = append(m.MetricTags, &telegraf.Tag{Key: key, Value: value})
|
||||
}
|
||||
|
||||
func (m *metric) HasTag(key string) bool {
|
||||
for _, tag := range m.MetricTags {
|
||||
if tag.Key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *metric) GetTag(key string) (string, bool) {
|
||||
for _, tag := range m.MetricTags {
|
||||
if tag.Key == key {
|
||||
return tag.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (m *metric) Tag(key string) string {
|
||||
v, _ := m.GetTag(key)
|
||||
return v
|
||||
}
|
||||
|
||||
func (m *metric) RemoveTag(key string) {
|
||||
for i, tag := range m.MetricTags {
|
||||
if tag.Key == key {
|
||||
copy(m.MetricTags[i:], m.MetricTags[i+1:])
|
||||
m.MetricTags[len(m.MetricTags)-1] = nil
|
||||
m.MetricTags = m.MetricTags[:len(m.MetricTags)-1]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metric) AddField(key string, value interface{}) {
|
||||
for i, field := range m.MetricFields {
|
||||
if key == field.Key {
|
||||
m.MetricFields[i] = &telegraf.Field{Key: key, Value: convertField(value)}
|
||||
return
|
||||
}
|
||||
}
|
||||
m.MetricFields = append(m.MetricFields, &telegraf.Field{Key: key, Value: convertField(value)})
|
||||
}
|
||||
|
||||
func (m *metric) HasField(key string) bool {
|
||||
for _, field := range m.MetricFields {
|
||||
if field.Key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *metric) GetField(key string) (interface{}, bool) {
|
||||
for _, field := range m.MetricFields {
|
||||
if field.Key == key {
|
||||
return field.Value, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *metric) Field(key string) interface{} {
|
||||
if v, found := m.GetField(key); found {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *metric) RemoveField(key string) {
|
||||
for i, field := range m.MetricFields {
|
||||
if field.Key == key {
|
||||
copy(m.MetricFields[i:], m.MetricFields[i+1:])
|
||||
m.MetricFields[len(m.MetricFields)-1] = nil
|
||||
m.MetricFields = m.MetricFields[:len(m.MetricFields)-1]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metric) SetTime(t time.Time) {
|
||||
m.MetricTime = t
|
||||
}
|
||||
|
||||
func (m *metric) SetType(t telegraf.ValueType) {
|
||||
m.MetricType = t
|
||||
}
|
||||
|
||||
func (m *metric) Copy() telegraf.Metric {
|
||||
m2 := &metric{
|
||||
MetricName: m.MetricName,
|
||||
MetricTags: make([]*telegraf.Tag, len(m.MetricTags)),
|
||||
MetricFields: make([]*telegraf.Field, len(m.MetricFields)),
|
||||
MetricTime: m.MetricTime,
|
||||
MetricType: m.MetricType,
|
||||
}
|
||||
|
||||
for i, tag := range m.MetricTags {
|
||||
m2.MetricTags[i] = &telegraf.Tag{Key: tag.Key, Value: tag.Value}
|
||||
}
|
||||
|
||||
for i, field := range m.MetricFields {
|
||||
m2.MetricFields[i] = &telegraf.Field{Key: field.Key, Value: field.Value}
|
||||
}
|
||||
return m2
|
||||
}
|
||||
|
||||
func (m *metric) HashID() uint64 {
|
||||
h := fnv.New64a()
|
||||
h.Write([]byte(m.MetricName))
|
||||
h.Write([]byte("\n"))
|
||||
for _, tag := range m.MetricTags {
|
||||
h.Write([]byte(tag.Key))
|
||||
h.Write([]byte("\n"))
|
||||
h.Write([]byte(tag.Value))
|
||||
h.Write([]byte("\n"))
|
||||
}
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
func (*metric) Accept() {
|
||||
}
|
||||
|
||||
func (*metric) Reject() {
|
||||
}
|
||||
|
||||
func (*metric) Drop() {
|
||||
}
|
||||
|
||||
// Convert field to a supported type or nil if inconvertible
|
||||
func convertField(v interface{}) interface{} {
|
||||
switch v := v.(type) {
|
||||
case float64:
|
||||
return v
|
||||
case int64:
|
||||
return v
|
||||
case string:
|
||||
return v
|
||||
case bool:
|
||||
return v
|
||||
case int:
|
||||
return int64(v)
|
||||
case uint:
|
||||
return uint64(v)
|
||||
case uint64:
|
||||
return v
|
||||
case []byte:
|
||||
return string(v)
|
||||
case int32:
|
||||
return int64(v)
|
||||
case int16:
|
||||
return int64(v)
|
||||
case int8:
|
||||
return int64(v)
|
||||
case uint32:
|
||||
return uint64(v)
|
||||
case uint16:
|
||||
return uint64(v)
|
||||
case uint8:
|
||||
return uint64(v)
|
||||
case float32:
|
||||
return float64(v)
|
||||
case *float64:
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
case *int64:
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
case *string:
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
case *bool:
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
case *int:
|
||||
if v != nil {
|
||||
return int64(*v)
|
||||
}
|
||||
case *uint:
|
||||
if v != nil {
|
||||
return uint64(*v)
|
||||
}
|
||||
case *uint64:
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
case *[]byte:
|
||||
if v != nil {
|
||||
return string(*v)
|
||||
}
|
||||
case *int32:
|
||||
if v != nil {
|
||||
return int64(*v)
|
||||
}
|
||||
case *int16:
|
||||
if v != nil {
|
||||
return int64(*v)
|
||||
}
|
||||
case *int8:
|
||||
if v != nil {
|
||||
return int64(*v)
|
||||
}
|
||||
case *uint32:
|
||||
if v != nil {
|
||||
return uint64(*v)
|
||||
}
|
||||
case *uint16:
|
||||
if v != nil {
|
||||
return uint64(*v)
|
||||
}
|
||||
case *uint8:
|
||||
if v != nil {
|
||||
return uint64(*v)
|
||||
}
|
||||
case *float32:
|
||||
if v != nil {
|
||||
return float64(*v)
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
328
metric/metric_test.go
Normal file
328
metric/metric_test.go
Normal file
|
@ -0,0 +1,328 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
func TestNewMetric(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
tags := map[string]string{
|
||||
"host": "localhost",
|
||||
"datacenter": "us-east-1",
|
||||
}
|
||||
fields := map[string]interface{}{
|
||||
"usage_idle": float64(99),
|
||||
"usage_busy": float64(1),
|
||||
}
|
||||
m := New("cpu", tags, fields, now)
|
||||
|
||||
require.Equal(t, "cpu", m.Name())
|
||||
require.Equal(t, tags, m.Tags())
|
||||
require.Equal(t, fields, m.Fields())
|
||||
require.Len(t, m.FieldList(), 2)
|
||||
require.Equal(t, now, m.Time())
|
||||
}
|
||||
|
||||
// cpu value=1
|
||||
func baseMetric() telegraf.Metric {
|
||||
tags := map[string]string{}
|
||||
fields := map[string]interface{}{
|
||||
"value": float64(1),
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
m := New("cpu", tags, fields, now)
|
||||
return m
|
||||
}
|
||||
|
||||
func TestHasTag(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
require.False(t, m.HasTag("host"))
|
||||
m.AddTag("host", "localhost")
|
||||
require.True(t, m.HasTag("host"))
|
||||
m.RemoveTag("host")
|
||||
require.False(t, m.HasTag("host"))
|
||||
}
|
||||
|
||||
func TestAddTagOverwrites(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
m.AddTag("host", "localhost")
|
||||
m.AddTag("host", "example.org")
|
||||
|
||||
value, ok := m.GetTag("host")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "example.org", value)
|
||||
require.Len(t, m.TagList(), 1)
|
||||
}
|
||||
|
||||
func TestRemoveTagNoEffectOnMissingTags(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
m.RemoveTag("foo")
|
||||
m.AddTag("a", "x")
|
||||
m.RemoveTag("foo")
|
||||
m.RemoveTag("bar")
|
||||
value, ok := m.GetTag("a")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "x", value)
|
||||
}
|
||||
|
||||
func TestGetTag(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
_, ok := m.GetTag("host")
|
||||
require.False(t, ok)
|
||||
|
||||
m.AddTag("host", "localhost")
|
||||
|
||||
value, ok := m.GetTag("host")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "localhost", value)
|
||||
|
||||
m.RemoveTag("host")
|
||||
_, ok = m.GetTag("host")
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestHasField(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
require.False(t, m.HasField("x"))
|
||||
m.AddField("x", 42.0)
|
||||
require.True(t, m.HasField("x"))
|
||||
m.RemoveTag("x")
|
||||
require.False(t, m.HasTag("x"))
|
||||
}
|
||||
|
||||
func TestAddFieldOverwrites(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
m.AddField("value", 1.0)
|
||||
m.AddField("value", 42.0)
|
||||
|
||||
require.Len(t, m.FieldList(), 1)
|
||||
|
||||
value, ok := m.GetField("value")
|
||||
require.True(t, ok)
|
||||
require.InDelta(t, 42.0, value, 0.001)
|
||||
}
|
||||
|
||||
func TestAddFieldChangesType(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
m.AddField("value", 1.0)
|
||||
m.AddField("value", "xyzzy")
|
||||
|
||||
require.Len(t, m.FieldList(), 1)
|
||||
|
||||
value, ok := m.GetField("value")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "xyzzy", value)
|
||||
}
|
||||
|
||||
func TestRemoveFieldNoEffectOnMissingFields(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
m.RemoveField("foo")
|
||||
m.AddField("a", "x")
|
||||
m.RemoveField("foo")
|
||||
m.RemoveField("bar")
|
||||
value, ok := m.GetField("a")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "x", value)
|
||||
}
|
||||
|
||||
func TestGetField(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
_, ok := m.GetField("foo")
|
||||
require.False(t, ok)
|
||||
|
||||
m.AddField("foo", "bar")
|
||||
|
||||
value, ok := m.GetField("foo")
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "bar", value)
|
||||
|
||||
m.RemoveTag("foo")
|
||||
_, ok = m.GetTag("foo")
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestTagList_Sorted(t *testing.T) {
|
||||
m := baseMetric()
|
||||
|
||||
m.AddTag("b", "y")
|
||||
m.AddTag("c", "z")
|
||||
m.AddTag("a", "x")
|
||||
|
||||
taglist := m.TagList()
|
||||
require.Equal(t, "a", taglist[0].Key)
|
||||
require.Equal(t, "b", taglist[1].Key)
|
||||
require.Equal(t, "c", taglist[2].Key)
|
||||
}
|
||||
|
||||
func TestEquals(t *testing.T) {
|
||||
now := time.Now()
|
||||
m1 := New("cpu",
|
||||
map[string]string{
|
||||
"host": "localhost",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": 42.0,
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
m2 := New("cpu",
|
||||
map[string]string{
|
||||
"host": "localhost",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": 42.0,
|
||||
},
|
||||
now,
|
||||
)
|
||||
|
||||
lhs := m1.(*metric)
|
||||
require.Equal(t, lhs, m2)
|
||||
|
||||
m3 := m2.Copy()
|
||||
require.Equal(t, lhs, m3)
|
||||
m3.AddTag("a", "x")
|
||||
require.NotEqual(t, lhs, m3)
|
||||
}
|
||||
|
||||
func TestHashID(t *testing.T) {
|
||||
m := New(
|
||||
"cpu",
|
||||
map[string]string{
|
||||
"datacenter": "us-east-1",
|
||||
"mytag": "foo",
|
||||
"another": "tag",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": float64(1),
|
||||
},
|
||||
time.Now(),
|
||||
)
|
||||
hash := m.HashID()
|
||||
|
||||
// adding a field doesn't change the hash:
|
||||
m.AddField("foo", int64(100))
|
||||
require.Equal(t, hash, m.HashID())
|
||||
|
||||
// removing a non-existent tag doesn't change the hash:
|
||||
m.RemoveTag("no-op")
|
||||
require.Equal(t, hash, m.HashID())
|
||||
|
||||
// adding a tag does change it:
|
||||
m.AddTag("foo", "bar")
|
||||
require.NotEqual(t, hash, m.HashID())
|
||||
hash = m.HashID()
|
||||
|
||||
// removing a tag also changes it:
|
||||
m.RemoveTag("mytag")
|
||||
require.NotEqual(t, hash, m.HashID())
|
||||
}
|
||||
|
||||
func TestHashID_Consistency(t *testing.T) {
|
||||
m := New(
|
||||
"cpu",
|
||||
map[string]string{
|
||||
"datacenter": "us-east-1",
|
||||
"mytag": "foo",
|
||||
"another": "tag",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": float64(1),
|
||||
},
|
||||
time.Now(),
|
||||
)
|
||||
hash := m.HashID()
|
||||
|
||||
m2 := New(
|
||||
"cpu",
|
||||
map[string]string{
|
||||
"datacenter": "us-east-1",
|
||||
"mytag": "foo",
|
||||
"another": "tag",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": float64(1),
|
||||
},
|
||||
time.Now(),
|
||||
)
|
||||
require.Equal(t, hash, m2.HashID())
|
||||
|
||||
m3 := m.Copy()
|
||||
require.Equal(t, m2.HashID(), m3.HashID())
|
||||
}
|
||||
|
||||
func TestHashID_Delimiting(t *testing.T) {
|
||||
m1 := New(
|
||||
"cpu",
|
||||
map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
"c": "z",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": float64(1),
|
||||
},
|
||||
time.Now(),
|
||||
)
|
||||
m2 := New(
|
||||
"cpu",
|
||||
map[string]string{
|
||||
"a": "xbycz",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"value": float64(1),
|
||||
},
|
||||
time.Now(),
|
||||
)
|
||||
require.NotEqual(t, m1.HashID(), m2.HashID())
|
||||
}
|
||||
|
||||
func TestSetName(t *testing.T) {
|
||||
m := baseMetric()
|
||||
m.SetName("foo")
|
||||
require.Equal(t, "foo", m.Name())
|
||||
}
|
||||
|
||||
func TestAddPrefix(t *testing.T) {
|
||||
m := baseMetric()
|
||||
m.AddPrefix("foo_")
|
||||
require.Equal(t, "foo_cpu", m.Name())
|
||||
m.AddPrefix("foo_")
|
||||
require.Equal(t, "foo_foo_cpu", m.Name())
|
||||
}
|
||||
|
||||
func TestAddSuffix(t *testing.T) {
|
||||
m := baseMetric()
|
||||
m.AddSuffix("_foo")
|
||||
require.Equal(t, "cpu_foo", m.Name())
|
||||
m.AddSuffix("_foo")
|
||||
require.Equal(t, "cpu_foo_foo", m.Name())
|
||||
}
|
||||
|
||||
func TestValueType(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
tags := map[string]string{}
|
||||
fields := map[string]interface{}{
|
||||
"value": float64(42),
|
||||
}
|
||||
m := New("cpu", tags, fields, now, telegraf.Gauge)
|
||||
|
||||
require.Equal(t, telegraf.Gauge, m.Type())
|
||||
}
|
106
metric/series_grouper.go
Normal file
106
metric/series_grouper.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash/maphash"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// NewSeriesGrouper returns a type that can be used to group fields by series
|
||||
// and time, so that fields which share these values will be combined into a
|
||||
// single telegraf.Metric.
|
||||
//
|
||||
// This is useful to build telegraf.Metric's when all fields for a series are
|
||||
// not available at once.
|
||||
//
|
||||
// ex:
|
||||
// - cpu,host=localhost usage_time=42
|
||||
// - cpu,host=localhost idle_time=42
|
||||
// + cpu,host=localhost idle_time=42,usage_time=42
|
||||
func NewSeriesGrouper() *SeriesGrouper {
|
||||
return &SeriesGrouper{
|
||||
metrics: make(map[uint64]telegraf.Metric),
|
||||
ordered: make([]telegraf.Metric, 0),
|
||||
hashSeed: maphash.MakeSeed(),
|
||||
}
|
||||
}
|
||||
|
||||
type SeriesGrouper struct {
|
||||
metrics map[uint64]telegraf.Metric
|
||||
ordered []telegraf.Metric
|
||||
|
||||
hashSeed maphash.Seed
|
||||
}
|
||||
|
||||
// Add adds a field key and value to the series.
|
||||
func (g *SeriesGrouper) Add(
|
||||
measurement string,
|
||||
tags map[string]string,
|
||||
tm time.Time,
|
||||
field string,
|
||||
fieldValue interface{},
|
||||
) {
|
||||
taglist := make([]*telegraf.Tag, 0, len(tags))
|
||||
for k, v := range tags {
|
||||
taglist = append(taglist,
|
||||
&telegraf.Tag{Key: k, Value: v})
|
||||
}
|
||||
sort.Slice(taglist, func(i, j int) bool { return taglist[i].Key < taglist[j].Key })
|
||||
|
||||
id := groupID(g.hashSeed, measurement, taglist, tm)
|
||||
m := g.metrics[id]
|
||||
if m == nil {
|
||||
m = New(measurement, tags, map[string]interface{}{field: fieldValue}, tm)
|
||||
g.metrics[id] = m
|
||||
g.ordered = append(g.ordered, m)
|
||||
} else {
|
||||
m.AddField(field, fieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
// AddMetric adds a metric to the series, merging with any previous matching metrics.
|
||||
func (g *SeriesGrouper) AddMetric(
|
||||
metric telegraf.Metric,
|
||||
) {
|
||||
id := groupID(g.hashSeed, metric.Name(), metric.TagList(), metric.Time())
|
||||
m := g.metrics[id]
|
||||
if m == nil {
|
||||
m = metric.Copy()
|
||||
g.metrics[id] = m
|
||||
g.ordered = append(g.ordered, m)
|
||||
} else {
|
||||
for _, f := range metric.FieldList() {
|
||||
m.AddField(f.Key, f.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics returns the metrics grouped by series and time.
|
||||
func (g *SeriesGrouper) Metrics() []telegraf.Metric {
|
||||
return g.ordered
|
||||
}
|
||||
|
||||
func groupID(seed maphash.Seed, measurement string, taglist []*telegraf.Tag, tm time.Time) uint64 {
|
||||
var mh maphash.Hash
|
||||
mh.SetSeed(seed)
|
||||
|
||||
mh.WriteString(measurement)
|
||||
mh.WriteByte(0)
|
||||
|
||||
for _, tag := range taglist {
|
||||
mh.WriteString(tag.Key)
|
||||
mh.WriteByte(0)
|
||||
mh.WriteString(tag.Value)
|
||||
mh.WriteByte(0)
|
||||
}
|
||||
mh.WriteByte(0)
|
||||
|
||||
var tsBuf [8]byte
|
||||
binary.BigEndian.PutUint64(tsBuf[:], uint64(tm.UnixNano()))
|
||||
mh.Write(tsBuf[:])
|
||||
|
||||
return mh.Sum64()
|
||||
}
|
37
metric/series_grouper_test.go
Normal file
37
metric/series_grouper_test.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"hash/maphash"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var m = New(
|
||||
"mymetric",
|
||||
map[string]string{
|
||||
"host": "host.example.com",
|
||||
"mykey": "myvalue",
|
||||
"another key": "another value",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"f1": 1,
|
||||
"f2": 2,
|
||||
"f3": 3,
|
||||
"f4": 4,
|
||||
"f5": 5,
|
||||
"f6": 6,
|
||||
"f7": 7,
|
||||
"f8": 8,
|
||||
},
|
||||
time.Now(),
|
||||
)
|
||||
|
||||
var result uint64
|
||||
|
||||
var hashSeed = maphash.MakeSeed()
|
||||
|
||||
func BenchmarkGroupID(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
result = groupID(hashSeed, m.Name(), m.TagList(), m.Time())
|
||||
}
|
||||
}
|
195
metric/tracking.go
Normal file
195
metric/tracking.go
Normal file
|
@ -0,0 +1,195 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// NotifyFunc is called when a tracking metric is done being processed with
|
||||
// the tracking information.
|
||||
type NotifyFunc = func(track telegraf.DeliveryInfo)
|
||||
|
||||
// WithTracking adds tracking to the metric and registers the notify function
|
||||
// to be called when processing is complete.
|
||||
func WithTracking(metric telegraf.Metric, fn NotifyFunc) (telegraf.Metric, telegraf.TrackingID) {
|
||||
return newTrackingMetric(metric, fn)
|
||||
}
|
||||
|
||||
// WithGroupTracking adds tracking to the metrics and registers the notify
|
||||
// function to be called when processing is complete.
|
||||
func WithGroupTracking(metric []telegraf.Metric, fn NotifyFunc) ([]telegraf.Metric, telegraf.TrackingID) {
|
||||
return newTrackingMetricGroup(metric, fn)
|
||||
}
|
||||
|
||||
var (
|
||||
lastID uint64
|
||||
finalizer func(*trackingData)
|
||||
)
|
||||
|
||||
func newTrackingID() telegraf.TrackingID {
|
||||
return telegraf.TrackingID(atomic.AddUint64(&lastID, 1))
|
||||
}
|
||||
|
||||
type trackingData struct {
|
||||
//nolint:revive // method is already named ID
|
||||
Id telegraf.TrackingID
|
||||
Rc int32
|
||||
AcceptCount int32
|
||||
RejectCount int32
|
||||
notifyFunc NotifyFunc
|
||||
}
|
||||
|
||||
func (d *trackingData) incr() {
|
||||
atomic.AddInt32(&d.Rc, 1)
|
||||
}
|
||||
|
||||
func (d *trackingData) RefCount() int32 {
|
||||
return d.Rc
|
||||
}
|
||||
|
||||
func (d *trackingData) decr() int32 {
|
||||
return atomic.AddInt32(&d.Rc, -1)
|
||||
}
|
||||
|
||||
func (d *trackingData) accept() {
|
||||
atomic.AddInt32(&d.AcceptCount, 1)
|
||||
}
|
||||
|
||||
func (d *trackingData) reject() {
|
||||
atomic.AddInt32(&d.RejectCount, 1)
|
||||
}
|
||||
|
||||
func (d *trackingData) notify() {
|
||||
d.notifyFunc(
|
||||
&deliveryInfo{
|
||||
id: d.Id,
|
||||
accepted: int(d.AcceptCount),
|
||||
rejected: int(d.RejectCount),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type trackingMetric struct {
|
||||
telegraf.Metric
|
||||
d *trackingData
|
||||
}
|
||||
|
||||
func newTrackingMetric(metric telegraf.Metric, fn NotifyFunc) (telegraf.Metric, telegraf.TrackingID) {
|
||||
m := &trackingMetric{
|
||||
Metric: metric,
|
||||
d: &trackingData{
|
||||
Id: newTrackingID(),
|
||||
Rc: 1,
|
||||
AcceptCount: 0,
|
||||
RejectCount: 0,
|
||||
notifyFunc: fn,
|
||||
},
|
||||
}
|
||||
|
||||
if finalizer != nil {
|
||||
runtime.SetFinalizer(m.d, finalizer)
|
||||
}
|
||||
return m, m.d.Id
|
||||
}
|
||||
|
||||
func rebuildTrackingMetric(metric telegraf.Metric, td telegraf.TrackingData) telegraf.Metric {
|
||||
return &trackingMetric{
|
||||
Metric: metric,
|
||||
d: td.(*trackingData),
|
||||
}
|
||||
}
|
||||
|
||||
func newTrackingMetricGroup(group []telegraf.Metric, fn NotifyFunc) ([]telegraf.Metric, telegraf.TrackingID) {
|
||||
d := &trackingData{
|
||||
Id: newTrackingID(),
|
||||
Rc: 0,
|
||||
AcceptCount: 0,
|
||||
RejectCount: 0,
|
||||
notifyFunc: fn,
|
||||
}
|
||||
|
||||
for i, m := range group {
|
||||
d.incr()
|
||||
dm := &trackingMetric{
|
||||
Metric: m,
|
||||
d: d,
|
||||
}
|
||||
group[i] = dm
|
||||
}
|
||||
if finalizer != nil {
|
||||
runtime.SetFinalizer(d, finalizer)
|
||||
}
|
||||
|
||||
if len(group) == 0 {
|
||||
d.notify()
|
||||
}
|
||||
|
||||
return group, d.Id
|
||||
}
|
||||
|
||||
func (m *trackingMetric) Copy() telegraf.Metric {
|
||||
m.d.incr()
|
||||
return &trackingMetric{
|
||||
Metric: m.Metric.Copy(),
|
||||
d: m.d,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *trackingMetric) Accept() {
|
||||
m.d.accept()
|
||||
m.decr()
|
||||
}
|
||||
|
||||
func (m *trackingMetric) Reject() {
|
||||
m.d.reject()
|
||||
m.decr()
|
||||
}
|
||||
|
||||
func (m *trackingMetric) Drop() {
|
||||
m.decr()
|
||||
}
|
||||
|
||||
func (m *trackingMetric) decr() {
|
||||
v := m.d.decr()
|
||||
if v < 0 {
|
||||
panic("negative refcount")
|
||||
}
|
||||
|
||||
if v == 0 {
|
||||
m.d.notify()
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap allows to access the underlying metric directly e.g. for go-templates
|
||||
func (m *trackingMetric) TrackingID() telegraf.TrackingID {
|
||||
return m.d.Id
|
||||
}
|
||||
|
||||
func (m *trackingMetric) TrackingData() telegraf.TrackingData {
|
||||
return m.d
|
||||
}
|
||||
|
||||
// Unwrap allows to access the underlying metric directly e.g. for go-templates
|
||||
func (m *trackingMetric) Unwrap() telegraf.Metric {
|
||||
return m.Metric
|
||||
}
|
||||
|
||||
type deliveryInfo struct {
|
||||
id telegraf.TrackingID
|
||||
accepted int
|
||||
rejected int
|
||||
}
|
||||
|
||||
func (r *deliveryInfo) ID() telegraf.TrackingID {
|
||||
return r.id
|
||||
}
|
||||
|
||||
func (r *deliveryInfo) Delivered() bool {
|
||||
return r.rejected == 0
|
||||
}
|
||||
|
||||
func (d *trackingData) ID() telegraf.TrackingID {
|
||||
return d.Id
|
||||
}
|
301
metric/tracking_test.go
Normal file
301
metric/tracking_test.go
Normal file
|
@ -0,0 +1,301 @@
|
|||
package metric
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
func mustMetric(
|
||||
name string,
|
||||
tags map[string]string,
|
||||
fields map[string]interface{},
|
||||
tm time.Time,
|
||||
tp ...telegraf.ValueType,
|
||||
) telegraf.Metric {
|
||||
m := New(name, tags, fields, tm, tp...)
|
||||
return m
|
||||
}
|
||||
|
||||
type deliveries struct {
|
||||
Info map[telegraf.TrackingID]telegraf.DeliveryInfo
|
||||
}
|
||||
|
||||
func (d *deliveries) onDelivery(info telegraf.DeliveryInfo) {
|
||||
d.Info[info.ID()] = info
|
||||
}
|
||||
|
||||
func TestNewTrackingID(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
var a [100000]telegraf.TrackingID
|
||||
var b [100000]telegraf.TrackingID
|
||||
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
for i := 0; i < len(a); i++ {
|
||||
a[i] = newTrackingID()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = newTrackingID()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
// Find any duplicate TrackingIDs in arrays a and b. Arrays must be sorted in increasing order.
|
||||
for i, j := 0, 0; i < len(a) && j < len(b); {
|
||||
if a[i] == b[j] {
|
||||
t.Errorf("Duplicate TrackingID: a[%d]==%d and b[%d]==%d.", i, a[i], j, b[j])
|
||||
break
|
||||
}
|
||||
if a[i] > b[j] {
|
||||
j++
|
||||
continue
|
||||
}
|
||||
if a[i] < b[j] {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTracking(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metric telegraf.Metric
|
||||
actions func(metric telegraf.Metric)
|
||||
delivered bool
|
||||
}{
|
||||
{
|
||||
name: "accept",
|
||||
metric: mustMetric(
|
||||
"memory",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
telegraf.Gauge,
|
||||
),
|
||||
actions: func(m telegraf.Metric) {
|
||||
m.Accept()
|
||||
},
|
||||
delivered: true,
|
||||
},
|
||||
{
|
||||
name: "reject",
|
||||
metric: mustMetric(
|
||||
"memory",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
telegraf.Gauge,
|
||||
),
|
||||
actions: func(m telegraf.Metric) {
|
||||
m.Reject()
|
||||
},
|
||||
delivered: false,
|
||||
},
|
||||
{
|
||||
name: "accept copy",
|
||||
metric: mustMetric(
|
||||
"memory",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
telegraf.Gauge,
|
||||
),
|
||||
actions: func(m telegraf.Metric) {
|
||||
m2 := m.Copy()
|
||||
m.Accept()
|
||||
m2.Accept()
|
||||
},
|
||||
delivered: true,
|
||||
},
|
||||
{
|
||||
name: "copy with accept and done",
|
||||
metric: mustMetric(
|
||||
"memory",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
telegraf.Gauge,
|
||||
),
|
||||
actions: func(m telegraf.Metric) {
|
||||
m2 := m.Copy()
|
||||
m.Accept()
|
||||
m2.Drop()
|
||||
},
|
||||
delivered: true,
|
||||
},
|
||||
{
|
||||
name: "copy with mixed delivery",
|
||||
metric: mustMetric(
|
||||
"memory",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
telegraf.Gauge,
|
||||
),
|
||||
actions: func(m telegraf.Metric) {
|
||||
m2 := m.Copy()
|
||||
m.Accept()
|
||||
m2.Reject()
|
||||
},
|
||||
delivered: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d := &deliveries{
|
||||
Info: make(map[telegraf.TrackingID]telegraf.DeliveryInfo),
|
||||
}
|
||||
metric, id := WithTracking(tt.metric, d.onDelivery)
|
||||
tt.actions(metric)
|
||||
|
||||
info := d.Info[id]
|
||||
require.Equal(t, tt.delivered, info.Delivered())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupTracking(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metrics []telegraf.Metric
|
||||
actions func(metrics []telegraf.Metric)
|
||||
delivered bool
|
||||
}{
|
||||
{
|
||||
name: "accept",
|
||||
metrics: []telegraf.Metric{
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
actions: func(metrics []telegraf.Metric) {
|
||||
metrics[0].Accept()
|
||||
metrics[1].Accept()
|
||||
},
|
||||
delivered: true,
|
||||
},
|
||||
{
|
||||
name: "reject",
|
||||
metrics: []telegraf.Metric{
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
actions: func(metrics []telegraf.Metric) {
|
||||
metrics[0].Reject()
|
||||
metrics[1].Reject()
|
||||
},
|
||||
delivered: false,
|
||||
},
|
||||
{
|
||||
name: "remove",
|
||||
metrics: []telegraf.Metric{
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
actions: func(metrics []telegraf.Metric) {
|
||||
metrics[0].Drop()
|
||||
metrics[1].Drop()
|
||||
},
|
||||
delivered: true,
|
||||
},
|
||||
{
|
||||
name: "mixed",
|
||||
metrics: []telegraf.Metric{
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
mustMetric(
|
||||
"cpu",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": 42,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
},
|
||||
actions: func(metrics []telegraf.Metric) {
|
||||
metrics[0].Accept()
|
||||
metrics[1].Reject()
|
||||
},
|
||||
delivered: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d := &deliveries{
|
||||
Info: make(map[telegraf.TrackingID]telegraf.DeliveryInfo),
|
||||
}
|
||||
metrics, id := WithGroupTracking(tt.metrics, d.onDelivery)
|
||||
tt.actions(metrics)
|
||||
|
||||
info := d.Info[id]
|
||||
require.Equal(t, tt.delivered, info.Delivered())
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue