Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grok: Implement support for ts-epochmilli #6476

Merged
merged 2 commits into from
Oct 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/inputs/logparser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Patterns that convert all captures to tags will result in points that can't be w
- ts-rfc3339nano ("2006-01-02T15:04:05.999999999Z07:00")
- ts-httpd ("02/Jan/2006:15:04:05 -0700")
- ts-epoch (seconds since unix epoch, may contain decimal)
- ts-epochmilli (milliseconds since unix epoch, may contain decimal)
- ts-epochnano (nanoseconds since unix epoch)
- ts-syslog ("Jan 02 15:04:05", parsed time is set to the current year)
- ts-"CUSTOM"
Expand Down
36 changes: 35 additions & 1 deletion plugins/inputs/logparser/logparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestGrokParseLogFiles(t *testing.T) {
Log: testutil.Logger{},
GrokConfig: GrokConfig{
MeasurementName: "logparser_grok",
Patterns: []string{"%{TEST_LOG_A}", "%{TEST_LOG_B}"},
Patterns: []string{"%{TEST_LOG_A}", "%{TEST_LOG_B}", "%{TEST_LOG_C}"},
CustomPatternFiles: []string{thisdir + "testdata/test-patterns"},
},
FromBeginning: true,
Expand Down Expand Up @@ -162,6 +162,40 @@ func TestGrokParseLogFilesOneBad(t *testing.T) {
})
}

func TestGrokParseLogFiles_TimestampInEpochMilli(t *testing.T) {
thisdir := getCurrentDir()

logparser := &LogParserPlugin{
Log: testutil.Logger{},
GrokConfig: GrokConfig{
MeasurementName: "logparser_grok",
Patterns: []string{"%{TEST_LOG_C}"},
CustomPatternFiles: []string{thisdir + "testdata/test-patterns"},
},
FromBeginning: true,
Files: []string{thisdir + "testdata/test_c.log"},
}

acc := testutil.Accumulator{}
acc.SetDebug(true)
assert.NoError(t, logparser.Start(&acc))
acc.Wait(1)

logparser.Stop()

acc.AssertContainsTaggedFields(t, "logparser_grok",
map[string]interface{}{
"clientip": "192.168.1.1",
"myfloat": float64(1.25),
"response_time": int64(5432),
"myint": int64(101),
},
map[string]string{
"response_code": "200",
"path": thisdir + "testdata/test_c.log",
})
}

func getCurrentDir() string {
_, filename, _, _ := runtime.Caller(1)
return strings.Replace(filename, "logparser_test.go", "", 1)
Expand Down
4 changes: 4 additions & 0 deletions plugins/inputs/logparser/testdata/test-patterns
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ TEST_LOG_B \[%{TEST_TIMESTAMP:timestamp:ts-"02/01/2006--15:04:05"}\] %{NUMBER:my

TEST_TIMESTAMP %{MONTHDAY}/%{MONTHNUM}/%{YEAR}--%{TIME}
TEST_LOG_BAD \[%{TEST_TIMESTAMP:timestamp:ts-"02/01/2006--15:04:05"}\] %{NUMBER:myfloat:float} %{WORD:mystring:int} %{WORD:dropme:drop} %{WORD:nomodifier}

# Test C log line:
# 1568723594631 1.25 200 192.168.1.1 5.432µs 101
TEST_LOG_C %{POSINT:timestamp:ts-epochmilli} %{NUMBER:myfloat:float} %{RESPONSE_CODE} %{IPORHOST:clientip} %{RESPONSE_TIME} %{NUMBER:myint:int}
1 change: 1 addition & 0 deletions plugins/inputs/logparser/testdata/test_c.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1568723594631 1.25 200 192.168.1.1 5.432µs 101
1 change: 1 addition & 0 deletions plugins/parsers/grok/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ You must capture at least one field per line.
- ts-httpd ("02/Jan/2006:15:04:05 -0700")
- ts-epoch (seconds since unix epoch, may contain decimal)
- ts-epochnano (nanoseconds since unix epoch)
- ts-epochmilli (milliseconds since unix epoch)
- ts-syslog ("Jan 02 15:04:05", parsed time is set to the current year)
- ts-"CUSTOM"

Expand Down
20 changes: 15 additions & 5 deletions plugins/parsers/grok/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ var timeLayouts = map[string]string{
"ts-rfc3339": "2006-01-02T15:04:05Z07:00",
"ts-rfc3339nano": "2006-01-02T15:04:05.999999999Z07:00",
"ts-httpd": "02/Jan/2006:15:04:05 -0700",
// These three are not exactly "layouts", but they are special cases that
// These four are not exactly "layouts", but they are special cases that
// will get handled in the ParseLine function.
"ts-epoch": "EPOCH",
"ts-epochnano": "EPOCH_NANO",
"ts-syslog": "SYSLOG_TIMESTAMP",
"ts": "GENERIC_TIMESTAMP", // try parsing all known timestamp layouts.
"ts-epoch": "EPOCH",
"ts-epochnano": "EPOCH_NANO",
"ts-epochmilli": "EPOCH_MILLI",
"ts-syslog": "SYSLOG_TIMESTAMP",
"ts": "GENERIC_TIMESTAMP", // try parsing all known timestamp layouts.
}

const (
Expand All @@ -45,6 +46,7 @@ const (
DURATION = "duration"
DROP = "drop"
EPOCH = "EPOCH"
EPOCH_MILLI = "EPOCH_MILLI"
EPOCH_NANO = "EPOCH_NANO"
SYSLOG_TIMESTAMP = "SYSLOG_TIMESTAMP"
GENERIC_TIMESTAMP = "GENERIC_TIMESTAMP"
Expand Down Expand Up @@ -297,6 +299,14 @@ func (p *Parser) ParseLine(line string) (telegraf.Metric, error) {
ts = ts.Add(time.Duration(nanosec) * time.Nanosecond)
}
timestamp = ts
case EPOCH_MILLI:
ms, err := strconv.ParseInt(v, 10, 64)
if err != nil {
log.Printf("E! Error parsing %s to int: %s", v, err)
} else {
timestamp = time.Unix(0, ms*int64(time.Millisecond))
fmt.Println(timestamp)
}
case EPOCH_NANO:
iv, err := strconv.ParseInt(v, 10, 64)
if err != nil {
Expand Down
22 changes: 22 additions & 0 deletions plugins/parsers/grok/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,28 @@ func TestParsePatternsWithoutCustom(t *testing.T) {
assert.Equal(t, time.Unix(0, 1466004605359052000), metricA.Time())
}

func TestParseEpochMilli(t *testing.T) {
p := &Parser{
Patterns: []string{"%{MYAPP}"},
CustomPatterns: `
MYAPP %{POSINT:ts:ts-epochmilli} response_time=%{POSINT:response_time:int} mymetric=%{NUMBER:metric:float}
`,
}
assert.NoError(t, p.Compile())

metricA, err := p.ParseLine(`1568540909963 response_time=20821 mymetric=10890.645`)
require.NotNil(t, metricA)
assert.NoError(t, err)
assert.Equal(t,
map[string]interface{}{
"response_time": int64(20821),
"metric": float64(10890.645),
},
metricA.Fields())
assert.Equal(t, map[string]string{}, metricA.Tags())
assert.Equal(t, time.Unix(0, 1568540909963000000), metricA.Time())
}

func TestParseEpochNano(t *testing.T) {
p := &Parser{
Patterns: []string{"%{MYAPP}"},
Expand Down