planetscale 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.buildkite/pipeline.yml +21 -0
- data/.licenses/go/github.com/planetscale/planetscale-go/planetscale.dep.yml +56 -3
- data/.licenses/go/github.com/planetscale/sql-proxy/proxy.dep.yml +205 -172
- data/.licenses/go/go.uber.org/zap.dep.yml +1 -1
- data/.licenses/go/go.uber.org/zap/buffer.dep.yml +1 -1
- data/.licenses/go/go.uber.org/zap/internal/bufferpool.dep.yml +1 -1
- data/.licenses/go/go.uber.org/zap/internal/color.dep.yml +1 -1
- data/.licenses/go/go.uber.org/zap/internal/exit.dep.yml +1 -1
- data/.licenses/go/go.uber.org/zap/zapcore.dep.yml +1 -1
- data/README.md +8 -7
- data/controller.go +23 -10
- data/go.mod +3 -3
- data/go.sum +22 -23
- data/lib/generators/planetscale/install_generator.rb +5 -5
- data/lib/planetscale.rb +12 -12
- data/lib/planetscale/version.rb +1 -1
- data/planetscale.gemspec +1 -1
- data/proxy/planetscale-darwin.so +0 -0
- data/proxy/planetscale-linux.so +0 -0
- data/vendor/github.com/planetscale/planetscale-go/LICENSE +202 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/audit_logs.go +136 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/branches.go +5 -3
- data/vendor/github.com/planetscale/planetscale-go/planetscale/certs.go +40 -16
- data/vendor/github.com/planetscale/planetscale-go/planetscale/client.go +13 -6
- data/vendor/github.com/planetscale/planetscale-go/planetscale/databases.go +2 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/regions.go +52 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/service_tokens.go +1 -1
- data/vendor/github.com/planetscale/sql-proxy/LICENSE +202 -0
- data/vendor/github.com/planetscale/sql-proxy/proxy/client.go +17 -8
- data/vendor/go.uber.org/zap/CHANGELOG.md +60 -0
- data/vendor/go.uber.org/zap/CONTRIBUTING.md +0 -6
- data/vendor/go.uber.org/zap/FAQ.md +8 -0
- data/vendor/go.uber.org/zap/Makefile +13 -3
- data/vendor/go.uber.org/zap/README.md +4 -4
- data/vendor/go.uber.org/zap/buffer/buffer.go +18 -0
- data/vendor/go.uber.org/zap/field.go +10 -0
- data/vendor/go.uber.org/zap/go.mod +7 -6
- data/vendor/go.uber.org/zap/go.sum +25 -27
- data/vendor/go.uber.org/zap/http_handler.go +75 -24
- data/vendor/go.uber.org/zap/logger.go +11 -7
- data/vendor/go.uber.org/zap/options.go +8 -0
- data/vendor/go.uber.org/zap/sugar.go +21 -10
- data/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go +188 -0
- data/vendor/go.uber.org/zap/zapcore/clock.go +50 -0
- data/vendor/go.uber.org/zap/zapcore/console_encoder.go +1 -1
- data/vendor/go.uber.org/zap/zapcore/entry.go +2 -2
- data/vendor/go.uber.org/zap/zapcore/error.go +18 -1
- data/vendor/go.uber.org/zap/zapcore/field.go +7 -1
- data/vendor/go.uber.org/zap/zapcore/write_syncer.go +1 -2
- data/vendor/modules.txt +3 -3
- metadata +11 -5
- data/vendor/go.uber.org/zap/.travis.yml +0 -23
@@ -26,7 +26,6 @@ import (
|
|
26
26
|
"os"
|
27
27
|
"runtime"
|
28
28
|
"strings"
|
29
|
-
"time"
|
30
29
|
|
31
30
|
"go.uber.org/zap/zapcore"
|
32
31
|
)
|
@@ -42,14 +41,17 @@ type Logger struct {
|
|
42
41
|
core zapcore.Core
|
43
42
|
|
44
43
|
development bool
|
44
|
+
addCaller bool
|
45
|
+
onFatal zapcore.CheckWriteAction // default is WriteThenFatal
|
46
|
+
|
45
47
|
name string
|
46
48
|
errorOutput zapcore.WriteSyncer
|
47
49
|
|
48
|
-
|
49
|
-
addStack zapcore.LevelEnabler
|
50
|
+
addStack zapcore.LevelEnabler
|
50
51
|
|
51
52
|
callerSkip int
|
52
|
-
|
53
|
+
|
54
|
+
clock zapcore.Clock
|
53
55
|
}
|
54
56
|
|
55
57
|
// New constructs a new Logger from the provided zapcore.Core and Options. If
|
@@ -70,6 +72,7 @@ func New(core zapcore.Core, options ...Option) *Logger {
|
|
70
72
|
core: core,
|
71
73
|
errorOutput: zapcore.Lock(os.Stderr),
|
72
74
|
addStack: zapcore.FatalLevel + 1,
|
75
|
+
clock: zapcore.DefaultClock,
|
73
76
|
}
|
74
77
|
return log.WithOptions(options...)
|
75
78
|
}
|
@@ -84,6 +87,7 @@ func NewNop() *Logger {
|
|
84
87
|
core: zapcore.NewNopCore(),
|
85
88
|
errorOutput: zapcore.AddSync(ioutil.Discard),
|
86
89
|
addStack: zapcore.FatalLevel + 1,
|
90
|
+
clock: zapcore.DefaultClock,
|
87
91
|
}
|
88
92
|
}
|
89
93
|
|
@@ -269,7 +273,7 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
|
|
269
273
|
// log message will actually be written somewhere.
|
270
274
|
ent := zapcore.Entry{
|
271
275
|
LoggerName: log.name,
|
272
|
-
Time:
|
276
|
+
Time: log.clock.Now(),
|
273
277
|
Level: lvl,
|
274
278
|
Message: msg,
|
275
279
|
}
|
@@ -306,7 +310,7 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
|
|
306
310
|
if log.addCaller {
|
307
311
|
frame, defined := getCallerFrame(log.callerSkip + callerSkipOffset)
|
308
312
|
if !defined {
|
309
|
-
fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n",
|
313
|
+
fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC())
|
310
314
|
log.errorOutput.Sync()
|
311
315
|
}
|
312
316
|
|
@@ -334,7 +338,7 @@ func getCallerFrame(skip int) (frame runtime.Frame, ok bool) {
|
|
334
338
|
const skipOffset = 2 // skip getCallerFrame and Callers
|
335
339
|
|
336
340
|
pc := make([]uintptr, 1)
|
337
|
-
numFrames := runtime.Callers(skip+skipOffset, pc
|
341
|
+
numFrames := runtime.Callers(skip+skipOffset, pc)
|
338
342
|
if numFrames < 1 {
|
339
343
|
return
|
340
344
|
}
|
@@ -138,3 +138,11 @@ func OnFatal(action zapcore.CheckWriteAction) Option {
|
|
138
138
|
log.onFatal = action
|
139
139
|
})
|
140
140
|
}
|
141
|
+
|
142
|
+
// WithClock specifies the clock used by the logger to determine the current
|
143
|
+
// time for logged entries. Defaults to the system clock with time.Now.
|
144
|
+
func WithClock(clock zapcore.Clock) Option {
|
145
|
+
return optionFunc(func(log *Logger) {
|
146
|
+
log.clock = clock
|
147
|
+
})
|
148
|
+
}
|
@@ -222,19 +222,30 @@ func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interf
|
|
222
222
|
return
|
223
223
|
}
|
224
224
|
|
225
|
-
|
226
|
-
msg := template
|
227
|
-
if msg == "" && len(fmtArgs) > 0 {
|
228
|
-
msg = fmt.Sprint(fmtArgs...)
|
229
|
-
} else if msg != "" && len(fmtArgs) > 0 {
|
230
|
-
msg = fmt.Sprintf(template, fmtArgs...)
|
231
|
-
}
|
232
|
-
|
225
|
+
msg := getMessage(template, fmtArgs)
|
233
226
|
if ce := s.base.Check(lvl, msg); ce != nil {
|
234
227
|
ce.Write(s.sweetenFields(context)...)
|
235
228
|
}
|
236
229
|
}
|
237
230
|
|
231
|
+
// getMessage format with Sprint, Sprintf, or neither.
|
232
|
+
func getMessage(template string, fmtArgs []interface{}) string {
|
233
|
+
if len(fmtArgs) == 0 {
|
234
|
+
return template
|
235
|
+
}
|
236
|
+
|
237
|
+
if template != "" {
|
238
|
+
return fmt.Sprintf(template, fmtArgs...)
|
239
|
+
}
|
240
|
+
|
241
|
+
if len(fmtArgs) == 1 {
|
242
|
+
if str, ok := fmtArgs[0].(string); ok {
|
243
|
+
return str
|
244
|
+
}
|
245
|
+
}
|
246
|
+
return fmt.Sprint(fmtArgs...)
|
247
|
+
}
|
248
|
+
|
238
249
|
func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
|
239
250
|
if len(args) == 0 {
|
240
251
|
return nil
|
@@ -255,7 +266,7 @@ func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
|
|
255
266
|
|
256
267
|
// Make sure this element isn't a dangling key.
|
257
268
|
if i == len(args)-1 {
|
258
|
-
s.base.
|
269
|
+
s.base.Error(_oddNumberErrMsg, Any("ignored", args[i]))
|
259
270
|
break
|
260
271
|
}
|
261
272
|
|
@@ -276,7 +287,7 @@ func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
|
|
276
287
|
|
277
288
|
// If we encountered any invalid key-value pairs, log an error.
|
278
289
|
if len(invalid) > 0 {
|
279
|
-
s.base.
|
290
|
+
s.base.Error(_nonStringKeyErrMsg, Array("invalid", invalid))
|
280
291
|
}
|
281
292
|
return fields
|
282
293
|
}
|
@@ -0,0 +1,188 @@
|
|
1
|
+
// Copyright (c) 2021 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 zapcore
|
22
|
+
|
23
|
+
import (
|
24
|
+
"bufio"
|
25
|
+
"sync"
|
26
|
+
"time"
|
27
|
+
|
28
|
+
"go.uber.org/multierr"
|
29
|
+
)
|
30
|
+
|
31
|
+
const (
|
32
|
+
// _defaultBufferSize specifies the default size used by Buffer.
|
33
|
+
_defaultBufferSize = 256 * 1024 // 256 kB
|
34
|
+
|
35
|
+
// _defaultFlushInterval specifies the default flush interval for
|
36
|
+
// Buffer.
|
37
|
+
_defaultFlushInterval = 30 * time.Second
|
38
|
+
)
|
39
|
+
|
40
|
+
// A BufferedWriteSyncer is a WriteSyncer that buffers writes in-memory before
|
41
|
+
// flushing them to a wrapped WriteSyncer after reaching some limit, or at some
|
42
|
+
// fixed interval--whichever comes first.
|
43
|
+
//
|
44
|
+
// BufferedWriteSyncer is safe for concurrent use. You don't need to use
|
45
|
+
// zapcore.Lock for WriteSyncers with BufferedWriteSyncer.
|
46
|
+
type BufferedWriteSyncer struct {
|
47
|
+
// WS is the WriteSyncer around which BufferedWriteSyncer will buffer
|
48
|
+
// writes.
|
49
|
+
//
|
50
|
+
// This field is required.
|
51
|
+
WS WriteSyncer
|
52
|
+
|
53
|
+
// Size specifies the maximum amount of data the writer will buffered
|
54
|
+
// before flushing.
|
55
|
+
//
|
56
|
+
// Defaults to 256 kB if unspecified.
|
57
|
+
Size int
|
58
|
+
|
59
|
+
// FlushInterval specifies how often the writer should flush data if
|
60
|
+
// there have been no writes.
|
61
|
+
//
|
62
|
+
// Defaults to 30 seconds if unspecified.
|
63
|
+
FlushInterval time.Duration
|
64
|
+
|
65
|
+
// Clock, if specified, provides control of the source of time for the
|
66
|
+
// writer.
|
67
|
+
//
|
68
|
+
// Defaults to the system clock.
|
69
|
+
Clock Clock
|
70
|
+
|
71
|
+
// unexported fields for state
|
72
|
+
mu sync.Mutex
|
73
|
+
initialized bool // whether initialize() has run
|
74
|
+
writer *bufio.Writer
|
75
|
+
ticker *time.Ticker
|
76
|
+
stop chan struct{} // closed when flushLoop should stop
|
77
|
+
stopped bool // whether Stop() has run
|
78
|
+
done chan struct{} // closed when flushLoop has stopped
|
79
|
+
}
|
80
|
+
|
81
|
+
func (s *BufferedWriteSyncer) initialize() {
|
82
|
+
size := s.Size
|
83
|
+
if size == 0 {
|
84
|
+
size = _defaultBufferSize
|
85
|
+
}
|
86
|
+
|
87
|
+
flushInterval := s.FlushInterval
|
88
|
+
if flushInterval == 0 {
|
89
|
+
flushInterval = _defaultFlushInterval
|
90
|
+
}
|
91
|
+
|
92
|
+
if s.Clock == nil {
|
93
|
+
s.Clock = DefaultClock
|
94
|
+
}
|
95
|
+
|
96
|
+
s.ticker = s.Clock.NewTicker(flushInterval)
|
97
|
+
s.writer = bufio.NewWriterSize(s.WS, size)
|
98
|
+
s.stop = make(chan struct{})
|
99
|
+
s.done = make(chan struct{})
|
100
|
+
s.initialized = true
|
101
|
+
go s.flushLoop()
|
102
|
+
}
|
103
|
+
|
104
|
+
// Write writes log data into buffer syncer directly, multiple Write calls will be batched,
|
105
|
+
// and log data will be flushed to disk when the buffer is full or periodically.
|
106
|
+
func (s *BufferedWriteSyncer) Write(bs []byte) (int, error) {
|
107
|
+
s.mu.Lock()
|
108
|
+
defer s.mu.Unlock()
|
109
|
+
|
110
|
+
if !s.initialized {
|
111
|
+
s.initialize()
|
112
|
+
}
|
113
|
+
|
114
|
+
// To avoid partial writes from being flushed, we manually flush the existing buffer if:
|
115
|
+
// * The current write doesn't fit into the buffer fully, and
|
116
|
+
// * The buffer is not empty (since bufio will not split large writes when the buffer is empty)
|
117
|
+
if len(bs) > s.writer.Available() && s.writer.Buffered() > 0 {
|
118
|
+
if err := s.writer.Flush(); err != nil {
|
119
|
+
return 0, err
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
return s.writer.Write(bs)
|
124
|
+
}
|
125
|
+
|
126
|
+
// Sync flushes buffered log data into disk directly.
|
127
|
+
func (s *BufferedWriteSyncer) Sync() error {
|
128
|
+
s.mu.Lock()
|
129
|
+
defer s.mu.Unlock()
|
130
|
+
|
131
|
+
var err error
|
132
|
+
if s.initialized {
|
133
|
+
err = s.writer.Flush()
|
134
|
+
}
|
135
|
+
|
136
|
+
return multierr.Append(err, s.WS.Sync())
|
137
|
+
}
|
138
|
+
|
139
|
+
// flushLoop flushes the buffer at the configured interval until Stop is
|
140
|
+
// called.
|
141
|
+
func (s *BufferedWriteSyncer) flushLoop() {
|
142
|
+
defer close(s.done)
|
143
|
+
|
144
|
+
for {
|
145
|
+
select {
|
146
|
+
case <-s.ticker.C:
|
147
|
+
// we just simply ignore error here
|
148
|
+
// because the underlying bufio writer stores any errors
|
149
|
+
// and we return any error from Sync() as part of the close
|
150
|
+
_ = s.Sync()
|
151
|
+
case <-s.stop:
|
152
|
+
return
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
// Stop closes the buffer, cleans up background goroutines, and flushes
|
158
|
+
// remaining unwritten data.
|
159
|
+
func (s *BufferedWriteSyncer) Stop() (err error) {
|
160
|
+
var stopped bool
|
161
|
+
|
162
|
+
// Critical section.
|
163
|
+
func() {
|
164
|
+
s.mu.Lock()
|
165
|
+
defer s.mu.Unlock()
|
166
|
+
|
167
|
+
if !s.initialized {
|
168
|
+
return
|
169
|
+
}
|
170
|
+
|
171
|
+
stopped = s.stopped
|
172
|
+
if stopped {
|
173
|
+
return
|
174
|
+
}
|
175
|
+
s.stopped = true
|
176
|
+
|
177
|
+
s.ticker.Stop()
|
178
|
+
close(s.stop) // tell flushLoop to stop
|
179
|
+
<-s.done // and wait until it has
|
180
|
+
}()
|
181
|
+
|
182
|
+
// Don't call Sync on consecutive Stops.
|
183
|
+
if !stopped {
|
184
|
+
err = s.Sync()
|
185
|
+
}
|
186
|
+
|
187
|
+
return err
|
188
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
// Copyright (c) 2021 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 zapcore
|
22
|
+
|
23
|
+
import (
|
24
|
+
"time"
|
25
|
+
)
|
26
|
+
|
27
|
+
// DefaultClock is the default clock used by Zap in operations that require
|
28
|
+
// time. This clock uses the system clock for all operations.
|
29
|
+
var DefaultClock = systemClock{}
|
30
|
+
|
31
|
+
// Clock is a source of time for logged entries.
|
32
|
+
type Clock interface {
|
33
|
+
// Now returns the current local time.
|
34
|
+
Now() time.Time
|
35
|
+
|
36
|
+
// NewTicker returns *time.Ticker that holds a channel
|
37
|
+
// that delivers "ticks" of a clock.
|
38
|
+
NewTicker(time.Duration) *time.Ticker
|
39
|
+
}
|
40
|
+
|
41
|
+
// systemClock implements default Clock that uses system time.
|
42
|
+
type systemClock struct{}
|
43
|
+
|
44
|
+
func (systemClock) Now() time.Time {
|
45
|
+
return time.Now()
|
46
|
+
}
|
47
|
+
|
48
|
+
func (systemClock) NewTicker(duration time.Duration) *time.Ticker {
|
49
|
+
return time.NewTicker(duration)
|
50
|
+
}
|
@@ -56,7 +56,7 @@ type consoleEncoder struct {
|
|
56
56
|
// encoder configuration, it will omit any element whose key is set to the empty
|
57
57
|
// string.
|
58
58
|
func NewConsoleEncoder(cfg EncoderConfig) Encoder {
|
59
|
-
if
|
59
|
+
if cfg.ConsoleSeparator == "" {
|
60
60
|
// Use a default delimiter of '\t' for backwards compatibility
|
61
61
|
cfg.ConsoleSeparator = "\t"
|
62
62
|
}
|
@@ -208,7 +208,7 @@ func (ce *CheckedEntry) Write(fields ...Field) {
|
|
208
208
|
// If the entry is dirty, log an internal error; because the
|
209
209
|
// CheckedEntry is being used after it was returned to the pool,
|
210
210
|
// the message may be an amalgamation from multiple call sites.
|
211
|
-
fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n",
|
211
|
+
fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry)
|
212
212
|
ce.ErrorOutput.Sync()
|
213
213
|
}
|
214
214
|
return
|
@@ -221,7 +221,7 @@ func (ce *CheckedEntry) Write(fields ...Field) {
|
|
221
221
|
}
|
222
222
|
if ce.ErrorOutput != nil {
|
223
223
|
if err != nil {
|
224
|
-
fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n",
|
224
|
+
fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err)
|
225
225
|
ce.ErrorOutput.Sync()
|
226
226
|
}
|
227
227
|
}
|
@@ -22,6 +22,7 @@ package zapcore
|
|
22
22
|
|
23
23
|
import (
|
24
24
|
"fmt"
|
25
|
+
"reflect"
|
25
26
|
"sync"
|
26
27
|
)
|
27
28
|
|
@@ -42,7 +43,23 @@ import (
|
|
42
43
|
// ...
|
43
44
|
// ],
|
44
45
|
// }
|
45
|
-
func encodeError(key string, err error, enc ObjectEncoder) error {
|
46
|
+
func encodeError(key string, err error, enc ObjectEncoder) (retErr error) {
|
47
|
+
// Try to capture panics (from nil references or otherwise) when calling
|
48
|
+
// the Error() method
|
49
|
+
defer func() {
|
50
|
+
if rerr := recover(); rerr != nil {
|
51
|
+
// If it's a nil pointer, just say "<nil>". The likeliest causes are a
|
52
|
+
// error that fails to guard against nil or a nil pointer for a
|
53
|
+
// value receiver, and in either case, "<nil>" is a nice result.
|
54
|
+
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
|
55
|
+
enc.AddString(key, "<nil>")
|
56
|
+
return
|
57
|
+
}
|
58
|
+
|
59
|
+
retErr = fmt.Errorf("PANIC=%v", rerr)
|
60
|
+
}
|
61
|
+
}()
|
62
|
+
|
46
63
|
basic := err.Error()
|
47
64
|
enc.AddString(key, basic)
|
48
65
|
|
@@ -92,6 +92,10 @@ const (
|
|
92
92
|
ErrorType
|
93
93
|
// SkipType indicates that the field is a no-op.
|
94
94
|
SkipType
|
95
|
+
|
96
|
+
// InlineMarshalerType indicates that the field carries an ObjectMarshaler
|
97
|
+
// that should be inlined.
|
98
|
+
InlineMarshalerType
|
95
99
|
)
|
96
100
|
|
97
101
|
// A Field is a marshaling operation used to add a key-value pair to a logger's
|
@@ -115,6 +119,8 @@ func (f Field) AddTo(enc ObjectEncoder) {
|
|
115
119
|
err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
|
116
120
|
case ObjectMarshalerType:
|
117
121
|
err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
|
122
|
+
case InlineMarshalerType:
|
123
|
+
err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc)
|
118
124
|
case BinaryType:
|
119
125
|
enc.AddBinary(f.Key, f.Interface.([]byte))
|
120
126
|
case BoolType:
|
@@ -167,7 +173,7 @@ func (f Field) AddTo(enc ObjectEncoder) {
|
|
167
173
|
case StringerType:
|
168
174
|
err = encodeStringer(f.Key, f.Interface, enc)
|
169
175
|
case ErrorType:
|
170
|
-
encodeError(f.Key, f.Interface.(error), enc)
|
176
|
+
err = encodeError(f.Key, f.Interface.(error), enc)
|
171
177
|
case SkipType:
|
172
178
|
break
|
173
179
|
default:
|