From 14d97e5416ef8c401fd11aafd0b44a26ad21f261 Mon Sep 17 00:00:00 2001
From: Daniel Nelson <daniel@wavesofdawn.com>
Date: Wed, 23 May 2018 16:37:14 -0700
Subject: [PATCH] Add special syslog timestamp parser that uses current year
 (#4190)

Previously it was impossible to parse syslog timestamps without the date
being reported as year 0, due to the year not being specified
---
 plugins/inputs/logparser/README.md         |  1 +
 plugins/inputs/logparser/grok/grok.go      | 17 +++++++++++++++++
 plugins/inputs/logparser/grok/grok_test.go | 12 ++++++++++++
 3 files changed, 30 insertions(+)

diff --git a/plugins/inputs/logparser/README.md b/plugins/inputs/logparser/README.md
index cfa9d02b..7a1df8bc 100644
--- a/plugins/inputs/logparser/README.md
+++ b/plugins/inputs/logparser/README.md
@@ -104,6 +104,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-syslog        ("Jan 02 15:04:05", parsed time is set to the current year)
   - ts-"CUSTOM"
 
 CUSTOM time layouts must be within quotes and be the representation of the
diff --git a/plugins/inputs/logparser/grok/grok.go b/plugins/inputs/logparser/grok/grok.go
index 47d4ecdf..57d8691c 100644
--- a/plugins/inputs/logparser/grok/grok.go
+++ b/plugins/inputs/logparser/grok/grok.go
@@ -32,6 +32,7 @@ var timeLayouts = map[string]string{
 	// 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.
 }
 
@@ -44,6 +45,7 @@ const (
 	DROP              = "drop"
 	EPOCH             = "EPOCH"
 	EPOCH_NANO        = "EPOCH_NANO"
+	SYSLOG_TIMESTAMP  = "SYSLOG_TIMESTAMP"
 	GENERIC_TIMESTAMP = "GENERIC_TIMESTAMP"
 )
 
@@ -112,6 +114,7 @@ type Parser struct {
 	// layouts.
 	foundTsLayouts []string
 
+	timeFunc func() time.Time
 	g        *grok.Grok
 	tsModder *tsModder
 }
@@ -174,6 +177,10 @@ func (p *Parser) Compile() error {
 		p.loc, _ = time.LoadLocation("UTC")
 	}
 
+	if p.timeFunc == nil {
+		p.timeFunc = time.Now
+	}
+
 	return p.compileCustomPatterns()
 }
 
@@ -285,6 +292,16 @@ func (p *Parser) ParseLine(line string) (telegraf.Metric, error) {
 			} else {
 				timestamp = time.Unix(0, iv)
 			}
+		case SYSLOG_TIMESTAMP:
+			ts, err := time.ParseInLocation("Jan 02 15:04:05", v, p.loc)
+			if err == nil {
+				if ts.Year() == 0 {
+					ts = ts.AddDate(timestamp.Year(), 0, 0)
+				}
+				timestamp = ts
+			} else {
+				log.Printf("E! Error parsing %s to time layout [%s]: %s", v, t, err)
+			}
 		case GENERIC_TIMESTAMP:
 			var foundTs bool
 			// first try timestamp layouts that we've already found
diff --git a/plugins/inputs/logparser/grok/grok_test.go b/plugins/inputs/logparser/grok/grok_test.go
index a585140f..1008ce94 100644
--- a/plugins/inputs/logparser/grok/grok_test.go
+++ b/plugins/inputs/logparser/grok/grok_test.go
@@ -970,3 +970,15 @@ func TestNewlineInPatterns(t *testing.T) {
 	require.NoError(t, err)
 	require.NotNil(t, m)
 }
+
+func TestSyslogTimestampParser(t *testing.T) {
+	p := &Parser{
+		Patterns: []string{`%{SYSLOGTIMESTAMP:timestamp:ts-syslog} value=%{NUMBER:value:int}`},
+		timeFunc: func() time.Time { return time.Date(2018, time.April, 1, 0, 0, 0, 0, nil) },
+	}
+	require.NoError(t, p.Compile())
+	m, err := p.ParseLine("Sep 25 09:01:55 value=42")
+	require.NoError(t, err)
+	require.NotNil(t, m)
+	require.Equal(t, 2018, m.Time().Year())
+}
-- 
GitLab