Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / go-logr / zapr / zapr.go
1 // Copyright 2018 Solly Ross
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // package zapr defines an implementation of the github.com/go-logr/logr
16 // interfaces built on top of Zap (go.uber.org/zap).
17 //
18 // Usage
19 //
20 // A new logr.Logger can be constructed from an existing zap.Logger using
21 // the NewLogger function:
22 //
23 //  log := zapr.NewLogger(someZapLogger)
24 //
25 // Implementation Details
26 //
27 // For the most part, concepts in Zap correspond directly with those in
28 // logr.
29 //
30 // Unlike Zap, all fields *must* be in the form of suggared fields --
31 // it's illegal to pass a strongly-typed Zap field in a key position
32 // to any of the log methods.
33 //
34 // Levels in logr correspond to custom debug levels in Zap.  Any given level
35 // in logr is represents by its inverse in zap (`zapLevel = -1*logrLevel`).
36 // For example V(2) is equivalent to log level -2 in Zap, while V(1) is
37 // equivalent to Zap's DebugLevel.
38 package zapr
39
40 import (
41         "github.com/go-logr/logr"
42         "go.uber.org/zap"
43         "go.uber.org/zap/zapcore"
44 )
45
46 // noopInfoLogger is a logr.InfoLogger that's always disabled, and does nothing.
47 type noopInfoLogger struct{}
48
49 func (l *noopInfoLogger) Enabled() bool                   { return false }
50 func (l *noopInfoLogger) Info(_ string, _ ...interface{}) {}
51
52 var disabledInfoLogger = &noopInfoLogger{}
53
54 // NB: right now, we always use the equivalent of sugared logging.
55 // This is necessary, since logr doesn't define non-suggared types,
56 // and using zap-specific non-suggared types would make uses tied
57 // directly to Zap.
58
59 // infoLogger is a logr.InfoLogger that uses Zap to log at a particular
60 // level.  The level has already been converted to a Zap level, which
61 // is to say that `logrLevel = -1*zapLevel`.
62 type infoLogger struct {
63         lvl zapcore.Level
64         l   *zap.Logger
65 }
66
67 func (l *infoLogger) Enabled() bool { return true }
68 func (l *infoLogger) Info(msg string, keysAndVals ...interface{}) {
69         if checkedEntry := l.l.Check(l.lvl, msg); checkedEntry != nil {
70                 checkedEntry.Write(handleFields(l.l, keysAndVals)...)
71         }
72 }
73
74 // zapLogger is a logr.Logger that uses Zap to log.
75 type zapLogger struct {
76         // NB: this looks very similar to zap.SugaredLogger, but
77         // deals with our desire to have multiple verbosity levels.
78         l *zap.Logger
79         infoLogger
80 }
81
82 // handleFields converts a bunch of arbitrary key-value pairs into Zap fields.  It takes
83 // additional pre-converted Zap fields, for use with automatically attached fields, like
84 // `error`.
85 func handleFields(l *zap.Logger, args []interface{}, additional ...zap.Field) []zap.Field {
86         // a slightly modified version of zap.SugaredLogger.sweetenFields
87         if len(args) == 0 {
88                 // fast-return if we have no suggared fields.
89                 return additional
90         }
91
92         // unlike Zap, we can be pretty sure users aren't passing structured
93         // fields (since logr has no concept of that), so guess that we need a
94         // little less space.
95         fields := make([]zap.Field, 0, len(args)/2+len(additional))
96         for i := 0; i < len(args); {
97                 // check just in case for strongly-typed Zap fields, which is illegal (since
98                 // it breaks implementation agnosticism), so we can give a better error message.
99                 if _, ok := args[i].(zap.Field); ok {
100                         l.DPanic("strongly-typed Zap Field passed to logr", zap.Any("zap field", args[i]))
101                         break
102                 }
103
104                 // make sure this isn't a mismatched key
105                 if i == len(args)-1 {
106                         l.DPanic("odd number of arguments passed as key-value pairs for logging", zap.Any("ignored key", args[i]))
107                         break
108                 }
109
110                 // process a key-value pair,
111                 // ensuring that the key is a string
112                 key, val := args[i], args[i+1]
113                 keyStr, isString := key.(string)
114                 if !isString {
115                         // if the key isn't a string, DPanic and stop logging
116                         l.DPanic("non-string key argument passed to logging, ignoring all later arguments", zap.Any("invalid key", key))
117                         break
118                 }
119
120                 fields = append(fields, zap.Any(keyStr, val))
121                 i += 2
122         }
123
124         return append(fields, additional...)
125 }
126
127 func (l *zapLogger) Error(err error, msg string, keysAndVals ...interface{}) {
128         if checkedEntry := l.l.Check(zap.ErrorLevel, msg); checkedEntry != nil {
129                 checkedEntry.Write(handleFields(l.l, keysAndVals, zap.Error(err))...)
130         }
131 }
132
133 func (l *zapLogger) V(level int) logr.InfoLogger {
134         lvl := zapcore.Level(-1 * level)
135         if l.l.Core().Enabled(lvl) {
136                 return &infoLogger{
137                         lvl: lvl,
138                         l:   l.l,
139                 }
140         }
141         return disabledInfoLogger
142 }
143
144 func (l *zapLogger) WithValues(keysAndValues ...interface{}) logr.Logger {
145         newLogger := l.l.With(handleFields(l.l, keysAndValues)...)
146         return NewLogger(newLogger)
147 }
148
149 func (l *zapLogger) WithName(name string) logr.Logger {
150         newLogger := l.l.Named(name)
151         return NewLogger(newLogger)
152 }
153
154 // NewLogger creates a new logr.Logger using the given Zap Logger to log.
155 func NewLogger(l *zap.Logger) logr.Logger {
156         return &zapLogger{
157                 l: l,
158                 infoLogger: infoLogger{
159                         l:   l,
160                         lvl: zap.InfoLevel,
161                 },
162         }
163 }