2016-04-23 05:47:26 +08:00
|
|
|
package tail
|
2016-04-27 00:43:41 +08:00
|
|
|
|
|
|
|
|
import (
|
2019-09-24 06:39:50 +08:00
|
|
|
"bytes"
|
2016-04-27 00:43:41 +08:00
|
|
|
"io/ioutil"
|
2019-09-24 06:39:50 +08:00
|
|
|
"log"
|
2016-04-27 00:43:41 +08:00
|
|
|
"os"
|
|
|
|
|
"testing"
|
2019-08-22 07:30:55 +08:00
|
|
|
"time"
|
2016-04-27 00:43:41 +08:00
|
|
|
|
2019-08-22 07:30:55 +08:00
|
|
|
"github.com/influxdata/telegraf"
|
2016-04-27 00:43:41 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers"
|
2019-08-22 07:30:55 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers/csv"
|
2020-07-08 03:43:32 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
2019-08-22 07:30:55 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers/json"
|
2016-04-27 00:43:41 +08:00
|
|
|
"github.com/influxdata/telegraf/testutil"
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
2020-07-08 03:43:32 +08:00
|
|
|
func TestTailBadLine(t *testing.T) {
|
2016-04-27 00:43:41 +08:00
|
|
|
tmpfile, err := ioutil.TempFile("", "")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer os.Remove(tmpfile.Name())
|
2020-03-28 06:40:08 +08:00
|
|
|
|
2020-07-08 03:43:32 +08:00
|
|
|
_, err = tmpfile.WriteString("cpu mytag= foo usage_idle= 100\n")
|
2020-03-28 06:40:08 +08:00
|
|
|
require.NoError(t, err)
|
2016-04-27 00:43:41 +08:00
|
|
|
|
2020-07-08 03:43:32 +08:00
|
|
|
// Write good metric so we can detect when processing is complete
|
|
|
|
|
_, err = tmpfile.WriteString("cpu usage_idle=100\n")
|
2016-04-27 00:43:41 +08:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
2020-07-11 01:59:06 +08:00
|
|
|
tmpfile.Close()
|
|
|
|
|
|
2020-07-11 04:58:38 +08:00
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
|
log.SetOutput(buf)
|
|
|
|
|
|
2016-04-27 00:43:41 +08:00
|
|
|
tt := NewTail()
|
2019-09-24 06:39:50 +08:00
|
|
|
tt.Log = testutil.Logger{}
|
2016-04-27 00:43:41 +08:00
|
|
|
tt.FromBeginning = true
|
|
|
|
|
tt.Files = []string{tmpfile.Name()}
|
2018-09-19 00:23:45 +08:00
|
|
|
tt.SetParserFunc(parsers.NewInfluxParser)
|
2020-03-28 06:40:08 +08:00
|
|
|
|
|
|
|
|
err = tt.Init()
|
|
|
|
|
require.NoError(t, err)
|
2016-04-27 00:43:41 +08:00
|
|
|
|
|
|
|
|
acc := testutil.Accumulator{}
|
|
|
|
|
require.NoError(t, tt.Start(&acc))
|
2019-09-24 06:39:50 +08:00
|
|
|
|
2018-03-09 05:03:48 +08:00
|
|
|
require.NoError(t, acc.GatherError(tt.Gather))
|
2016-04-27 00:43:41 +08:00
|
|
|
|
2020-07-08 03:43:32 +08:00
|
|
|
acc.Wait(1)
|
2016-04-27 00:43:41 +08:00
|
|
|
|
2020-07-03 06:20:47 +08:00
|
|
|
tt.Stop()
|
2019-09-24 06:39:50 +08:00
|
|
|
assert.Contains(t, buf.String(), "Malformed log line")
|
2016-04-27 00:43:41 +08:00
|
|
|
}
|
2017-06-17 04:16:48 +08:00
|
|
|
|
|
|
|
|
func TestTailDosLineendings(t *testing.T) {
|
|
|
|
|
tmpfile, err := ioutil.TempFile("", "")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer os.Remove(tmpfile.Name())
|
|
|
|
|
_, err = tmpfile.WriteString("cpu usage_idle=100\r\ncpu2 usage_idle=200\r\n")
|
|
|
|
|
require.NoError(t, err)
|
2020-07-11 01:59:06 +08:00
|
|
|
tmpfile.Close()
|
2017-06-17 04:16:48 +08:00
|
|
|
|
|
|
|
|
tt := NewTail()
|
2019-09-24 06:39:50 +08:00
|
|
|
tt.Log = testutil.Logger{}
|
2017-06-17 04:16:48 +08:00
|
|
|
tt.FromBeginning = true
|
|
|
|
|
tt.Files = []string{tmpfile.Name()}
|
2018-09-19 00:23:45 +08:00
|
|
|
tt.SetParserFunc(parsers.NewInfluxParser)
|
2020-03-28 06:40:08 +08:00
|
|
|
|
|
|
|
|
err = tt.Init()
|
|
|
|
|
require.NoError(t, err)
|
2017-06-17 04:16:48 +08:00
|
|
|
|
|
|
|
|
acc := testutil.Accumulator{}
|
|
|
|
|
require.NoError(t, tt.Start(&acc))
|
2020-03-28 06:40:08 +08:00
|
|
|
defer tt.Stop()
|
2017-06-17 04:16:48 +08:00
|
|
|
require.NoError(t, acc.GatherError(tt.Gather))
|
|
|
|
|
|
|
|
|
|
acc.Wait(2)
|
|
|
|
|
acc.AssertContainsFields(t, "cpu",
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_idle": float64(100),
|
|
|
|
|
})
|
|
|
|
|
acc.AssertContainsFields(t, "cpu2",
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_idle": float64(200),
|
|
|
|
|
})
|
|
|
|
|
}
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
// The csv parser should only parse the header line once per file.
|
|
|
|
|
func TestCSVHeadersParsedOnce(t *testing.T) {
|
|
|
|
|
tmpfile, err := ioutil.TempFile("", "")
|
|
|
|
|
require.NoError(t, err)
|
2020-07-11 01:59:06 +08:00
|
|
|
defer os.Remove(tmpfile.Name())
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
_, err = tmpfile.WriteString(`
|
|
|
|
|
measurement,time_idle
|
|
|
|
|
cpu,42
|
|
|
|
|
cpu,42
|
|
|
|
|
`)
|
|
|
|
|
require.NoError(t, err)
|
2020-07-11 01:59:06 +08:00
|
|
|
tmpfile.Close()
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
plugin := NewTail()
|
2019-09-24 06:39:50 +08:00
|
|
|
plugin.Log = testutil.Logger{}
|
2019-08-22 07:30:55 +08:00
|
|
|
plugin.FromBeginning = true
|
|
|
|
|
plugin.Files = []string{tmpfile.Name()}
|
|
|
|
|
plugin.SetParserFunc(func() (parsers.Parser, error) {
|
2020-07-08 03:43:32 +08:00
|
|
|
return csv.NewParser(&csv.Config{
|
2019-08-22 07:30:55 +08:00
|
|
|
MeasurementColumn: "measurement",
|
|
|
|
|
HeaderRowCount: 1,
|
|
|
|
|
TimeFunc: func() time.Time { return time.Unix(0, 0) },
|
2020-07-08 03:43:32 +08:00
|
|
|
})
|
2019-08-22 07:30:55 +08:00
|
|
|
})
|
2020-03-28 06:40:08 +08:00
|
|
|
|
|
|
|
|
err = plugin.Init()
|
|
|
|
|
require.NoError(t, err)
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
acc := testutil.Accumulator{}
|
|
|
|
|
err = plugin.Start(&acc)
|
|
|
|
|
require.NoError(t, err)
|
2020-03-28 06:40:08 +08:00
|
|
|
defer plugin.Stop()
|
2019-08-22 07:30:55 +08:00
|
|
|
err = plugin.Gather(&acc)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
acc.Wait(2)
|
|
|
|
|
plugin.Stop()
|
|
|
|
|
|
|
|
|
|
expected := []telegraf.Metric{
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"path": tmpfile.Name(),
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
2020-05-27 05:16:48 +08:00
|
|
|
"time_idle": 42,
|
2019-08-22 07:30:55 +08:00
|
|
|
},
|
|
|
|
|
time.Unix(0, 0)),
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"path": tmpfile.Name(),
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
2020-05-27 05:16:48 +08:00
|
|
|
"time_idle": 42,
|
2019-08-22 07:30:55 +08:00
|
|
|
},
|
|
|
|
|
time.Unix(0, 0)),
|
|
|
|
|
}
|
|
|
|
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure that the first line can produce multiple metrics (#6138)
|
|
|
|
|
func TestMultipleMetricsOnFirstLine(t *testing.T) {
|
|
|
|
|
tmpfile, err := ioutil.TempFile("", "")
|
|
|
|
|
require.NoError(t, err)
|
2020-07-11 01:59:06 +08:00
|
|
|
defer os.Remove(tmpfile.Name())
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
_, err = tmpfile.WriteString(`
|
|
|
|
|
[{"time_idle": 42}, {"time_idle": 42}]
|
|
|
|
|
`)
|
|
|
|
|
require.NoError(t, err)
|
2020-07-11 01:59:06 +08:00
|
|
|
tmpfile.Close()
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
plugin := NewTail()
|
2019-09-24 06:39:50 +08:00
|
|
|
plugin.Log = testutil.Logger{}
|
2019-08-22 07:30:55 +08:00
|
|
|
plugin.FromBeginning = true
|
|
|
|
|
plugin.Files = []string{tmpfile.Name()}
|
|
|
|
|
plugin.SetParserFunc(func() (parsers.Parser, error) {
|
|
|
|
|
return json.New(
|
|
|
|
|
&json.Config{
|
|
|
|
|
MetricName: "cpu",
|
|
|
|
|
})
|
|
|
|
|
})
|
2020-03-28 06:40:08 +08:00
|
|
|
|
|
|
|
|
err = plugin.Init()
|
|
|
|
|
require.NoError(t, err)
|
2019-08-22 07:30:55 +08:00
|
|
|
|
|
|
|
|
acc := testutil.Accumulator{}
|
|
|
|
|
err = plugin.Start(&acc)
|
|
|
|
|
require.NoError(t, err)
|
2020-03-28 06:40:08 +08:00
|
|
|
defer plugin.Stop()
|
2019-08-22 07:30:55 +08:00
|
|
|
err = plugin.Gather(&acc)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
acc.Wait(2)
|
|
|
|
|
plugin.Stop()
|
|
|
|
|
|
|
|
|
|
expected := []telegraf.Metric{
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"path": tmpfile.Name(),
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"time_idle": 42.0,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0)),
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"path": tmpfile.Name(),
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"time_idle": 42.0,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0)),
|
|
|
|
|
}
|
|
|
|
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(),
|
|
|
|
|
testutil.IgnoreTime())
|
|
|
|
|
}
|
2020-07-08 03:43:32 +08:00
|
|
|
|
|
|
|
|
func TestCharacterEncoding(t *testing.T) {
|
|
|
|
|
full := []telegraf.Metric{
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"cpu": "cpu0",
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_active": 11.9,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"cpu": "cpu1",
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_active": 26.0,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"cpu": "cpu2",
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_active": 14.0,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"cpu": "cpu3",
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_active": 20.4,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
testutil.MustMetric("cpu",
|
|
|
|
|
map[string]string{
|
|
|
|
|
"cpu": "cpu-total",
|
|
|
|
|
},
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_active": 18.4,
|
|
|
|
|
},
|
|
|
|
|
time.Unix(0, 0),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
plugin *Tail
|
|
|
|
|
offset int64
|
|
|
|
|
expected []telegraf.Metric
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "utf-8",
|
|
|
|
|
plugin: &Tail{
|
|
|
|
|
Files: []string{"testdata/cpu-utf-8.influx"},
|
|
|
|
|
FromBeginning: true,
|
|
|
|
|
MaxUndeliveredLines: 1000,
|
|
|
|
|
Log: testutil.Logger{},
|
|
|
|
|
CharacterEncoding: "utf-8",
|
|
|
|
|
},
|
|
|
|
|
expected: full,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "utf-8 seek",
|
|
|
|
|
plugin: &Tail{
|
|
|
|
|
Files: []string{"testdata/cpu-utf-8.influx"},
|
|
|
|
|
MaxUndeliveredLines: 1000,
|
|
|
|
|
Log: testutil.Logger{},
|
|
|
|
|
CharacterEncoding: "utf-8",
|
|
|
|
|
},
|
|
|
|
|
offset: 0x33,
|
|
|
|
|
expected: full[1:],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "utf-16le",
|
|
|
|
|
plugin: &Tail{
|
|
|
|
|
Files: []string{"testdata/cpu-utf-16le.influx"},
|
|
|
|
|
FromBeginning: true,
|
|
|
|
|
MaxUndeliveredLines: 1000,
|
|
|
|
|
Log: testutil.Logger{},
|
|
|
|
|
CharacterEncoding: "utf-16le",
|
|
|
|
|
},
|
|
|
|
|
expected: full,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "utf-16le seek",
|
|
|
|
|
plugin: &Tail{
|
|
|
|
|
Files: []string{"testdata/cpu-utf-16le.influx"},
|
|
|
|
|
MaxUndeliveredLines: 1000,
|
|
|
|
|
Log: testutil.Logger{},
|
|
|
|
|
CharacterEncoding: "utf-16le",
|
|
|
|
|
},
|
|
|
|
|
offset: 0x68,
|
|
|
|
|
expected: full[1:],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "utf-16be",
|
|
|
|
|
plugin: &Tail{
|
|
|
|
|
Files: []string{"testdata/cpu-utf-16be.influx"},
|
|
|
|
|
FromBeginning: true,
|
|
|
|
|
MaxUndeliveredLines: 1000,
|
|
|
|
|
Log: testutil.Logger{},
|
|
|
|
|
CharacterEncoding: "utf-16be",
|
|
|
|
|
},
|
|
|
|
|
expected: full,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
tt.plugin.SetParserFunc(func() (parsers.Parser, error) {
|
|
|
|
|
handler := influx.NewMetricHandler()
|
|
|
|
|
return influx.NewParser(handler), nil
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if tt.offset != 0 {
|
|
|
|
|
tt.plugin.offsets = map[string]int64{
|
|
|
|
|
tt.plugin.Files[0]: tt.offset,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := tt.plugin.Init()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
var acc testutil.Accumulator
|
|
|
|
|
err = tt.plugin.Start(&acc)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
acc.Wait(len(tt.expected))
|
|
|
|
|
tt.plugin.Stop()
|
|
|
|
|
|
|
|
|
|
actual := acc.GetTelegrafMetrics()
|
|
|
|
|
for _, m := range actual {
|
|
|
|
|
m.RemoveTag("path")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testutil.RequireMetricsEqual(t, tt.expected, actual, testutil.IgnoreTime())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-01 03:31:02 +08:00
|
|
|
|
|
|
|
|
func TestTailEOF(t *testing.T) {
|
|
|
|
|
tmpfile, err := ioutil.TempFile("", "")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer os.Remove(tmpfile.Name())
|
|
|
|
|
_, err = tmpfile.WriteString("cpu usage_idle=100\r\n")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
err = tmpfile.Sync()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
tt := NewTail()
|
|
|
|
|
tt.Log = testutil.Logger{}
|
|
|
|
|
tt.FromBeginning = true
|
|
|
|
|
tt.Files = []string{tmpfile.Name()}
|
|
|
|
|
tt.SetParserFunc(parsers.NewInfluxParser)
|
|
|
|
|
|
|
|
|
|
err = tt.Init()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
acc := testutil.Accumulator{}
|
|
|
|
|
require.NoError(t, tt.Start(&acc))
|
|
|
|
|
defer tt.Stop()
|
|
|
|
|
require.NoError(t, acc.GatherError(tt.Gather))
|
|
|
|
|
acc.Wait(1) // input hits eof
|
|
|
|
|
|
|
|
|
|
_, err = tmpfile.WriteString("cpu2 usage_idle=200\r\n")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
err = tmpfile.Sync()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
acc.Wait(2)
|
|
|
|
|
require.NoError(t, acc.GatherError(tt.Gather))
|
|
|
|
|
acc.AssertContainsFields(t, "cpu",
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_idle": float64(100),
|
|
|
|
|
})
|
|
|
|
|
acc.AssertContainsFields(t, "cpu2",
|
|
|
|
|
map[string]interface{}{
|
|
|
|
|
"usage_idle": float64(200),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
err = tmpfile.Close()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|