| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package weblog |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "testing" |
| 8 | |
| 9 | "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/logs" |
| 10 | |
| 11 | "github.com/stretchr/testify/assert" |
| 12 | "github.com/stretchr/testify/require" |
| 13 | ) |
| 14 | |
| 15 | func TestWebLog_guessParser(t *testing.T) { |
| 16 | type test = struct { |
| 17 | name string |
| 18 | inputs []string |
| 19 | wantParserType string |
| 20 | wantErr bool |
| 21 | } |
| 22 | tests := []test{ |
| 23 | { |
| 24 | name: "guessed csv", |
| 25 | wantParserType: logs.TypeCSV, |
| 26 | inputs: []string{ |
| 27 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123 0.123,0.321`, |
| 28 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123`, |
| 29 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123 0.123,0.321`, |
| 30 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123`, |
| 31 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 32 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123 0.123,0.321`, |
| 33 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123`, |
| 34 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123 0.123,0.321`, |
| 35 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123`, |
| 36 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 37 | }, |
| 38 | }, |
| 39 | { |
| 40 | name: "guessed ltsv", |
| 41 | wantParserType: logs.TypeLTSV, |
| 42 | inputs: []string{ |
| 43 | `field1:test.example.com:80 field2:88.191.254.20 field3:"GET / HTTP/1.0" 200 8674 field4:8674 field5:0.123`, |
| 44 | }, |
| 45 | }, |
| 46 | { |
| 47 | name: "guessed json", |
| 48 | wantParserType: logs.TypeJSON, |
| 49 | inputs: []string{ |
| 50 | `{}`, |
| 51 | ` {}`, |
| 52 | ` {} `, |
| 53 | `{"host": "example.com"}`, |
| 54 | `{"host": "example.com","time": "2020-08-04T20:23:27+03:00", "upstream_response_time": "0.776", "remote_addr": "1.2.3.4"}`, |
| 55 | ` {"host": "example.com","time": "2020-08-04T20:23:27+03:00", "upstream_response_time": "0.776", "remote_addr": "1.2.3.4"} `, |
| 56 | }, |
| 57 | }, |
| 58 | { |
| 59 | name: "unknown", |
| 60 | wantErr: true, |
| 61 | inputs: []string{ |
| 62 | `test.example.com 80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 63 | `test.example.com 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 64 | }, |
| 65 | }, |
| 66 | } |
| 67 | |
| 68 | weblog := prepareWebLog() |
| 69 | |
| 70 | for _, tc := range tests { |
| 71 | for i, input := range tc.inputs { |
| 72 | name := fmt.Sprintf("name=%s,input_num=%d", tc.name, i+1) |
| 73 | |
| 74 | t.Run(name, func(t *testing.T) { |
| 75 | p, err := weblog.newParser([]byte(input)) |
| 76 | |
| 77 | if tc.wantErr { |
| 78 | assert.Error(t, err) |
| 79 | } else { |
| 80 | require.NoError(t, err) |
| 81 | switch tc.wantParserType { |
| 82 | default: |
| 83 | t.Errorf("unknown parser type: %s", tc.wantParserType) |
| 84 | case logs.TypeLTSV: |
| 85 | assert.IsType(t, (*logs.LTSVParser)(nil), p) |
| 86 | case logs.TypeCSV: |
| 87 | require.IsType(t, (*logs.CSVParser)(nil), p) |
| 88 | case logs.TypeJSON: |
| 89 | require.IsType(t, (*logs.JSONParser)(nil), p) |
| 90 | } |
| 91 | } |
| 92 | }) |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | func TestWebLog_guessCSVParser(t *testing.T) { |
| 98 | type test = struct { |
| 99 | name string |
| 100 | inputs []string |
| 101 | wantCSVFormat string |
| 102 | wantErr bool |
| 103 | } |
| 104 | tests := []test{ |
| 105 | { |
| 106 | name: "guessed vhost custom4", |
| 107 | wantCSVFormat: csvVhostCustom4, |
| 108 | inputs: []string{ |
| 109 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123 0.123,0.321`, |
| 110 | }, |
| 111 | }, |
| 112 | { |
| 113 | name: "guessed vhost custom3", |
| 114 | wantCSVFormat: csvVhostCustom3, |
| 115 | inputs: []string{ |
| 116 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123`, |
| 117 | }, |
| 118 | }, |
| 119 | { |
| 120 | name: "guessed vhost custom2", |
| 121 | wantCSVFormat: csvVhostCustom2, |
| 122 | inputs: []string{ |
| 123 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123 0.123,0.321`, |
| 124 | }, |
| 125 | }, |
| 126 | { |
| 127 | name: "guessed vhost custom1", |
| 128 | wantCSVFormat: csvVhostCustom1, |
| 129 | inputs: []string{ |
| 130 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123`, |
| 131 | }, |
| 132 | }, |
| 133 | { |
| 134 | name: "guessed vhost common", |
| 135 | wantCSVFormat: csvVhostCommon, |
| 136 | inputs: []string{ |
| 137 | `test.example.com:80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 138 | }, |
| 139 | }, |
| 140 | { |
| 141 | name: "guessed custom4", |
| 142 | wantCSVFormat: csvCustom4, |
| 143 | inputs: []string{ |
| 144 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123 0.123,0.321`, |
| 145 | }, |
| 146 | }, |
| 147 | { |
| 148 | name: "guessed custom3", |
| 149 | wantCSVFormat: csvCustom3, |
| 150 | inputs: []string{ |
| 151 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 "-" "-" 8674 0.123`, |
| 152 | }, |
| 153 | }, |
| 154 | { |
| 155 | name: "guessed custom2", |
| 156 | wantCSVFormat: csvCustom2, |
| 157 | inputs: []string{ |
| 158 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123 0.123,0.321`, |
| 159 | }, |
| 160 | }, |
| 161 | { |
| 162 | name: "guessed custom1", |
| 163 | wantCSVFormat: csvCustom1, |
| 164 | inputs: []string{ |
| 165 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674 8674 0.123`, |
| 166 | }, |
| 167 | }, |
| 168 | { |
| 169 | name: "guessed common", |
| 170 | wantCSVFormat: csvCommon, |
| 171 | inputs: []string{ |
| 172 | `88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 173 | }, |
| 174 | }, |
| 175 | { |
| 176 | name: "unknown", |
| 177 | wantErr: true, |
| 178 | inputs: []string{ |
| 179 | `test.example.com 80 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 180 | `test.example.com 88.191.254.20 - - [22/Mar/2009:09:30:31 +0100] "GET / HTTP/1.0" 200 8674`, |
| 181 | }, |
| 182 | }, |
| 183 | } |
| 184 | |
| 185 | weblog := prepareWebLog() |
| 186 | |
| 187 | for i, tc := range tests { |
| 188 | for _, input := range tc.inputs { |
| 189 | name := fmt.Sprintf("name=%s,input_num=%d", tc.name, i+1) |
| 190 | |
| 191 | t.Run(name, func(t *testing.T) { |
| 192 | p, err := weblog.guessCSVParser([]byte(input)) |
| 193 | |
| 194 | if tc.wantErr { |
| 195 | assert.Error(t, err) |
| 196 | } else { |
| 197 | require.NoError(t, err) |
| 198 | assert.Equal(t, cleanCSVFormat(tc.wantCSVFormat), p.(*logs.CSVParser).Config.Format) |
| 199 | } |
| 200 | }) |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | func prepareWebLog() *Collector { |
| 206 | cfg := logs.ParserConfig{ |
| 207 | LogType: typeAuto, |
| 208 | CSV: logs.CSVConfig{ |
| 209 | Delimiter: " ", |
| 210 | CheckField: checkCSVFormatField, |
| 211 | }, |
| 212 | LTSV: logs.LTSVConfig{ |
| 213 | FieldDelimiter: "\t", |
| 214 | ValueDelimiter: ":", |
| 215 | }, |
| 216 | } |
| 217 | |
| 218 | return &Collector{ |
| 219 | Config: Config{ |
| 220 | GroupRespCodes: false, |
| 221 | ParserConfig: cfg, |
| 222 | }, |
| 223 | } |
| 224 | } |