gitlab.com/gitlab-org/labkit@v1.21.0/log/initialization_test.go (about) 1 package log 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "regexp" 8 "testing" 9 "time" 10 11 logrus "github.com/sirupsen/logrus" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestInitialize(t *testing.T) { 16 rfc3339baseTimestampPattern := "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}" 17 iso8601baseTimestampPattern := "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}" 18 19 tests := []struct { 20 name string 21 opts []LoggerOption 22 envVars map[string]string 23 wantErr bool 24 regexps []*regexp.Regexp 25 infoMessage string 26 debugMessage string 27 }{ 28 { 29 name: "trivial", 30 regexps: []*regexp.Regexp{ 31 regexp.MustCompile(`^$`), 32 }, 33 }, 34 { 35 name: "simple", 36 infoMessage: "Hello", 37 regexps: []*regexp.Regexp{ 38 regexp.MustCompile(`^time=.*level=info.*msg=Hello`), 39 }, 40 }, 41 { 42 name: "simple-json", 43 opts: []LoggerOption{ 44 WithFormatter("json"), 45 }, 46 infoMessage: "Hello", 47 regexps: []*regexp.Regexp{ 48 regexp.MustCompile(`^{"level":"info","msg":"Hello"`), 49 }, 50 }, 51 { 52 name: "simple-text", 53 opts: []LoggerOption{ 54 WithFormatter("text"), 55 }, 56 infoMessage: "Hello", 57 regexps: []*regexp.Regexp{ 58 regexp.MustCompile(`^time=.*level=info.*msg=Hello`), 59 }, 60 }, 61 { 62 name: "invalid-formatter", 63 opts: []LoggerOption{ 64 WithFormatter("rubbish"), 65 }, 66 infoMessage: "Hello", 67 regexps: []*regexp.Regexp{ 68 regexp.MustCompile(`^time=.*level=warning.*msg="unknown logging format rubbish, ignoring option"`), 69 regexp.MustCompile(`(?m)^time=.*level=info.*msg=Hello`), 70 }, 71 }, 72 { 73 name: "loglevel-below-threshold", 74 opts: []LoggerOption{ 75 WithLogLevel("info"), 76 }, 77 debugMessage: "Hello", 78 regexps: []*regexp.Regexp{ 79 regexp.MustCompile(`^$`), 80 }, 81 }, 82 { 83 name: "loglevel-at-threshold", 84 opts: []LoggerOption{ 85 WithLogLevel("info"), 86 }, 87 infoMessage: "InfoHello", 88 debugMessage: "DebugHello", 89 regexps: []*regexp.Regexp{ 90 regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`), 91 }, 92 }, 93 { 94 name: "invalid-threshold", 95 opts: []LoggerOption{ 96 WithLogLevel("garbage"), 97 }, 98 infoMessage: "InfoHello", 99 debugMessage: "DebugHello", 100 101 regexps: []*regexp.Regexp{ 102 regexp.MustCompile(`level=warning msg="unknown log level, ignoring option`), 103 regexp.MustCompile(`(?m)^time=.*level=info.*msg=InfoHello\n$`), 104 }, 105 }, 106 { 107 name: "invalid-threshold-json-format", 108 opts: []LoggerOption{ 109 WithFormatter("json"), 110 WithLogLevel("garbage"), 111 }, 112 infoMessage: "InfoHello", 113 debugMessage: "DebugHello", 114 115 regexps: []*regexp.Regexp{ 116 regexp.MustCompile(`"msg":"unknown log level, ignoring option: not a valid logrus Level`), 117 regexp.MustCompile(`"msg":"InfoHello"`), 118 }, 119 }, 120 { 121 name: "emit-to-stderr", 122 opts: []LoggerOption{ 123 WithOutputName("stderr"), 124 }, 125 infoMessage: "InfoHello", 126 regexps: []*regexp.Regexp{ 127 regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`), 128 }, 129 }, 130 { 131 name: "emit-to-stdout", 132 opts: []LoggerOption{ 133 WithOutputName("stdout"), 134 }, 135 infoMessage: "InfoHello", 136 regexps: []*regexp.Regexp{ 137 regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`), 138 }, 139 }, 140 { 141 name: "emit-to-file", 142 opts: []LoggerOption{ 143 WithOutputName("/tmp/test.log"), 144 }, 145 infoMessage: "InfoHello", 146 regexps: []*regexp.Regexp{ 147 regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`), 148 }, 149 }, 150 { 151 name: "color", 152 opts: []LoggerOption{ 153 WithFormatter("color"), 154 }, 155 infoMessage: "It's starting to look like a triple rainbow.", 156 regexps: []*regexp.Regexp{ 157 regexp.MustCompile("^\x1b\\[36mINFO\x1b\\[0m\\[0000\\] It's starting to look like a triple rainbow."), 158 }, 159 }, 160 { 161 name: "timezone-utc-plus-12", 162 opts: []LoggerOption{ 163 WithTimezone(time.FixedZone("UTC+12", 12*60*60)), 164 }, 165 infoMessage: "test", 166 regexps: []*regexp.Regexp{ 167 regexp.MustCompile(`^time=[^ ]+\+12:00.*level=info.*msg=test\n$`), 168 }, 169 }, 170 { 171 name: "timezone-utc-plus-zero", 172 opts: []LoggerOption{ 173 WithTimezone(time.UTC), 174 }, 175 infoMessage: "test", 176 regexps: []*regexp.Regexp{ 177 regexp.MustCompile(`^time=[^ ]+Z.*level=info.*msg=test\n$`), 178 }, 179 }, 180 { 181 name: "two-formatters", 182 opts: []LoggerOption{ 183 WithTimezone(time.FixedZone("UTC-12", -12*60*60)), 184 WithFormatter("json"), 185 }, 186 infoMessage: "test", 187 regexps: []*regexp.Regexp{ 188 regexp.MustCompile(`"time":"[^"]+-12:00"`), 189 regexp.MustCompile(`"msg":"test"`), 190 }, 191 }, 192 { 193 name: "timestamp-format-default-formatter-and-default-layout", 194 opts: []LoggerOption{ 195 WithTimezone(time.UTC), // using a fixed timezone for portable/reproducible tests 196 }, 197 infoMessage: "Hello", 198 regexps: []*regexp.Regexp{ 199 regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", rfc3339baseTimestampPattern)), 200 }, 201 }, 202 { 203 name: "timestamp-format-default-formatter-and-iso8601-layout", 204 opts: []LoggerOption{ 205 WithTimezone(time.UTC), 206 }, 207 envVars: map[string]string{iso8601TimestampEnvKey: ""}, 208 infoMessage: "Hello", 209 regexps: []*regexp.Regexp{ 210 regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", iso8601baseTimestampPattern)), 211 }, 212 }, 213 { 214 name: "timestamp-format-json-formatter-and-default-layout", 215 opts: []LoggerOption{ 216 WithFormatter("json"), 217 WithTimezone(time.UTC), 218 }, 219 infoMessage: "Hello", 220 regexps: []*regexp.Regexp{ 221 regexp.MustCompile(fmt.Sprintf("\"time\":\"%sZ\"", rfc3339baseTimestampPattern)), 222 }, 223 }, 224 { 225 name: "timestamp-format-json-formatter-and-iso8601-layout", 226 opts: []LoggerOption{ 227 WithFormatter("json"), 228 WithTimezone(time.UTC), 229 }, 230 envVars: map[string]string{iso8601TimestampEnvKey: ""}, 231 infoMessage: "Hello", 232 regexps: []*regexp.Regexp{ 233 regexp.MustCompile(fmt.Sprintf("\"time\":\"%sZ\"", iso8601baseTimestampPattern)), 234 }, 235 }, 236 { 237 name: "timestamp-format-text-formatter-and-default-layout", 238 opts: []LoggerOption{ 239 WithFormatter("text"), 240 WithTimezone(time.UTC), 241 }, 242 infoMessage: "Hello", 243 regexps: []*regexp.Regexp{ 244 regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", rfc3339baseTimestampPattern)), 245 }, 246 }, 247 { 248 name: "timestamp-format-text-formatter-and-iso8601-layout", 249 opts: []LoggerOption{ 250 WithFormatter("text"), 251 WithTimezone(time.UTC), 252 }, 253 envVars: map[string]string{iso8601TimestampEnvKey: ""}, 254 infoMessage: "Hello", 255 regexps: []*regexp.Regexp{ 256 regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", iso8601baseTimestampPattern)), 257 }, 258 }, 259 { 260 name: "timestamp-format-iso8601-layout-and-timezone-offset", 261 opts: []LoggerOption{ 262 WithTimezone(time.FixedZone("UTC+12", 12*60*60)), 263 }, 264 envVars: map[string]string{iso8601TimestampEnvKey: ""}, 265 infoMessage: "Hello", 266 regexps: []*regexp.Regexp{ 267 regexp.MustCompile(fmt.Sprintf("^time=\"%s\\+12:00\"", iso8601baseTimestampPattern)), 268 }, 269 }, 270 } 271 272 for _, tt := range tests { 273 t.Run(tt.name, func(t *testing.T) { 274 var buf bytes.Buffer 275 276 opts := append([]LoggerOption{}, tt.opts...) 277 opts = append(opts, WithWriter(&buf)) 278 279 backupAndSetEnvVars(t, tt.envVars) 280 281 closer, err := Initialize(opts...) 282 defer closer.Close() 283 284 if tt.infoMessage != "" { 285 logrus.Info(tt.infoMessage) 286 } 287 288 if tt.debugMessage != "" { 289 logrus.Debug(tt.debugMessage) 290 } 291 292 if tt.wantErr { 293 require.Error(t, err) 294 } else { 295 require.NoError(t, err) 296 } 297 298 bytes := buf.Bytes() 299 for _, re := range tt.regexps { 300 require.Regexp(t, re, string(bytes)) 301 } 302 }) 303 } 304 } 305 306 func backupAndSetEnvVars(t *testing.T, vars map[string]string) { 307 t.Helper() 308 309 // backup any env vars and override them for the duration of t 310 bkpSetEnvVars := make(map[string]string) 311 var bkpUnsetEnvVars []string 312 for k := range vars { 313 v, exists := os.LookupEnv(k) 314 if exists { 315 bkpSetEnvVars[k] = v 316 } else { 317 bkpUnsetEnvVars = append(bkpUnsetEnvVars, k) 318 } 319 require.NoError(t, os.Setenv(k, v)) 320 } 321 322 // restore any env vars at the end of t 323 t.Cleanup(func() { 324 for k, v := range bkpSetEnvVars { 325 require.NoError(t, os.Setenv(k, v)) 326 } 327 for _, k := range bkpUnsetEnvVars { 328 require.NoError(t, os.Unsetenv(k)) 329 } 330 }) 331 }