| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package cloudtail |
| |
| import ( |
| "reflect" |
| "testing" |
| "time" |
| |
| . "github.com/smartystreets/goconvey/convey" |
| ) |
| |
| func TestLogParserChain(t *testing.T) { |
| Convey("StdParser coverage", t, func() { |
| So(StdParser(), ShouldNotBeNil) |
| }) |
| |
| Convey("Empty works", t, func() { |
| chain := LogParserChain{} |
| entry := chain.ParseLogLine("hi") |
| So(entry, ShouldNotBeNil) |
| So(entry.TextPayload, ShouldEqual, "hi") |
| }) |
| |
| Convey("Non empty works", t, func() { |
| parser1 := &callbackParser{ |
| cb: func(string) *Entry { return nil }, |
| } |
| parser2 := &callbackParser{ |
| cb: func(line string) *Entry { return &Entry{TextPayload: line + ", yo"} }, |
| } |
| chain := LogParserChain{parser1, parser2} |
| entry := chain.ParseLogLine("hi") |
| So(entry.TextPayload, ShouldEqual, "hi, yo") |
| }) |
| } |
| |
| type textTestCase struct { |
| line string |
| wantSuccess bool |
| wantTimestamp string |
| wantSeverity Severity |
| wantPayload string |
| wantLabels map[string]string |
| } |
| |
| func testTextLogsParser(t *testing.T, p LogParser, cases []textTestCase) { |
| timeLayout := "2006-01-02T15:04:05.000000-07:00" |
| |
| for i, test := range cases { |
| got := p.ParseLogLine(test.line) |
| if test.wantSuccess && got == nil { |
| t.Errorf("%d: ParseLogLine('%s') -> nil, want success", i, test.line) |
| } else if !test.wantSuccess && got != nil { |
| t.Errorf("%d: ParseLogLine('%s') -> %v, want failure", i, test.line, got) |
| } else if test.wantSuccess && got.Timestamp.Format(timeLayout) != test.wantTimestamp { |
| t.Errorf("%d: ParseLogLine('%s').Timestamp -> %v, want %v", i, test.line, got.Timestamp.Format(timeLayout), test.wantTimestamp) |
| } else if test.wantSuccess && got.Severity != test.wantSeverity { |
| t.Errorf("%d: ParseLogLine('%s').Severity -> %v, want %v", i, test.line, got.Severity, test.wantSeverity) |
| } else if test.wantSuccess && got.TextPayload != test.wantPayload { |
| t.Errorf("%d: ParseLogLine('%s').TextPayload -> %v, want %v", i, test.line, got.TextPayload, test.wantPayload) |
| } else if test.wantSuccess && !reflect.DeepEqual(got.Labels, test.wantLabels) { |
| t.Errorf("%d: ParseLogLine('%s').Labels -> %v, want %v", i, test.line, got.Labels, test.wantLabels) |
| } |
| } |
| } |
| |
| func TestInfraLogsParser(t *testing.T) { |
| testTextLogsParser(t, &infraLogsParser{}, |
| []textTestCase{ |
| { |
| line: "not a valid log line", |
| wantSuccess: false, |
| }, |
| { |
| line: "[I2015-09-22T01:02:03.000004+00:00 123 456 __main__:789] Hello world", |
| wantSuccess: true, |
| wantTimestamp: "2015-09-22T01:02:03.000004+00:00", |
| wantSeverity: Info, |
| wantPayload: "[pid:123 tid:456 __main__:789] Hello world", |
| }, |
| { |
| line: "[C2015-09-22T01:02:03.000004-07:00 123 456 foo.bar:789] Hello world", |
| wantSuccess: true, |
| wantTimestamp: "2015-09-22T01:02:03.000004-07:00", |
| wantSeverity: Critical, |
| wantPayload: "[pid:123 tid:456 foo.bar:789] Hello world", |
| }, |
| { |
| line: "[C2015-09-22T01:02:03.000004-07:00 123 -456 foo.bar:789] Hello world", |
| wantSuccess: true, |
| wantTimestamp: "2015-09-22T01:02:03.000004-07:00", |
| wantSeverity: Critical, |
| wantPayload: "[pid:123 tid:-456 foo.bar:789] Hello world", |
| }, |
| { |
| line: "[I2015-10-27T06:59:24.219355 29084 140208595912448 lib.botmap:403] Checking swarming botmap updates...", |
| wantSuccess: true, |
| wantTimestamp: "2015-10-27T06:59:24.219355+00:00", |
| wantSeverity: Info, |
| wantPayload: "[pid:29084 tid:140208595912448 lib.botmap:403] Checking swarming botmap updates...", |
| }, |
| }) |
| } |
| |
| func TestTwistedLogsParser(t *testing.T) { |
| testTextLogsParser(t, &twistedLogsParser{}, |
| []textTestCase{ |
| { |
| line: "not a valid log line", |
| wantSuccess: false, |
| }, |
| { |
| line: "2015-09-27 23:40:38-0700 [-] Finished processing request with id: 97038920", |
| wantSuccess: true, |
| wantTimestamp: "2015-09-27T23:40:38.000000-07:00", |
| wantSeverity: Default, |
| wantPayload: "[-] Finished processing request with id: 97038920", |
| }, |
| { |
| line: "2015-09-27 23:41:00+0000 [HTTP11ClientProtocol,client] GitilesPoller: No new commits.", |
| wantSuccess: true, |
| wantTimestamp: "2015-09-27T23:41:00.000000+00:00", |
| wantSeverity: Default, |
| wantPayload: "[HTTP11ClientProtocol,client] GitilesPoller: No new commits.", |
| }, |
| { |
| line: "2015-09-27 23:41:00+1000 [HTTPChannel,44506,127.0.0.1] Loading builder Android's build 39149 from on-disk pickle", |
| wantSuccess: true, |
| wantTimestamp: "2015-09-27T23:41:00.000000+10:00", |
| wantSeverity: Default, |
| wantPayload: "[HTTPChannel,44506,127.0.0.1] Loading builder Android's build 39149 from on-disk pickle", |
| }, |
| }) |
| } |
| |
| func TestPuppetLogsParser(t *testing.T) { |
| testTextLogsParser(t, &puppetLogsParser{}, |
| []textTestCase{ |
| { |
| line: "not a valid log line", |
| wantSuccess: false, |
| }, |
| { |
| line: "Thu Oct 22 22:42:42 -0700 2015 Puppet (notice): Compiled catalog for vm8-m4.golo.chromium.org in environment production in 0.30 seconds", |
| wantSuccess: true, |
| wantTimestamp: "2015-10-22T22:42:42.000000-07:00", |
| wantSeverity: Notice, |
| wantPayload: "Puppet: Compiled catalog for vm8-m4.golo.chromium.org in environment production in 0.30 seconds", |
| }, |
| { |
| line: "Thu Oct 22 22:44:00 +1000 2015 Puppet (warning): Ignoring invalid UTF-8 byte sequences in data to be sent to PuppetDB", |
| wantSuccess: true, |
| wantTimestamp: "2015-10-22T22:44:00.000000+10:00", |
| wantSeverity: Warning, |
| wantPayload: "Puppet: Ignoring invalid UTF-8 byte sequences in data to be sent to PuppetDB", |
| }, |
| }) |
| } |
| |
| func TestApacheErrorLogsParser(t *testing.T) { |
| testTextLogsParser(t, &apacheErrorLogsParser{ |
| localTimeZone: time.FixedZone("", -7*60*60), |
| }, []textTestCase{ |
| { |
| line: "not a valid log line", |
| wantSuccess: false, |
| }, |
| { |
| line: "[Thu Oct 22 23:27:26 2015] [error] [client 192.168.70.8] Certificate Verification: Error (23): certificate revoked", |
| wantSuccess: true, |
| wantTimestamp: "2015-10-22T23:27:26.000000-07:00", |
| wantSeverity: Error, |
| wantPayload: "[192.168.70.8] Certificate Verification: Error (23): certificate revoked", |
| }, |
| }) |
| } |
| |
| func TestGlogLogsParser(t *testing.T) { |
| now := time.Date(2016, 6, 12, 23, 4, 5, 6, time.FixedZone("", -7*60*60)) |
| testTextLogsParser(t, &glogLogsParser{ |
| now: &now, |
| }, []textTestCase{ |
| { |
| line: "not a valid log line", |
| wantSuccess: false, |
| }, |
| { |
| line: "I0612 22:55:35.173471 4608 example.cc:123] Hello World", |
| wantSuccess: true, |
| wantTimestamp: "2016-06-12T22:55:35.173471-07:00", |
| wantSeverity: Info, |
| wantPayload: "[tid:4608 example.cc:123] Hello World", |
| wantLabels: map[string]string{ |
| "threadID": "4608", |
| "module": "example.cc", |
| "caller": "example.cc:123", |
| }, |
| }, |
| }) |
| } |
| |
| func TestLucipyLogsParser(t *testing.T) { |
| testTextLogsParser(t, &lucipyLogsParser{}, []textTestCase{ |
| { |
| line: "not a valid log line", |
| wantSuccess: false, |
| }, |
| { |
| line: "23176 2021-03-19 02:14:18.237 I: Hello World", |
| wantSuccess: true, |
| wantTimestamp: "2021-03-19T02:14:18.237000+00:00", |
| wantSeverity: Info, |
| wantPayload: "Hello World", |
| wantLabels: map[string]string{ |
| "processID": "23176", |
| }, |
| }, |
| }) |
| } |
| |
| type callbackParser struct { |
| cb func(line string) *Entry |
| mergeCb func(line string, e *Entry) bool |
| } |
| |
| func (p *callbackParser) ParseLogLine(line string) *Entry { return p.cb(line) } |
| |
| func (p *callbackParser) MergeLogLine(line string, e *Entry) bool { return p.mergeCb(line, e) } |