...

Source file src/go.uber.org/zap/field_test.go

Documentation: go.uber.org/zap

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package zap
    22  
    23  import (
    24  	"math"
    25  	"net"
    26  	"regexp"
    27  	"sync"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"go.uber.org/zap/internal/stacktrace"
    33  	"go.uber.org/zap/zapcore"
    34  )
    35  
    36  type username string
    37  
    38  func (n username) MarshalLogObject(enc zapcore.ObjectEncoder) error {
    39  	enc.AddString("username", string(n))
    40  	return nil
    41  }
    42  
    43  func assertCanBeReused(t testing.TB, field Field) {
    44  	var wg sync.WaitGroup
    45  
    46  	for i := 0; i < 100; i++ {
    47  		enc := zapcore.NewMapObjectEncoder()
    48  
    49  		// Ensure using the field in multiple encoders in separate goroutines
    50  		// does not cause any races or panics.
    51  		wg.Add(1)
    52  		go func() {
    53  			defer wg.Done()
    54  			assert.NotPanics(t, func() {
    55  				field.AddTo(enc)
    56  			}, "Reusing a field should not cause issues")
    57  		}()
    58  	}
    59  
    60  	wg.Wait()
    61  }
    62  
    63  func TestFieldConstructors(t *testing.T) {
    64  	// Interface types.
    65  	addr := net.ParseIP("1.2.3.4")
    66  	name := username("phil")
    67  	ints := []int{5, 6}
    68  
    69  	// Helpful values for use in constructing pointers to primitives below.
    70  	var (
    71  		boolVal       = bool(true)
    72  		complex128Val = complex128(complex(0, 0))
    73  		complex64Val  = complex64(complex(0, 0))
    74  		durationVal   = time.Duration(time.Second)
    75  		float64Val    = float64(1.0)
    76  		float32Val    = float32(1.0)
    77  		intVal        = int(1)
    78  		int64Val      = int64(1)
    79  		int32Val      = int32(1)
    80  		int16Val      = int16(1)
    81  		int8Val       = int8(1)
    82  		stringVal     = string("hello")
    83  		timeVal       = time.Unix(100000, 0)
    84  		uintVal       = uint(1)
    85  		uint64Val     = uint64(1)
    86  		uint32Val     = uint32(1)
    87  		uint16Val     = uint16(1)
    88  		uint8Val      = uint8(1)
    89  		uintptrVal    = uintptr(1)
    90  		nilErr        error
    91  	)
    92  
    93  	tests := []struct {
    94  		name   string
    95  		field  Field
    96  		expect Field
    97  	}{
    98  		{"Skip", Field{Type: zapcore.SkipType}, Skip()},
    99  		{"Binary", Field{Key: "k", Type: zapcore.BinaryType, Interface: []byte("ab12")}, Binary("k", []byte("ab12"))},
   100  		{"Bool", Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)},
   101  		{"Bool", Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)},
   102  		{"ByteString", Field{Key: "k", Type: zapcore.ByteStringType, Interface: []byte("ab12")}, ByteString("k", []byte("ab12"))},
   103  		{"Complex128", Field{Key: "k", Type: zapcore.Complex128Type, Interface: 1 + 2i}, Complex128("k", 1+2i)},
   104  		{"Complex64", Field{Key: "k", Type: zapcore.Complex64Type, Interface: complex64(1 + 2i)}, Complex64("k", 1+2i)},
   105  		{"Duration", Field{Key: "k", Type: zapcore.DurationType, Integer: 1}, Duration("k", 1)},
   106  		{"Int", Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int("k", 1)},
   107  		{"Int64", Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int64("k", 1)},
   108  		{"Int32", Field{Key: "k", Type: zapcore.Int32Type, Integer: 1}, Int32("k", 1)},
   109  		{"Int16", Field{Key: "k", Type: zapcore.Int16Type, Integer: 1}, Int16("k", 1)},
   110  		{"Int8", Field{Key: "k", Type: zapcore.Int8Type, Integer: 1}, Int8("k", 1)},
   111  		{"String", Field{Key: "k", Type: zapcore.StringType, String: "foo"}, String("k", "foo")},
   112  		{"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: 0, Interface: time.UTC}, Time("k", time.Unix(0, 0).In(time.UTC))},
   113  		{"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: 1000, Interface: time.UTC}, Time("k", time.Unix(0, 1000).In(time.UTC))},
   114  		{"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: math.MinInt64, Interface: time.UTC}, Time("k", time.Unix(0, math.MinInt64).In(time.UTC))},
   115  		{"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: math.MaxInt64, Interface: time.UTC}, Time("k", time.Unix(0, math.MaxInt64).In(time.UTC))},
   116  		{"Time", Field{Key: "k", Type: zapcore.TimeFullType, Interface: time.Time{}}, Time("k", time.Time{})},
   117  		{"Time", Field{Key: "k", Type: zapcore.TimeFullType, Interface: time.Unix(math.MaxInt64, 0)}, Time("k", time.Unix(math.MaxInt64, 0))},
   118  		{"Uint", Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint("k", 1)},
   119  		{"Uint64", Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint64("k", 1)},
   120  		{"Uint32", Field{Key: "k", Type: zapcore.Uint32Type, Integer: 1}, Uint32("k", 1)},
   121  		{"Uint16", Field{Key: "k", Type: zapcore.Uint16Type, Integer: 1}, Uint16("k", 1)},
   122  		{"Uint8", Field{Key: "k", Type: zapcore.Uint8Type, Integer: 1}, Uint8("k", 1)},
   123  		{"Uintptr", Field{Key: "k", Type: zapcore.UintptrType, Integer: 10}, Uintptr("k", 0xa)},
   124  		{"Reflect", Field{Key: "k", Type: zapcore.ReflectType, Interface: ints}, Reflect("k", ints)},
   125  		{"Reflect", Field{Key: "k", Type: zapcore.ReflectType}, Reflect("k", nil)},
   126  		{"Stringer", Field{Key: "k", Type: zapcore.StringerType, Interface: addr}, Stringer("k", addr)},
   127  		{"Object", Field{Key: "k", Type: zapcore.ObjectMarshalerType, Interface: name}, Object("k", name)},
   128  		{"Inline", Field{Type: zapcore.InlineMarshalerType, Interface: name}, Inline(name)},
   129  		{"Any:ObjectMarshaler", Any("k", name), Object("k", name)},
   130  		{"Any:ArrayMarshaler", Any("k", bools([]bool{true})), Array("k", bools([]bool{true}))},
   131  		{"Any:Dict", Any("k", []Field{String("k", "v")}), Dict("k", String("k", "v"))},
   132  		{"Any:Stringer", Any("k", addr), Stringer("k", addr)},
   133  		{"Any:Bool", Any("k", true), Bool("k", true)},
   134  		{"Any:Bools", Any("k", []bool{true}), Bools("k", []bool{true})},
   135  		{"Any:Byte", Any("k", byte(1)), Uint8("k", 1)},
   136  		{"Any:Bytes", Any("k", []byte{1}), Binary("k", []byte{1})},
   137  		{"Any:Complex128", Any("k", 1+2i), Complex128("k", 1+2i)},
   138  		{"Any:Complex128s", Any("k", []complex128{1 + 2i}), Complex128s("k", []complex128{1 + 2i})},
   139  		{"Any:Complex64", Any("k", complex64(1+2i)), Complex64("k", 1+2i)},
   140  		{"Any:Complex64s", Any("k", []complex64{1 + 2i}), Complex64s("k", []complex64{1 + 2i})},
   141  		{"Any:Float64", Any("k", 3.14), Float64("k", 3.14)},
   142  		{"Any:Float64s", Any("k", []float64{3.14}), Float64s("k", []float64{3.14})},
   143  		{"Any:Float32", Any("k", float32(3.14)), Float32("k", 3.14)},
   144  		{"Any:Float32s", Any("k", []float32{3.14}), Float32s("k", []float32{3.14})},
   145  		{"Any:Int", Any("k", 1), Int("k", 1)},
   146  		{"Any:Ints", Any("k", []int{1}), Ints("k", []int{1})},
   147  		{"Any:Int64", Any("k", int64(1)), Int64("k", 1)},
   148  		{"Any:Int64s", Any("k", []int64{1}), Int64s("k", []int64{1})},
   149  		{"Any:Int32", Any("k", int32(1)), Int32("k", 1)},
   150  		{"Any:Int32s", Any("k", []int32{1}), Int32s("k", []int32{1})},
   151  		{"Any:Int16", Any("k", int16(1)), Int16("k", 1)},
   152  		{"Any:Int16s", Any("k", []int16{1}), Int16s("k", []int16{1})},
   153  		{"Any:Int8", Any("k", int8(1)), Int8("k", 1)},
   154  		{"Any:Int8s", Any("k", []int8{1}), Int8s("k", []int8{1})},
   155  		{"Any:Rune", Any("k", rune(1)), Int32("k", 1)},
   156  		{"Any:Runes", Any("k", []rune{1}), Int32s("k", []int32{1})},
   157  		{"Any:String", Any("k", "v"), String("k", "v")},
   158  		{"Any:Strings", Any("k", []string{"v"}), Strings("k", []string{"v"})},
   159  		{"Any:Uint", Any("k", uint(1)), Uint("k", 1)},
   160  		{"Any:Uints", Any("k", []uint{1}), Uints("k", []uint{1})},
   161  		{"Any:Uint64", Any("k", uint64(1)), Uint64("k", 1)},
   162  		{"Any:Uint64s", Any("k", []uint64{1}), Uint64s("k", []uint64{1})},
   163  		{"Any:Uint32", Any("k", uint32(1)), Uint32("k", 1)},
   164  		{"Any:Uint32s", Any("k", []uint32{1}), Uint32s("k", []uint32{1})},
   165  		{"Any:Uint16", Any("k", uint16(1)), Uint16("k", 1)},
   166  		{"Any:Uint16s", Any("k", []uint16{1}), Uint16s("k", []uint16{1})},
   167  		{"Any:Uint8", Any("k", uint8(1)), Uint8("k", 1)},
   168  		{"Any:Uint8s", Any("k", []uint8{1}), Binary("k", []uint8{1})},
   169  		{"Any:Uintptr", Any("k", uintptr(1)), Uintptr("k", 1)},
   170  		{"Any:Uintptrs", Any("k", []uintptr{1}), Uintptrs("k", []uintptr{1})},
   171  		{"Any:Time", Any("k", time.Unix(0, 0)), Time("k", time.Unix(0, 0))},
   172  		{"Any:TimeFullType", Any("k", time.Time{}), Time("k", time.Time{})},
   173  		{"Any:Times", Any("k", []time.Time{time.Unix(0, 0)}), Times("k", []time.Time{time.Unix(0, 0)})},
   174  		{"Any:Duration", Any("k", time.Second), Duration("k", time.Second)},
   175  		{"Any:Durations", Any("k", []time.Duration{time.Second}), Durations("k", []time.Duration{time.Second})},
   176  		{"Any:Fallback", Any("k", struct{}{}), Reflect("k", struct{}{})},
   177  		{"Ptr:Bool", Boolp("k", nil), nilField("k")},
   178  		{"Ptr:Bool", Boolp("k", &boolVal), Bool("k", boolVal)},
   179  		{"Any:PtrBool", Any("k", (*bool)(nil)), nilField("k")},
   180  		{"Any:PtrBool", Any("k", &boolVal), Bool("k", boolVal)},
   181  		{"Ptr:Complex128", Complex128p("k", nil), nilField("k")},
   182  		{"Ptr:Complex128", Complex128p("k", &complex128Val), Complex128("k", complex128Val)},
   183  		{"Any:PtrComplex128", Any("k", (*complex128)(nil)), nilField("k")},
   184  		{"Any:PtrComplex128", Any("k", &complex128Val), Complex128("k", complex128Val)},
   185  		{"Ptr:Complex64", Complex64p("k", nil), nilField("k")},
   186  		{"Ptr:Complex64", Complex64p("k", &complex64Val), Complex64("k", complex64Val)},
   187  		{"Any:PtrComplex64", Any("k", (*complex64)(nil)), nilField("k")},
   188  		{"Any:PtrComplex64", Any("k", &complex64Val), Complex64("k", complex64Val)},
   189  		{"Ptr:Duration", Durationp("k", nil), nilField("k")},
   190  		{"Ptr:Duration", Durationp("k", &durationVal), Duration("k", durationVal)},
   191  		{"Any:PtrDuration", Any("k", (*time.Duration)(nil)), nilField("k")},
   192  		{"Any:PtrDuration", Any("k", &durationVal), Duration("k", durationVal)},
   193  		{"Ptr:Float64", Float64p("k", nil), nilField("k")},
   194  		{"Ptr:Float64", Float64p("k", &float64Val), Float64("k", float64Val)},
   195  		{"Any:PtrFloat64", Any("k", (*float64)(nil)), nilField("k")},
   196  		{"Any:PtrFloat64", Any("k", &float64Val), Float64("k", float64Val)},
   197  		{"Ptr:Float32", Float32p("k", nil), nilField("k")},
   198  		{"Ptr:Float32", Float32p("k", &float32Val), Float32("k", float32Val)},
   199  		{"Any:PtrFloat32", Any("k", (*float32)(nil)), nilField("k")},
   200  		{"Any:PtrFloat32", Any("k", &float32Val), Float32("k", float32Val)},
   201  		{"Ptr:Int", Intp("k", nil), nilField("k")},
   202  		{"Ptr:Int", Intp("k", &intVal), Int("k", intVal)},
   203  		{"Any:PtrInt", Any("k", (*int)(nil)), nilField("k")},
   204  		{"Any:PtrInt", Any("k", &intVal), Int("k", intVal)},
   205  		{"Ptr:Int64", Int64p("k", nil), nilField("k")},
   206  		{"Ptr:Int64", Int64p("k", &int64Val), Int64("k", int64Val)},
   207  		{"Any:PtrInt64", Any("k", (*int64)(nil)), nilField("k")},
   208  		{"Any:PtrInt64", Any("k", &int64Val), Int64("k", int64Val)},
   209  		{"Ptr:Int32", Int32p("k", nil), nilField("k")},
   210  		{"Ptr:Int32", Int32p("k", &int32Val), Int32("k", int32Val)},
   211  		{"Any:PtrInt32", Any("k", (*int32)(nil)), nilField("k")},
   212  		{"Any:PtrInt32", Any("k", &int32Val), Int32("k", int32Val)},
   213  		{"Ptr:Int16", Int16p("k", nil), nilField("k")},
   214  		{"Ptr:Int16", Int16p("k", &int16Val), Int16("k", int16Val)},
   215  		{"Any:PtrInt16", Any("k", (*int16)(nil)), nilField("k")},
   216  		{"Any:PtrInt16", Any("k", &int16Val), Int16("k", int16Val)},
   217  		{"Ptr:Int8", Int8p("k", nil), nilField("k")},
   218  		{"Ptr:Int8", Int8p("k", &int8Val), Int8("k", int8Val)},
   219  		{"Any:PtrInt8", Any("k", (*int8)(nil)), nilField("k")},
   220  		{"Any:PtrInt8", Any("k", &int8Val), Int8("k", int8Val)},
   221  		{"Ptr:String", Stringp("k", nil), nilField("k")},
   222  		{"Ptr:String", Stringp("k", &stringVal), String("k", stringVal)},
   223  		{"Any:PtrString", Any("k", (*string)(nil)), nilField("k")},
   224  		{"Any:PtrString", Any("k", &stringVal), String("k", stringVal)},
   225  		{"Ptr:Time", Timep("k", nil), nilField("k")},
   226  		{"Ptr:Time", Timep("k", &timeVal), Time("k", timeVal)},
   227  		{"Any:PtrTime", Any("k", (*time.Time)(nil)), nilField("k")},
   228  		{"Any:PtrTime", Any("k", &timeVal), Time("k", timeVal)},
   229  		{"Any:PtrTimeFullType", Any("k", &time.Time{}), Time("k", time.Time{})},
   230  		{"Ptr:Uint", Uintp("k", nil), nilField("k")},
   231  		{"Ptr:Uint", Uintp("k", &uintVal), Uint("k", uintVal)},
   232  		{"Any:PtrUint", Any("k", (*uint)(nil)), nilField("k")},
   233  		{"Any:PtrUint", Any("k", &uintVal), Uint("k", uintVal)},
   234  		{"Ptr:Uint64", Uint64p("k", nil), nilField("k")},
   235  		{"Ptr:Uint64", Uint64p("k", &uint64Val), Uint64("k", uint64Val)},
   236  		{"Any:PtrUint64", Any("k", (*uint64)(nil)), nilField("k")},
   237  		{"Any:PtrUint64", Any("k", &uint64Val), Uint64("k", uint64Val)},
   238  		{"Ptr:Uint32", Uint32p("k", nil), nilField("k")},
   239  		{"Ptr:Uint32", Uint32p("k", &uint32Val), Uint32("k", uint32Val)},
   240  		{"Any:PtrUint32", Any("k", (*uint32)(nil)), nilField("k")},
   241  		{"Any:PtrUint32", Any("k", &uint32Val), Uint32("k", uint32Val)},
   242  		{"Ptr:Uint16", Uint16p("k", nil), nilField("k")},
   243  		{"Ptr:Uint16", Uint16p("k", &uint16Val), Uint16("k", uint16Val)},
   244  		{"Any:PtrUint16", Any("k", (*uint16)(nil)), nilField("k")},
   245  		{"Any:PtrUint16", Any("k", &uint16Val), Uint16("k", uint16Val)},
   246  		{"Ptr:Uint8", Uint8p("k", nil), nilField("k")},
   247  		{"Ptr:Uint8", Uint8p("k", &uint8Val), Uint8("k", uint8Val)},
   248  		{"Any:PtrUint8", Any("k", (*uint8)(nil)), nilField("k")},
   249  		{"Any:PtrUint8", Any("k", &uint8Val), Uint8("k", uint8Val)},
   250  		{"Ptr:Uintptr", Uintptrp("k", nil), nilField("k")},
   251  		{"Ptr:Uintptr", Uintptrp("k", &uintptrVal), Uintptr("k", uintptrVal)},
   252  		{"Any:PtrUintptr", Any("k", (*uintptr)(nil)), nilField("k")},
   253  		{"Any:PtrUintptr", Any("k", &uintptrVal), Uintptr("k", uintptrVal)},
   254  		{"Any:ErrorNil", Any("k", nilErr), nilField("k")},
   255  		{"Namespace", Namespace("k"), Field{Key: "k", Type: zapcore.NamespaceType}},
   256  	}
   257  
   258  	for _, tt := range tests {
   259  		t.Run(tt.name, func(t *testing.T) {
   260  			if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor") {
   261  				t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface)
   262  			}
   263  			assertCanBeReused(t, tt.field)
   264  		})
   265  	}
   266  }
   267  
   268  func TestStackField(t *testing.T) {
   269  	f := Stack("stacktrace")
   270  	assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
   271  	assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
   272  	r := regexp.MustCompile(`field_test.go:(\d+)`)
   273  	assert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), "field_test.go"), r.ReplaceAllString(f.String, "field_test.go"), "Unexpected stack trace")
   274  	assertCanBeReused(t, f)
   275  }
   276  
   277  func TestStackSkipField(t *testing.T) {
   278  	f := StackSkip("stacktrace", 0)
   279  	assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
   280  	assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
   281  	r := regexp.MustCompile(`field_test.go:(\d+)`)
   282  	assert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), "field_test.go"), r.ReplaceAllString(f.String, "field_test.go"), f.String, "Unexpected stack trace")
   283  	assertCanBeReused(t, f)
   284  }
   285  
   286  func TestStackSkipFieldWithSkip(t *testing.T) {
   287  	f := StackSkip("stacktrace", 1)
   288  	assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
   289  	assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
   290  	assert.Equal(t, stacktrace.Take(1), f.String, "Unexpected stack trace")
   291  	assertCanBeReused(t, f)
   292  }
   293  
   294  func TestDict(t *testing.T) {
   295  	tests := []struct {
   296  		desc     string
   297  		field    Field
   298  		expected any
   299  	}{
   300  		{"empty", Dict(""), map[string]any{}},
   301  		{"single", Dict("", String("k", "v")), map[string]any{"k": "v"}},
   302  		{"multiple", Dict("", String("k", "v"), String("k2", "v2")), map[string]any{"k": "v", "k2": "v2"}},
   303  	}
   304  
   305  	for _, tt := range tests {
   306  		t.Run(tt.desc, func(t *testing.T) {
   307  			enc := zapcore.NewMapObjectEncoder()
   308  			tt.field.Key = "k"
   309  			tt.field.AddTo(enc)
   310  			assert.Equal(t, tt.expected, enc.Fields["k"], "unexpected map contents")
   311  			assert.Len(t, enc.Fields, 1, "found extra keys in map: %v", enc.Fields)
   312  
   313  			assertCanBeReused(t, tt.field)
   314  		})
   315  	}
   316  }
   317  

View as plain text