github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/logger/tests/implementation_test.go (about)

     1  // Copyright 2022 Meta Platforms, Inc. and affiliates.
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
     4  //
     5  // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     6  //
     7  // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
     8  //
     9  // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    10  //
    11  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    12  
    13  package tests
    14  
    15  import (
    16  	"bytes"
    17  	"fmt"
    18  	"log"
    19  	"net/url"
    20  	"strings"
    21  	"sync"
    22  	"testing"
    23  
    24  	"github.com/facebookincubator/go-belt"
    25  	"github.com/facebookincubator/go-belt/pkg/field"
    26  	"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
    27  	"github.com/facebookincubator/go-belt/tool/logger/implementation/stdlib"
    28  	"github.com/facebookincubator/go-belt/tool/logger/implementation/zap"
    29  	"github.com/facebookincubator/go-belt/tool/logger/types"
    30  	upstreamlogrus "github.com/sirupsen/logrus"
    31  	upstreamzap "go.uber.org/zap"
    32  )
    33  
    34  type zapBuffer struct {
    35  	bytes.Buffer
    36  }
    37  
    38  func (buf *zapBuffer) Close() error {
    39  	return nil
    40  }
    41  
    42  func (buf *zapBuffer) Sync() error {
    43  	return nil
    44  }
    45  
    46  type logger struct {
    47  	Name   string
    48  	Logger types.Logger
    49  	Output *bytes.Buffer
    50  }
    51  
    52  func getImplementations(t *testing.T) []logger {
    53  	var result []logger
    54  
    55  	// stdlib
    56  	{
    57  		var buf bytes.Buffer
    58  		result = append(result, logger{
    59  			Name:   "stdlib",
    60  			Logger: stdlib.New(log.New(&buf, "", 0), types.LevelTrace),
    61  			Output: &buf,
    62  		})
    63  	}
    64  
    65  	// zap
    66  	{
    67  		var buf zapBuffer
    68  		err := upstreamzap.RegisterSink("buf", func(*url.URL) (upstreamzap.Sink, error) {
    69  			return &buf, nil
    70  		})
    71  		if err != nil {
    72  			t.Fatal(err)
    73  		}
    74  
    75  		zapCfg := upstreamzap.NewDevelopmentConfig()
    76  		zapCfg.Encoding = "json"
    77  		zapCfg.OutputPaths = []string{"buf:"}
    78  		zapCfg.Level = upstreamzap.NewAtomicLevelAt(zap.LevelToZap(types.LevelTrace))
    79  		zapLogger, err := zapCfg.Build()
    80  		if err != nil {
    81  			t.Fatal(err)
    82  		}
    83  		result = append(result, logger{
    84  			Name:   "zap",
    85  			Logger: zap.New(zapLogger),
    86  			Output: &buf.Buffer,
    87  		})
    88  	}
    89  
    90  	// logrus
    91  	{
    92  		var buf bytes.Buffer
    93  		logrusLogger := upstreamlogrus.New()
    94  		logrusLogger.Out = &buf
    95  		logrusLogger.Level = logrus.LevelToLogrus(types.LevelTrace)
    96  		result = append(result, logger{
    97  			Name:   "logrus",
    98  			Logger: logrus.New(logrusLogger),
    99  			Output: &buf,
   100  		})
   101  	}
   102  
   103  	// glog
   104  	{
   105  		// the upstream glog logger does not support diverting the output to a buffer
   106  	}
   107  
   108  	return result
   109  }
   110  
   111  func TestImplementations(t *testing.T) {
   112  	for _, l := range getImplementations(t) {
   113  		t.Run(l.Name, func(t *testing.T) {
   114  			t.Run("race-check", func(t *testing.T) {
   115  				l.Output.Reset()
   116  
   117  				// this test supposed to be ran with "-race"
   118  				var wg sync.WaitGroup
   119  				wg.Add(1)
   120  				go func() {
   121  					defer wg.Done()
   122  					l.Logger.Errorf("test0")
   123  				}()
   124  				wg.Add(1)
   125  				go func() {
   126  					defer wg.Done()
   127  					l.Logger.Errorf("test1")
   128  				}()
   129  				wg.Wait()
   130  				l.Logger.Flush()
   131  			})
   132  
   133  			t.Run("Errorf", func(t *testing.T) {
   134  				l.Output.Reset()
   135  				l.Logger.Errorf("unit-test")
   136  				l.Logger.Flush()
   137  				if !strings.Contains(l.Output.String(), "unit-test") {
   138  					t.Fatalf("logger %s did not print an error using Errorf", l.Name)
   139  				}
   140  			})
   141  
   142  			t.Run("Error", func(t *testing.T) {
   143  				l.Output.Reset()
   144  				l.Logger.Error(fmt.Errorf("unit-test"))
   145  				l.Logger.Flush()
   146  				if !strings.Contains(l.Output.String(), "unit-test") {
   147  					t.Fatalf("logger %s did not print an error using Error", l.Name)
   148  				}
   149  			})
   150  
   151  			t.Run("Error-with-PreHook", func(t *testing.T) {
   152  				l.Output.Reset()
   153  				logger := l.Logger.WithPreHooks(addExtraFieldPreHook{})
   154  				logger.Error(fmt.Errorf("unit-test"))
   155  				logger.Flush()
   156  				if !strings.Contains(l.Output.String(), "unit-test") {
   157  					t.Fatalf("logger %s did not print an error using Error with the PreHook", l.Name)
   158  				}
   159  			})
   160  
   161  			t.Run("WithMessagePrefix", func(t *testing.T) {
   162  				l.Output.Reset()
   163  				logger := l.Logger.WithMessagePrefix("specialMagic")
   164  				logger.Error(fmt.Errorf("unit-test"))
   165  				logger.Flush()
   166  				if !strings.Contains(l.Output.String(), "specialMagic") {
   167  					t.Fatalf("logger %s did not print the special magic string", l.Name)
   168  				}
   169  			})
   170  		})
   171  	}
   172  }
   173  
   174  type addExtraFieldPreHook struct{}
   175  
   176  var addExtraFieldPreHookResult = types.PreHookResult{
   177  	ExtraFields: &field.Field{
   178  		Key:   "some-key",
   179  		Value: "some-value",
   180  	},
   181  }
   182  
   183  func (addExtraFieldPreHook) ProcessInput(belt.TraceIDs, types.Level, ...any) types.PreHookResult {
   184  	return addExtraFieldPreHookResult
   185  }
   186  
   187  func (addExtraFieldPreHook) ProcessInputf(belt.TraceIDs, types.Level, string, ...any) types.PreHookResult {
   188  	return addExtraFieldPreHookResult
   189  }
   190  
   191  func (addExtraFieldPreHook) ProcessInputFields(belt.TraceIDs, types.Level, string, field.AbstractFields) types.PreHookResult {
   192  	return addExtraFieldPreHookResult
   193  }