diff --git a/format/automatic.go b/format/automatic.go index ae1684e..2400db8 100644 --- a/format/automatic.go +++ b/format/automatic.go @@ -3,7 +3,6 @@ package format import ( "bufio" "bytes" - "errors" "strconv" "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164" @@ -31,33 +30,40 @@ const ( detectedRFC6587 = iota ) -func detect(data []byte) (detected int, err error) { +/* + * Will always fallback to rfc3164 (see section 4.3.3) + */ +func detect(data []byte) int { // all formats have a sapce somewhere if i := bytes.IndexByte(data, ' '); i > 0 { pLength := data[0:i] if _, err := strconv.Atoi(string(pLength)); err == nil { - return detectedRFC6587, nil + return detectedRFC6587 + } + // are we starting with < + if data[0] != '<' { + return detectedRFC3164 } - // is there a close angle bracket before the ' '? there should be angle := bytes.IndexByte(data, '>') if (angle < 0) || (angle >= i) { - return detectedUnknown, errors.New("No close angle bracket before space") + return detectedRFC3164 } // if a single digit immediately follows the angle bracket, then a space // it is RFC5424, as RFC3164 must begin with a letter (month name) if (angle+2 == i) && (data[angle+1] >= '0') && (data[angle+1] <= '9') { - return detectedRFC5424, nil + return detectedRFC5424 } else { - return detectedRFC3164, nil + return detectedRFC3164 } } - return detectedUnknown, nil + // fallback to rfc 3164 section 4.3.3 + return detectedRFC3164 } func (f *Automatic) GetParser(line []byte) LogParser { - switch format, _ := detect(line); format { + switch format := detect(line); format { case detectedRFC3164: return &parserWrapper{rfc3164.NewParser(line)} case detectedRFC5424: @@ -82,7 +88,7 @@ func (f *Automatic) automaticScannerSplit(data []byte, atEOF bool) (advance int, return 0, nil, nil } - switch format, err := detect(data); format { + switch format := detect(data); format { case detectedRFC6587: return rfc6587ScannerSplit(data, atEOF) case detectedRFC3164, detectedRFC5424: diff --git a/internal/syslogparser/rfc3164/rfc3164.go b/internal/syslogparser/rfc3164/rfc3164.go index d5873aa..c875089 100644 --- a/internal/syslogparser/rfc3164/rfc3164.go +++ b/internal/syslogparser/rfc3164/rfc3164.go @@ -43,12 +43,22 @@ func (p *Parser) Location(location *time.Location) { } func (p *Parser) Parse() error { + tcursor := p.cursor pri, err := p.parsePriority() if err != nil { - return err + // RFC3164 sec 4.3.3 + p.priority = syslogparser.Priority{13, syslogparser.Facility{Value: 1}, syslogparser.Severity{Value: 5}} + p.cursor = tcursor + content, err := p.parseContent() + p.header.timestamp = time.Now().Round(time.Second) + if err != syslogparser.ErrEOL { + return err + } + p.message = rfc3164message{content: content} + return nil } - tcursor := p.cursor + tcursor = p.cursor hdr, err := p.parseHeader() if err == syslogparser.ErrTimestampUnknownFormat { // RFC3164 sec 4.3.2. diff --git a/internal/syslogparser/rfc3164/rfc3164_test.go b/internal/syslogparser/rfc3164/rfc3164_test.go index b95eb72..91a245e 100644 --- a/internal/syslogparser/rfc3164/rfc3164_test.go +++ b/internal/syslogparser/rfc3164/rfc3164_test.go @@ -107,6 +107,10 @@ func (s *Rfc3164TestSuite) TestParser_NoTimestamp(c *C) { now := time.Now() obtained := p.Dump() + + obtainedTime := obtained["timestamp"].(time.Time) + s.assertTimeIsCloseToNow(c, obtainedTime) + obtained["timestamp"] = now // XXX: Need to mock out time to test this fully expected := syslogparser.LogParts{ "timestamp": now, @@ -121,6 +125,43 @@ func (s *Rfc3164TestSuite) TestParser_NoTimestamp(c *C) { c.Assert(obtained, DeepEquals, expected) } +// RFC 3164 section 4.3.3 +func (s *Rfc3164TestSuite) TestParser_NoPriority(c *C) { + buff := []byte("Oct 11 22:14:15 Testing no priority") + + p := NewParser(buff) + expectedP := &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + location: time.UTC, + } + + c.Assert(p, DeepEquals, expectedP) + + err := p.Parse() + c.Assert(err, IsNil) + + now := time.Now() + + obtained := p.Dump() + obtainedTime := obtained["timestamp"].(time.Time) + s.assertTimeIsCloseToNow(c, obtainedTime) + + obtained["timestamp"] = now // XXX: Need to mock out time to test this fully + expected := syslogparser.LogParts{ + "timestamp": now, + "hostname": "", + "tag": "", + "content": "Oct 11 22:14:15 Testing no priority", + "priority": 13, + "facility": 1, + "severity": 5, + } + + c.Assert(obtained, DeepEquals, expected) +} + func (s *Rfc3164TestSuite) TestParseHeader_Valid(c *C) { buff := []byte("Oct 11 22:14:15 mymachine ") now := time.Now() @@ -375,3 +416,11 @@ func (s *Rfc3164TestSuite) assertRfc3164message(c *C, msg rfc3164message, b []by c.Assert(obtained, Equals, msg) c.Assert(p.cursor, Equals, expC) } + +func (s *Rfc3164TestSuite) assertTimeIsCloseToNow(c *C, obtainedTime time.Time) { + now := time.Now() + timeStart := now.Add(-(time.Second * 5)) + timeEnd := now.Add(time.Second) + c.Assert(obtainedTime.After(timeStart), Equals, true) + c.Assert(obtainedTime.Before(timeEnd), Equals, true) +} diff --git a/server.go b/server.go index fd1d96b..352597b 100644 --- a/server.go +++ b/server.go @@ -258,7 +258,7 @@ func (s *Server) parser(line []byte, client string, tlsPeer string) { logParts := parser.Dump() logParts["client"] = client - if logParts["hostname"] == "" && s.format == RFC3164 { + if logParts["hostname"] == "" && (s.format == RFC3164 || s.format == Automatic) { if i := strings.Index(client, ":"); i > 1 { logParts["hostname"] = client[:i] } else { diff --git a/server_test.go b/server_test.go index 3431821..3f7da2f 100644 --- a/server_test.go +++ b/server_test.go @@ -19,6 +19,7 @@ type ServerSuite struct { var _ = Suite(&ServerSuite{}) var exampleSyslog = "<31>Dec 26 05:08:46 hostname tag[296]: content" var exampleSyslogNoTSTagHost = "<14>INFO leaving (1) step postscripts" +var exampleSyslogNoPriority = "Dec 26 05:08:46 hostname test with no priority - see rfc 3164 section 4.3.3" var exampleRFC5424Syslog = "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - 'su root' failed for lonvick on /dev/pts/8" func (s *ServerSuite) TestTailFile(c *C) { @@ -184,6 +185,24 @@ func (s *ServerSuite) TestUDP3164NoTag(c *C) { c.Check(handler.LastError, IsNil) } +func (s *ServerSuite) TestUDPAutomatic3164NoPriority(c *C) { + handler := new(HandlerMock) + server := NewServer() + server.SetFormat(Automatic) + server.SetHandler(handler) + server.SetTimeout(10) + server.goParseDatagrams() + server.datagramChannel <- DatagramMessage{[]byte(exampleSyslogNoPriority), "127.0.0.1:45789"} + close(server.datagramChannel) + server.Wait() + c.Check(handler.LastLogParts["hostname"], Equals, "127.0.0.1") + c.Check(handler.LastLogParts["tag"], Equals, "") + c.Check(handler.LastLogParts["priority"], Equals, 13) + c.Check(handler.LastLogParts["content"], Equals, exampleSyslogNoPriority) + c.Check(handler.LastMessageLength, Equals, int64(len(exampleSyslogNoPriority))) + c.Check(handler.LastError, IsNil) +} + func (s *ServerSuite) TestUDP6587(c *C) { handler := new(HandlerMock) server := NewServer()