diff --git a/plugins/inputs/tail/README.md b/plugins/inputs/tail/README.md index 3b1c50665..9ae120e91 100644 --- a/plugins/inputs/tail/README.md +++ b/plugins/inputs/tail/README.md @@ -5,14 +5,14 @@ The tail plugin "tails" a logfile and parses each log message. By default, the tail plugin acts like the following unix tail command: ``` -tail --follow=name --lines=0 --retry myfile.log +tail -F --lines=0 myfile.log ``` -- `--follow=name` means that it will follow the _name_ of the given file, so -that it will be compatible with log-rotated files. +- `-F` means that it will follow the _name_ of the given file, so +that it will be compatible with log-rotated files, and that it will retry on +inaccessible files. - `--lines=0` means that it will start at the end of the file (unless the `from_beginning` option is set). -- `--retry` means it will retry on inaccessible files. see http://man7.org/linux/man-pages/man1/tail.1.html for more details. @@ -24,6 +24,23 @@ The plugin expects messages in one of the ```toml # Stream a log file, like the tail -f command [[inputs.tail]] - # SampleConfig + ## files to tail. + ## These accept standard unix glob matching rules, but with the addition of + ## ** as a "super asterisk". ie: + ## "/var/log/**.log" -> recursively find all .log files in /var/log + ## "/var/log/*/*.log" -> find all .log files with a parent dir in /var/log + ## "/var/log/apache.log" -> just tail the apache log file + ## + ## See https://github.com/gobwas/glob for more examples + ## + files = ["/var/mymetrics.out"] + ## Read file from beginning. + from_beginning = false + + ## Data format to consume. + ## Each data format has it's own unique set of configuration options, read + ## more about them here: + ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md + data_format = "influx" ``` diff --git a/plugins/inputs/tail/tail.go b/plugins/inputs/tail/tail.go index cb99eff61..7cfca81e2 100644 --- a/plugins/inputs/tail/tail.go +++ b/plugins/inputs/tail/tail.go @@ -25,6 +25,12 @@ type Tail struct { sync.Mutex } +func NewTail() *Tail { + return &Tail{ + FromBeginning: false, + } +} + const sampleConfig = ` ## files to tail. ## These accept standard unix glob matching rules, but with the addition of @@ -145,8 +151,6 @@ func (t *Tail) SetParser(parser parsers.Parser) { func init() { inputs.Add("tail", func() telegraf.Input { - return &Tail{ - FromBeginning: false, - } + return NewTail() }) } diff --git a/plugins/inputs/tail/tail_test.go b/plugins/inputs/tail/tail_test.go index 78e2dd578..5d7c04a88 100644 --- a/plugins/inputs/tail/tail_test.go +++ b/plugins/inputs/tail/tail_test.go @@ -1 +1,101 @@ package tail + +import ( + "io/ioutil" + "os" + "testing" + "time" + + "github.com/influxdata/telegraf/plugins/parsers" + "github.com/influxdata/telegraf/testutil" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTailFromBeginning(t *testing.T) { + tmpfile, err := ioutil.TempFile("", "") + require.NoError(t, err) + defer os.Remove(tmpfile.Name()) + + tt := NewTail() + tt.FromBeginning = true + tt.Files = []string{tmpfile.Name()} + p, _ := parsers.NewInfluxParser() + tt.SetParser(p) + defer tt.Stop() + defer tmpfile.Close() + + acc := testutil.Accumulator{} + require.NoError(t, tt.Start(&acc)) + + _, err = tmpfile.WriteString("cpu,mytag=foo usage_idle=100\n") + require.NoError(t, err) + require.NoError(t, tt.Gather(&acc)) + time.Sleep(time.Millisecond * 50) + + acc.AssertContainsTaggedFields(t, "cpu", + map[string]interface{}{ + "usage_idle": float64(100), + }, + map[string]string{ + "mytag": "foo", + }) +} + +func TestTailFromEnd(t *testing.T) { + tmpfile, err := ioutil.TempFile("", "") + require.NoError(t, err) + defer os.Remove(tmpfile.Name()) + _, err = tmpfile.WriteString("cpu,mytag=foo usage_idle=100\n") + require.NoError(t, err) + + tt := NewTail() + tt.Files = []string{tmpfile.Name()} + p, _ := parsers.NewInfluxParser() + tt.SetParser(p) + defer tt.Stop() + defer tmpfile.Close() + + acc := testutil.Accumulator{} + require.NoError(t, tt.Start(&acc)) + time.Sleep(time.Millisecond * 100) + + _, err = tmpfile.WriteString("cpu,othertag=foo usage_idle=100\n") + require.NoError(t, err) + require.NoError(t, tt.Gather(&acc)) + time.Sleep(time.Millisecond * 50) + + acc.AssertContainsTaggedFields(t, "cpu", + map[string]interface{}{ + "usage_idle": float64(100), + }, + map[string]string{ + "othertag": "foo", + }) + assert.Len(t, acc.Metrics, 1) +} + +func TestTailBadLine(t *testing.T) { + tmpfile, err := ioutil.TempFile("", "") + require.NoError(t, err) + defer os.Remove(tmpfile.Name()) + + tt := NewTail() + tt.FromBeginning = true + tt.Files = []string{tmpfile.Name()} + p, _ := parsers.NewInfluxParser() + tt.SetParser(p) + defer tt.Stop() + defer tmpfile.Close() + + acc := testutil.Accumulator{} + require.NoError(t, tt.Start(&acc)) + + _, err = tmpfile.WriteString("cpu mytag= foo usage_idle= 100\n") + require.NoError(t, err) + require.NoError(t, tt.Gather(&acc)) + time.Sleep(time.Millisecond * 50) + + assert.Len(t, acc.Metrics, 0) +}