// Copyright 2020 Knative Authors // Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package logging import ( "github.com/davecgh/go-spew/spew" "go.uber.org/zap" "go.uber.org/zap/zapcore" "go.uber.org/multierr" ) const ( _oddNumberErrMsg = "Ignored key without a value." _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys." spewLevel1 = 2 spewLevel2 = 4 spewLevel3 = 6 ) var spewConfig *spew.ConfigState func init() { spewConfig = spew.NewDefaultConfig() spewConfig.DisableCapacities = true spewConfig.SortKeys = true spewConfig.SpewKeys = true spewConfig.ContinueOnMethod = true } func (o *TLogger) handleFields(args []interface{}) []zap.Field { if len(args) == 0 { return nil } s := o.l.Sugar() // Allocate enough space for the worst case; if users pass only structured // fields, we shouldn't penalize them with extra allocations. fields := make([]zap.Field, 0, len(args)) var invalid invalidPairs for i := 0; i < len(args); { // This is a strongly-typed field. Consume it and move on. if f, ok := args[i].(zap.Field); ok { fields = append(fields, f) i++ continue } // Make sure this element isn't a dangling key. if i == len(args)-1 { s.DPanic(_oddNumberErrMsg, zap.Any("ignored", args[i])) break } // Consume this value and the next, treating them as a key-value pair. If the // key isn't a string, add this pair to the slice of invalid pairs. key, val := args[i], args[i+1] if keyStr, ok := key.(string); !ok { // Subsequent errors are likely, so allocate once up front. if cap(invalid) == 0 { invalid = make(invalidPairs, 0, len(args)/2) } invalid = append(invalid, invalidPair{i, key, val}) } else { fields = append(fields, zap.Any(keyStr, val)) } i += 2 } // If we encountered any invalid key-value pairs, log an error. if len(invalid) > 0 { s.DPanic(_nonStringKeyErrMsg, zap.Array("invalid", invalid), zap.String("all_input", spew.Sprintf("%#+v", args))) } return fields } type invalidPair struct { position int key, value interface{} } func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddInt64("position", int64(p.position)) zap.Any("key", p.key).AddTo(enc) zap.Any("value", p.value).AddTo(enc) return nil } type invalidPairs []invalidPair func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error { var err error for i := range ps { err = multierr.Append(err, enc.AppendObject(ps[i])) } return err }