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  }