2016-03-18 01:50:39 +08:00
|
|
|
package json
|
|
|
|
|
|
|
|
|
|
import (
|
2022-07-22 00:37:36 +08:00
|
|
|
"encoding/json"
|
2016-03-18 01:50:39 +08:00
|
|
|
"fmt"
|
2020-04-29 04:41:25 +08:00
|
|
|
"math"
|
2022-07-22 00:37:36 +08:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
2016-03-18 01:50:39 +08:00
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
"github.com/influxdata/toml"
|
2021-12-01 04:50:00 +08:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
2018-05-05 09:27:31 +08:00
|
|
|
"github.com/influxdata/telegraf"
|
2016-11-22 20:51:57 +08:00
|
|
|
"github.com/influxdata/telegraf/metric"
|
2022-07-22 00:37:36 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
2020-04-29 04:41:25 +08:00
|
|
|
"github.com/influxdata/telegraf/testutil"
|
2016-03-18 01:50:39 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestSerializeMetricFloat(t *testing.T) {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
tags := map[string]string{
|
|
|
|
|
"cpu": "cpu0",
|
|
|
|
|
}
|
|
|
|
|
fields := map[string]interface{}{
|
|
|
|
|
"usage_idle": float64(91.5),
|
|
|
|
|
}
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New("cpu", tags, fields, now)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
|
|
|
|
require.NoError(t, err)
|
2021-04-14 02:40:03 +08:00
|
|
|
buf, err := s.Serialize(m)
|
2021-12-01 04:50:00 +08:00
|
|
|
require.NoError(t, err)
|
2016-12-01 23:18:46 +08:00
|
|
|
expS := []byte(fmt.Sprintf(`{"fields":{"usage_idle":91.5},"name":"cpu","tags":{"cpu":"cpu0"},"timestamp":%d}`, now.Unix()) + "\n")
|
2021-12-01 04:50:00 +08:00
|
|
|
require.Equal(t, string(expS), string(buf))
|
2016-03-18 01:50:39 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-05 09:27:31 +08:00
|
|
|
func TestSerialize_TimestampUnits(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
2021-09-21 23:12:44 +08:00
|
|
|
name string
|
|
|
|
|
timestampUnits time.Duration
|
|
|
|
|
timestampFormat string
|
|
|
|
|
expected string
|
2018-05-05 09:27:31 +08:00
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "default of 1s",
|
|
|
|
|
timestampUnits: 0,
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":1525478795}`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "1ns",
|
|
|
|
|
timestampUnits: 1 * time.Nanosecond,
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":1525478795123456789}`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "1ms",
|
|
|
|
|
timestampUnits: 1 * time.Millisecond,
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":1525478795123}`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "10ms",
|
|
|
|
|
timestampUnits: 10 * time.Millisecond,
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":152547879512}`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "15ms is reduced to 10ms",
|
|
|
|
|
timestampUnits: 15 * time.Millisecond,
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":152547879512}`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "65ms is reduced to 10ms",
|
|
|
|
|
timestampUnits: 65 * time.Millisecond,
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":152547879512}`,
|
|
|
|
|
},
|
2021-09-21 23:12:44 +08:00
|
|
|
{
|
|
|
|
|
name: "timestamp format",
|
|
|
|
|
timestampFormat: "2006-01-02T15:04:05Z07:00",
|
|
|
|
|
expected: `{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":"2018-05-05T00:06:35Z"}`,
|
|
|
|
|
},
|
2018-05-05 09:27:31 +08:00
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New(
|
|
|
|
|
"cpu",
|
|
|
|
|
map[string]string{},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"value": 42.0,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(1525478795, 123456789),
|
2018-05-05 09:27:31 +08:00
|
|
|
)
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(tt.timestampUnits, tt.timestampFormat, "")
|
|
|
|
|
require.NoError(t, err)
|
2018-05-05 09:27:31 +08:00
|
|
|
actual, err := s.Serialize(m)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, tt.expected+"\n", string(actual))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-18 01:50:39 +08:00
|
|
|
func TestSerializeMetricInt(t *testing.T) {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
tags := map[string]string{
|
|
|
|
|
"cpu": "cpu0",
|
|
|
|
|
}
|
|
|
|
|
fields := map[string]interface{}{
|
|
|
|
|
"usage_idle": int64(90),
|
|
|
|
|
}
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New("cpu", tags, fields, now)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
|
|
|
|
require.NoError(t, err)
|
2021-04-14 02:40:03 +08:00
|
|
|
buf, err := s.Serialize(m)
|
2021-12-01 04:50:00 +08:00
|
|
|
require.NoError(t, err)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2016-12-01 23:18:46 +08:00
|
|
|
expS := []byte(fmt.Sprintf(`{"fields":{"usage_idle":90},"name":"cpu","tags":{"cpu":"cpu0"},"timestamp":%d}`, now.Unix()) + "\n")
|
2021-12-01 04:50:00 +08:00
|
|
|
require.Equal(t, string(expS), string(buf))
|
2016-03-18 01:50:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSerializeMetricString(t *testing.T) {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
tags := map[string]string{
|
|
|
|
|
"cpu": "cpu0",
|
|
|
|
|
}
|
|
|
|
|
fields := map[string]interface{}{
|
|
|
|
|
"usage_idle": "foobar",
|
|
|
|
|
}
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New("cpu", tags, fields, now)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
|
|
|
|
require.NoError(t, err)
|
2021-04-14 02:40:03 +08:00
|
|
|
buf, err := s.Serialize(m)
|
2021-12-01 04:50:00 +08:00
|
|
|
require.NoError(t, err)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2016-12-01 23:18:46 +08:00
|
|
|
expS := []byte(fmt.Sprintf(`{"fields":{"usage_idle":"foobar"},"name":"cpu","tags":{"cpu":"cpu0"},"timestamp":%d}`, now.Unix()) + "\n")
|
2021-12-01 04:50:00 +08:00
|
|
|
require.Equal(t, string(expS), string(buf))
|
2016-03-18 01:50:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSerializeMultiFields(t *testing.T) {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
tags := map[string]string{
|
|
|
|
|
"cpu": "cpu0",
|
|
|
|
|
}
|
|
|
|
|
fields := map[string]interface{}{
|
|
|
|
|
"usage_idle": int64(90),
|
|
|
|
|
"usage_total": 8559615,
|
|
|
|
|
}
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New("cpu", tags, fields, now)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
|
|
|
|
require.NoError(t, err)
|
2021-04-14 02:40:03 +08:00
|
|
|
buf, err := s.Serialize(m)
|
2021-12-01 04:50:00 +08:00
|
|
|
require.NoError(t, err)
|
2016-03-18 01:50:39 +08:00
|
|
|
|
2016-12-01 23:18:46 +08:00
|
|
|
expS := []byte(fmt.Sprintf(`{"fields":{"usage_idle":90,"usage_total":8559615},"name":"cpu","tags":{"cpu":"cpu0"},"timestamp":%d}`, now.Unix()) + "\n")
|
2021-12-01 04:50:00 +08:00
|
|
|
require.Equal(t, string(expS), string(buf))
|
2016-03-18 01:50:39 +08:00
|
|
|
}
|
2016-12-02 02:16:52 +08:00
|
|
|
|
|
|
|
|
func TestSerializeMetricWithEscapes(t *testing.T) {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
tags := map[string]string{
|
|
|
|
|
"cpu tag": "cpu0",
|
|
|
|
|
}
|
|
|
|
|
fields := map[string]interface{}{
|
|
|
|
|
"U,age=Idle": int64(90),
|
|
|
|
|
}
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New("My CPU", tags, fields, now)
|
2016-12-02 02:16:52 +08:00
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
|
|
|
|
require.NoError(t, err)
|
2016-12-01 23:18:46 +08:00
|
|
|
buf, err := s.Serialize(m)
|
2021-12-01 04:50:00 +08:00
|
|
|
require.NoError(t, err)
|
2016-12-02 02:16:52 +08:00
|
|
|
|
2016-12-01 23:18:46 +08:00
|
|
|
expS := []byte(fmt.Sprintf(`{"fields":{"U,age=Idle":90},"name":"My CPU","tags":{"cpu tag":"cpu0"},"timestamp":%d}`, now.Unix()) + "\n")
|
2021-12-01 04:50:00 +08:00
|
|
|
require.Equal(t, string(expS), string(buf))
|
2016-12-02 02:16:52 +08:00
|
|
|
}
|
2018-05-05 09:27:31 +08:00
|
|
|
|
|
|
|
|
func TestSerializeBatch(t *testing.T) {
|
2021-04-14 02:40:03 +08:00
|
|
|
m := metric.New(
|
|
|
|
|
"cpu",
|
|
|
|
|
map[string]string{},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"value": 42.0,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
2018-05-05 09:27:31 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
metrics := []telegraf.Metric{m, m}
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
|
|
|
|
require.NoError(t, err)
|
2018-05-05 09:27:31 +08:00
|
|
|
buf, err := s.SerializeBatch(metrics)
|
|
|
|
|
require.NoError(t, err)
|
2022-11-11 21:32:11 +08:00
|
|
|
require.Equal(
|
|
|
|
|
t,
|
|
|
|
|
[]byte(`{"metrics":[{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":0},{"fields":{"value":42},"name":"cpu","tags":{},"timestamp":0}]}`),
|
|
|
|
|
buf,
|
|
|
|
|
)
|
2018-05-05 09:27:31 +08:00
|
|
|
}
|
2020-04-29 04:41:25 +08:00
|
|
|
|
|
|
|
|
func TestSerializeBatchSkipInf(t *testing.T) {
|
|
|
|
|
metrics := []telegraf.Metric{
|
|
|
|
|
testutil.MustMetric(
|
|
|
|
|
"cpu",
|
|
|
|
|
map[string]string{},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"inf": math.Inf(1),
|
|
|
|
|
"time_idle": 42,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
2020-04-29 04:41:25 +08:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
buf, err := s.SerializeBatch(metrics)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, []byte(`{"metrics":[{"fields":{"time_idle":42},"name":"cpu","tags":{},"timestamp":0}]}`), buf)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSerializeBatchSkipInfAllFields(t *testing.T) {
|
|
|
|
|
metrics := []telegraf.Metric{
|
|
|
|
|
testutil.MustMetric(
|
|
|
|
|
"cpu",
|
|
|
|
|
map[string]string{},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"inf": math.Inf(1),
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-22 00:37:36 +08:00
|
|
|
s, err := NewSerializer(0, "", "")
|
2020-04-29 04:41:25 +08:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
buf, err := s.SerializeBatch(metrics)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, []byte(`{"metrics":[{"fields":{},"name":"cpu","tags":{},"timestamp":0}]}`), buf)
|
|
|
|
|
}
|
2022-07-22 00:37:36 +08:00
|
|
|
|
|
|
|
|
func TestSerializeTransformationNonBatch(t *testing.T) {
|
|
|
|
|
var tests = []struct {
|
|
|
|
|
name string
|
|
|
|
|
filename string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "non-batch transformation test",
|
|
|
|
|
filename: "testcases/transformation_single.conf",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
parser := &influx.Parser{}
|
|
|
|
|
require.NoError(t, parser.Init())
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
filename := filepath.FromSlash(tt.filename)
|
|
|
|
|
cfg, header, err := loadTestConfiguration(filename)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Get the input metrics
|
|
|
|
|
metrics, err := testutil.ParseMetricsFrom(header, "Input:", parser)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Get the expectations
|
|
|
|
|
expectedArray, err := loadJSON(strings.TrimSuffix(filename, ".conf") + "_out.json")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
expected := expectedArray.([]interface{})
|
|
|
|
|
|
|
|
|
|
// Serialize
|
|
|
|
|
serializer, err := NewSerializer(cfg.TimestampUnits, cfg.TimestampFormat, cfg.Transformation)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
for i, m := range metrics {
|
|
|
|
|
buf, err := serializer.Serialize(m)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Compare
|
|
|
|
|
var actual interface{}
|
|
|
|
|
require.NoError(t, json.Unmarshal(buf, &actual))
|
|
|
|
|
fmt.Printf("actual: %v\n", actual)
|
|
|
|
|
fmt.Printf("expected: %v\n", expected[i])
|
|
|
|
|
require.EqualValuesf(t, expected[i], actual, "mismatch in %d", i)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSerializeTransformationBatch(t *testing.T) {
|
|
|
|
|
var tests = []struct {
|
|
|
|
|
name string
|
|
|
|
|
filename string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "batch transformation test",
|
|
|
|
|
filename: "testcases/transformation_batch.conf",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
parser := &influx.Parser{}
|
|
|
|
|
require.NoError(t, parser.Init())
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
filename := filepath.FromSlash(tt.filename)
|
|
|
|
|
cfg, header, err := loadTestConfiguration(filename)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Get the input metrics
|
|
|
|
|
metrics, err := testutil.ParseMetricsFrom(header, "Input:", parser)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Get the expectations
|
|
|
|
|
expected, err := loadJSON(strings.TrimSuffix(filename, ".conf") + "_out.json")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Serialize
|
|
|
|
|
serializer, err := NewSerializer(cfg.TimestampUnits, cfg.TimestampFormat, cfg.Transformation)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
buf, err := serializer.SerializeBatch(metrics)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Compare
|
|
|
|
|
var actual interface{}
|
|
|
|
|
require.NoError(t, json.Unmarshal(buf, &actual))
|
|
|
|
|
require.EqualValues(t, expected, actual)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
|
TimestampUnits time.Duration `toml:"json_timestamp_units"`
|
|
|
|
|
TimestampFormat string `toml:"json_timestamp_format"`
|
|
|
|
|
Transformation string `toml:"json_transformation"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadTestConfiguration(filename string) (*Config, []string, error) {
|
|
|
|
|
buf, err := os.ReadFile(filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header := make([]string, 0)
|
|
|
|
|
for _, line := range strings.Split(string(buf), "\n") {
|
|
|
|
|
line = strings.TrimSpace(line)
|
|
|
|
|
if strings.HasPrefix(line, "#") {
|
|
|
|
|
header = append(header, line)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var cfg Config
|
|
|
|
|
err = toml.Unmarshal(buf, &cfg)
|
|
|
|
|
return &cfg, header, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadJSON(filename string) (interface{}, error) {
|
|
|
|
|
buf, err := os.ReadFile(filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var data interface{}
|
|
|
|
|
err = json.Unmarshal(buf, &data)
|
|
|
|
|
return data, err
|
|
|
|
|
}
|