github.com/blend/go-sdk@v1.20220411.3/logger/logger_test.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package logger 9 10 import ( 11 "bytes" 12 "context" 13 "fmt" 14 "testing" 15 "time" 16 17 "github.com/blend/go-sdk/assert" 18 "github.com/blend/go-sdk/uuid" 19 ) 20 21 func TestNew(t *testing.T) { 22 assert := assert.New(t) 23 24 log, err := New() 25 assert.Nil(err) 26 assert.NotNil(log.Flags) 27 assert.NotNil(log.Writable) 28 assert.NotNil(log.Scope) 29 assert.NotNil(log.Formatter) 30 assert.NotNil(log.Output) 31 assert.True(log.RecoverPanics) 32 33 for _, defaultFlag := range DefaultFlags { 34 assert.True(log.Flags.IsEnabled(defaultFlag)) 35 } 36 assert.True(log.Writable.All()) 37 38 log, err = New(OptAll(), OptFormatter(NewJSONOutputFormatter())) 39 assert.Nil(err) 40 assert.True(log.Flags.IsEnabled(uuid.V4().String())) 41 typed, ok := log.Formatter.(*JSONOutputFormatter) 42 assert.True(ok) 43 assert.NotNil(typed) 44 } 45 46 func TestLoggerFlagsWritten(t *testing.T) { 47 its := assert.New(t) 48 49 buf := new(bytes.Buffer) 50 log := Memory(buf) 51 defer log.Close() 52 53 log.Writable.Disable(Info) 54 55 eventTriggered := make(chan struct{}) 56 log.Listen(Info, DefaultListenerName, func(_ context.Context, e Event) { 57 close(eventTriggered) 58 }) 59 60 log.Dispatch(context.TODO(), NewMessageEvent(Info, "test")) 61 <-eventTriggered 62 its.Empty(buf.String()) 63 64 log.Dispatch(context.TODO(), NewMessageEvent(Error, "this is just a test")) 65 its.Equal("[error] this is just a test\n", buf.String()) 66 } 67 68 func TestLoggerScopes(t *testing.T) { 69 its := assert.New(t) 70 71 buf := new(bytes.Buffer) 72 log := Memory(buf) 73 defer log.Close() 74 75 log.Scopes.Disable("disabled/*") 76 77 eventTriggered := make(chan struct{}) 78 log.Listen(Error, DefaultListenerName, func(_ context.Context, e Event) { 79 close(eventTriggered) 80 }) 81 82 log.WithPath("disabled", "foo").TriggerContext(context.TODO(), NewMessageEvent(Info, "test")) 83 // should not trigger with scope disabled 84 its.Empty(buf.String()) 85 86 log.WithPath("not-disabled", "foo").TriggerContext(context.TODO(), NewMessageEvent(Error, "this is just a test")) 87 <-eventTriggered 88 its.Equal("[not-disabled > foo] [error] this is just a test\n", buf.String()) 89 } 90 91 func TestLoggerWritableScopes(t *testing.T) { 92 its := assert.New(t) 93 94 buf := new(bytes.Buffer) 95 log := Memory(buf) 96 defer log.Close() 97 98 log.WritableScopes.Disable("disabled/*") 99 100 eventTriggered := make(chan struct{}) 101 log.Listen(Error, DefaultListenerName, func(_ context.Context, e Event) { 102 close(eventTriggered) 103 }) 104 105 log.WithPath("disabled", "foo").TriggerContext(context.TODO(), NewMessageEvent(Error, "test")) 106 <-eventTriggered 107 its.Empty(buf.String()) 108 109 eventTriggered = make(chan struct{}) 110 log.WithPath("not-disabled", "foo").TriggerContext(context.TODO(), NewMessageEvent(Error, "this is just a test")) 111 <-eventTriggered 112 its.Equal("[not-disabled > foo] [error] this is just a test\n", buf.String()) 113 } 114 115 func TestLoggerE2ESubContext(t *testing.T) { 116 assert := assert.New(t) 117 118 output := new(bytes.Buffer) 119 log, err := New( 120 OptOutput(output), 121 OptText(OptTextHideTimestamp(), OptTextNoColor()), 122 ) 123 assert.Nil(err) 124 defer log.Close() 125 126 scID := uuid.V4().String() 127 sc := log.WithPath(scID) 128 129 sc.Infof("this is infof") 130 sc.Errorf("this is errorf") 131 sc.Fatalf("this is fatalf") 132 133 sc.Trigger(NewMessageEvent(Info, "this is a triggered message")) 134 135 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 136 defer cancel() 137 log.DrainContext(ctx) 138 139 assert.Contains(output.String(), fmt.Sprintf("[%s] [info] this is infof", scID)) 140 assert.Contains(output.String(), fmt.Sprintf("[%s] [error] this is errorf", scID)) 141 assert.Contains(output.String(), fmt.Sprintf("[%s] [fatal] this is fatalf", scID)) 142 assert.Contains(output.String(), fmt.Sprintf("[%s] [info] this is a triggered message", scID)) 143 } 144 145 func TestLoggerE2ESubContextFields(t *testing.T) { 146 assert := assert.New(t) 147 148 output := new(bytes.Buffer) 149 log, err := New( 150 OptOutput(output), 151 OptText(OptTextHideTimestamp(), OptTextNoColor()), 152 ) 153 assert.Nil(err) 154 155 fieldKey := uuid.V4().String() 156 fieldValue := uuid.V4().String() 157 sc := log.WithLabels(Labels{fieldKey: fieldValue}) 158 159 sc.Infof("this is infof") 160 sc.Errorf("this is errorf") 161 sc.Fatalf("this is fatalf") 162 163 sc.Trigger(NewMessageEvent(Info, "this is a triggered message")) 164 log.DrainContext(context.Background()) 165 166 assert.Contains(output.String(), fmt.Sprintf("[info] this is infof\t%s=%s", fieldKey, fieldValue)) 167 assert.Contains(output.String(), fmt.Sprintf("[error] this is errorf\t%s=%s", fieldKey, fieldValue)) 168 assert.Contains(output.String(), fmt.Sprintf("[fatal] this is fatalf\t%s=%s", fieldKey, fieldValue)) 169 assert.Contains(output.String(), fmt.Sprintf("[info] this is a triggered message\t%s=%s", fieldKey, fieldValue)) 170 } 171 172 func TestLoggerSkipTrigger(t *testing.T) { 173 assert := assert.New(t) 174 175 output := new(bytes.Buffer) 176 log, err := New( 177 OptOutput(output), 178 OptText(OptTextHideTimestamp(), OptTextNoColor()), 179 ) 180 assert.Nil(err) 181 defer log.Close() 182 183 var wasCalled bool 184 log.Listen(Info, "---", func(ctx context.Context, e Event) { 185 wasCalled = true 186 }) 187 188 log.TriggerContext(WithSkipTrigger(context.Background(), true), NewMessageEvent(Info, "this is a triggered message")) 189 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 190 defer cancel() 191 log.DrainContext(ctx) 192 193 assert.False(wasCalled) 194 assert.Contains(output.String(), "[info] this is a triggered message") 195 } 196 197 func TestLoggerSkipWrite(t *testing.T) { 198 assert := assert.New(t) 199 200 output := new(bytes.Buffer) 201 log, err := New( 202 OptOutput(output), 203 OptText(OptTextHideTimestamp(), OptTextNoColor()), 204 ) 205 assert.Nil(err) 206 defer log.Close() 207 208 var wasCalled bool 209 log.Listen(Info, "---", func(ctx context.Context, e Event) { 210 wasCalled = true 211 }) 212 assert.True(log.HasListener(Info, "---")) 213 214 log.TriggerContext(WithSkipWrite(context.Background(), true), NewMessageEvent(Info, "this is a triggered message")) 215 216 // at the very least this cannot cause a deadlock. 217 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 218 defer cancel() 219 220 log.DrainContext(ctx) 221 assert.True(wasCalled) 222 assert.Empty(output.String()) 223 } 224 225 func TestLoggerListeners(t *testing.T) { 226 assert := assert.New(t) 227 228 log := MustNew() 229 defer log.Close() 230 231 assert.Empty(log.Listeners) 232 log.Listen(Info, "foo", NewMessageEventListener(func(_ context.Context, me MessageEvent) {})) 233 assert.NotEmpty(log.Listeners) 234 assert.True(log.HasListeners(Info)) 235 assert.True(log.HasListener(Info, "foo")) 236 assert.False(log.HasListener(Info, "bar")) 237 238 log.Listen(Error, "foo", NewMessageEventListener(func(_ context.Context, me MessageEvent) {})) 239 assert.True(log.HasListeners(Error)) 240 assert.True(log.HasListener(Error, "foo")) 241 assert.False(log.HasListener(Error, "bar")) 242 243 log.Listen(Info, "bar", NewMessageEventListener(func(_ context.Context, me MessageEvent) {})) 244 assert.True(log.HasListeners(Info)) 245 assert.True(log.HasListener(Info, "foo")) 246 assert.True(log.HasListener(Info, "bar")) 247 248 log.Listen(Error, "bar", NewMessageEventListener(func(_ context.Context, me MessageEvent) {})) 249 assert.True(log.HasListeners(Error)) 250 assert.True(log.HasListener(Error, "foo")) 251 assert.True(log.HasListener(Error, "bar")) 252 253 assert.Nil(log.RemoveListener(Info, "foo")) 254 assert.True(log.HasListeners(Info)) 255 assert.False(log.HasListener(Info, "foo")) 256 assert.True(log.HasListener(Info, "bar")) 257 258 assert.Nil(log.RemoveListeners(Error)) 259 assert.False(log.HasListeners(Error)) 260 assert.False(log.HasListener(Error, "foo")) 261 assert.False(log.HasListener(Error, "bar")) 262 } 263 264 func TestLoggerFilters(t *testing.T) { 265 assert := assert.New(t) 266 267 log := MustNew() 268 defer log.Close() 269 270 noop := func(_ context.Context, e MessageEvent) (MessageEvent, bool) { 271 return e, false 272 } 273 274 assert.Empty(log.Filters) 275 log.Filter(Info, "foo", NewMessageEventFilter(noop)) 276 assert.NotEmpty(log.Filters) 277 assert.True(log.HasFilters(Info)) 278 assert.True(log.HasFilter(Info, "foo")) 279 assert.False(log.HasFilter(Info, "bar")) 280 281 log.Filter(Error, "foo", NewMessageEventFilter(noop)) 282 assert.True(log.HasFilters(Error)) 283 assert.True(log.HasFilter(Error, "foo")) 284 assert.False(log.HasFilter(Error, "bar")) 285 286 log.Filter(Info, "bar", NewMessageEventFilter(noop)) 287 assert.True(log.HasFilters(Info)) 288 assert.True(log.HasFilter(Info, "foo")) 289 assert.True(log.HasFilter(Info, "bar")) 290 291 log.Filter(Error, "bar", NewMessageEventFilter(noop)) 292 assert.True(log.HasFilters(Error)) 293 assert.True(log.HasFilter(Error, "foo")) 294 assert.True(log.HasFilter(Error, "bar")) 295 296 log.RemoveFilter(Info, "foo") 297 assert.True(log.HasFilters(Info)) 298 assert.False(log.HasFilter(Info, "foo")) 299 assert.True(log.HasFilter(Info, "bar")) 300 301 log.RemoveFilters(Error) 302 assert.False(log.HasFilters(Error)) 303 assert.False(log.HasFilter(Error, "foo")) 304 assert.False(log.HasFilter(Error, "bar")) 305 } 306 307 func TestLoggerDispatchFilterMutate(t *testing.T) { 308 it := assert.New(t) 309 310 output := new(bytes.Buffer) 311 log, err := New( 312 OptOutput(output), 313 OptText(OptTextHideTimestamp(), OptTextNoColor()), 314 ) 315 it.Nil(err) 316 defer log.Close() 317 318 var wasCalled bool 319 var textWasModified bool 320 321 log.Listen(Info, "---", func(ctx context.Context, e Event) { 322 wasCalled = true 323 textWasModified = e.(MessageEvent).Text == "not_test_message" 324 }) 325 var wasFiltered bool 326 log.Filter(Info, "---", func(ctx context.Context, e Event) (Event, bool) { 327 wasFiltered = true 328 copy := e.(MessageEvent) 329 copy.Text = "not_" + copy.Text 330 return copy, false 331 }) 332 333 log.TriggerContext(context.Background(), NewMessageEvent(Info, "test_message")) 334 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 335 defer cancel() 336 log.DrainContext(ctx) 337 338 it.True(wasFiltered) 339 it.True(wasCalled) 340 it.True(textWasModified) 341 342 it.Equal("[info] not_test_message\n", output.String()) 343 } 344 345 func TestLoggerDispatchFilterDrop(t *testing.T) { 346 it := assert.New(t) 347 348 output := new(bytes.Buffer) 349 log, err := New( 350 OptOutput(output), 351 OptText(OptTextHideTimestamp(), OptTextNoColor()), 352 ) 353 it.Nil(err) 354 defer log.Close() 355 356 var wasCalled bool 357 log.Listen(Info, "---", func(ctx context.Context, e Event) { 358 wasCalled = true 359 }) 360 var wasFiltered bool 361 log.Filter(Info, "---", func(ctx context.Context, e Event) (Event, bool) { 362 wasFiltered = true 363 return nil, true 364 }) 365 366 log.TriggerContext(context.Background(), NewMessageEvent(Info, "this is a triggered message")) 367 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 368 defer cancel() 369 log.DrainContext(ctx) 370 371 it.True(wasFiltered) 372 it.False(wasCalled) 373 it.Empty(output.String()) 374 } 375 376 func TestLoggerDrain(t *testing.T) { 377 assert := assert.New(t) 378 379 log := MustNew() 380 defer log.Close() 381 assert.Empty(log.Listeners) 382 383 eventsCounted := 0 384 log.Listen(Info, "foo", NewMessageEventListener(func(_ context.Context, me MessageEvent) { 385 eventsCounted++ 386 })) 387 388 for i := 0; i < 5; i++ { 389 log.Info("event") 390 } 391 log.Drain() 392 assert.Equal(5, eventsCounted) 393 394 for i := 0; i < 4; i++ { 395 log.Info("event") 396 } 397 log.Drain() 398 assert.Equal(9, eventsCounted) 399 } 400 401 func TestLoggerProd(t *testing.T) { 402 assert := assert.New(t) 403 404 p := Prod(OptEnabled("example-string")) 405 defer p.Close() 406 407 assert.True(p.Flags.IsEnabled("example-string")) 408 assert.True(p.Formatter.(*TextOutputFormatter).NoColor) 409 } 410 411 func TestLoggerClose(t *testing.T) { 412 assert := assert.New(t) 413 414 buf0 := new(bytes.Buffer) 415 l0 := Memory(buf0) 416 buf1 := new(bytes.Buffer) 417 l1 := Memory(buf1) 418 419 l0.Close() 420 l1.Infof("this is a test") 421 assert.Empty(buf0.String()) 422 assert.NotEmpty(buf1.String()) 423 }