教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 聊聊golang的zap的error

聊聊golang的zap的error

发布时间:2022-02-21   编辑:jiaochengji.com
教程集为您提供聊聊golang的zap的error等资源,欢迎您收藏本站,我们将为您提供最新的聊聊golang的zap的error资源
<h2>序</h2>

本文主要研究一下golang的zap的error

<h2>error</h2>

zap@v1.16.0/error.go

<pre><code class="lang-go hljs">var _errArrayElemPool = sync.Pool{New: func() interface{} { return &errArrayElem{} }} // Error is shorthand for the common idiom NamedError("error", err). func Error(err error) Field { return NamedError("error", err) } // NamedError constructs a field that lazily stores err.Error() under the // provided key. Errors which also implement fmt.Formatter (like those produced // by github.com/pkg/errors) will also have their verbose representation stored // under key "Verbose". If passed a nil error, the field is a no-op. // // For the common case in which the key is simply "error", the Error function // is shorter and less repetitive. func NamedError(key string, err error) Field { if err == nil { return Skip() } return Field{Key: key, Type: zapcore.ErrorType, Interface: err} } type errArray []error func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { for i := range errs { if errs[i] == nil { continue } // To represent each error as an object with an "error" attribute and // potentially an "errorVerbose" attribute, we need to wrap it in a // type that implements LogObjectMarshaler. To prevent this from // allocating, pool the wrapper type. elem := _errArrayElemPool.Get().(*errArrayElem) elem.error = errs[i] arr.AppendObject(elem) elem.error = nil _errArrayElemPool.Put(elem) } return nil } type errArrayElem struct { error } func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error { // Re-use the error field's logic, which supports non-standard error types. Error(e.error).AddTo(enc) return nil }</code></code></pre><blockquote>Error方法使用NamedError创建err的Field;NamedError创建的fieldType为zapcore.ErrorType;errArray类型实现了ArrayMarshaler的MarshalLogArray方法;errArrayElem实现了ObjectMarshaler的MarshalLogObject方法;error.go定义了_errArrayElemPool,其pool的元素类型为errArrayElem</blockquote><h2>AddTo</h2>

zap@v1.16.0/zapcore/field.go

<pre><code class="lang-go hljs">func (f Field) AddTo(enc ObjectEncoder) { var err error switch f.Type { case ArrayMarshalerType: err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler)) case ObjectMarshalerType: err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler)) case BinaryType: enc.AddBinary(f.Key, f.Interface.([]byte)) case BoolType: enc.AddBool(f.Key, f.Integer == 1) case ByteStringType: enc.AddByteString(f.Key, f.Interface.([]byte)) case Complex128Type: enc.AddComplex128(f.Key, f.Interface.(complex128)) case Complex64Type: enc.AddComplex64(f.Key, f.Interface.(complex64)) case DurationType: enc.AddDuration(f.Key, time.Duration(f.Integer)) case Float64Type: enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer))) case Float32Type: enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer))) case Int64Type: enc.AddInt64(f.Key, f.Integer) case Int32Type: enc.AddInt32(f.Key, int32(f.Integer)) case Int16Type: enc.AddInt16(f.Key, int16(f.Integer)) case Int8Type: enc.AddInt8(f.Key, int8(f.Integer)) case StringType: enc.AddString(f.Key, f.String) case TimeType: if f.Interface != nil { enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location))) } else { // Fall back to UTC if location is nil. enc.AddTime(f.Key, time.Unix(0, f.Integer)) } case TimeFullType: enc.AddTime(f.Key, f.Interface.(time.Time)) case Uint64Type: enc.AddUint64(f.Key, uint64(f.Integer)) case Uint32Type: enc.AddUint32(f.Key, uint32(f.Integer)) case Uint16Type: enc.AddUint16(f.Key, uint16(f.Integer)) case Uint8Type: enc.AddUint8(f.Key, uint8(f.Integer)) case UintptrType: enc.AddUintptr(f.Key, uintptr(f.Integer)) case ReflectType: err = enc.AddReflected(f.Key, f.Interface) case NamespaceType: enc.OpenNamespace(f.Key) case StringerType: err = encodeStringer(f.Key, f.Interface, enc) case ErrorType: encodeError(f.Key, f.Interface.(error), enc) case SkipType: break default: panic(fmt.Sprintf("unknown field type: %v", f)) } if err != nil { enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error()) } }</code></code></pre><blockquote>AddTo方法针对ErrorType类型的Field执行的是encodeError</blockquote><h2>encodeError</h2>

zap@v1.16.0/zapcore/error.go

<pre><code class="lang-go hljs">func encodeError(key string, err error, enc ObjectEncoder) error { basic := err.Error() enc.AddString(key, basic) switch e := err.(type) { case errorGroup: return enc.AddArray(key "Causes", errArray(e.Errors())) case fmt.Formatter: verbose := fmt.Sprintf("% v", e) if verbose != basic { // This is a rich error type, like those produced by // github.com/pkg/errors. enc.AddString(key "Verbose", verbose) } } return nil } type errorGroup interface { // Provides read-only access to the underlying list of errors, preferably // without causing any allocs. Errors() []error } type errArray []error func (errs errArray) MarshalLogArray(arr ArrayEncoder) error { for i := range errs { if errs[i] == nil { continue } el := newErrArrayElem(errs[i]) arr.AppendObject(el) el.Free() } return nil }</code></code></pre><blockquote>encodeError方法判断err.(type),若是errorGroup则执行enc.AddArray,若是fmt.Formatter且verbose不是basic则执行<code>enc.AddString(key "Verbose", verbose)</code></blockquote><h2>Stack</h2>

zap@v1.16.0/field.go

<pre><code class="lang-go hljs">func Stack(key string) Field { return StackSkip(key, 1) // skip Stack } func StackSkip(key string, skip int) Field { // Returning the stacktrace as a string costs an allocation, but saves us // from expanding the zapcore.Field union struct to include a byte slice. Since // taking a stacktrace is already so expensive (~10us), the extra allocation // is okay. return String(key, takeStacktrace(skip 1)) // skip StackSkip }</code></code></pre><blockquote>Stack执行StackSkip,其skip为1;StackSkip用于构建String类型的stack,同时跳过前几个frame,这里使用takeStacktrace来取所需的stack</blockquote><h2>takeStacktrace</h2>

zap@v1.16.0/stacktrace.go

<pre><code class="lang-go hljs">func takeStacktrace(skip int) string { buffer := bufferpool.Get() defer buffer.Free() programCounters := _stacktracePool.Get().(*programCounters) defer _stacktracePool.Put(programCounters) var numFrames int for { // Skip the call to runtime.Callers and takeStacktrace so that the // program counters start at the caller of takeStacktrace. numFrames = runtime.Callers(skip 2, programCounters.pcs) if numFrames < len(programCounters.pcs) { break } // Don't put the too-short counter slice back into the pool; this lets // the pool adjust if we consistently take deep stacktraces. programCounters = newProgramCounters(len(programCounters.pcs) * 2) } i := 0 frames := runtime.CallersFrames(programCounters.pcs[:numFrames]) // Note: On the last iteration, frames.Next() returns false, with a valid // frame, but we ignore this frame. The last frame is a a runtime frame which // adds noise, since it's only either runtime.main or runtime.goexit. for frame, more := frames.Next(); more; frame, more = frames.Next() { if i != 0 { buffer.AppendByte('\n') } i buffer.AppendString(frame.Function) buffer.AppendByte('\n') buffer.AppendByte('\t') buffer.AppendString(frame.File) buffer.AppendByte(':') buffer.AppendInt(int64(frame.Line)) } return buffer.String() }</code></code></pre><blockquote>takeStacktrace方法通过runtime.Callers来获取frames,之后遍历frames,将其拼接为string</blockquote><h2>实例</h2><pre><code class="lang-go hljs">func errorStacktraceDemo() { logger, err := zap.NewDevelopment() defer logger.Sync() if err != nil { panic(err) } logger.Info("errorField", zap.Error(errors.New("demo err"))) fmt.Println(zap.Stack("default stack").String) fmt.Println("------") fmt.Println(zap.StackSkip("skip 2", 2).String) logger.Info("stacktrace default", zap.Stack("default stack")) logger.Info("stacktrace skip 2", zap.StackSkip("skip 2", 2)) }</code></code></pre>

输出

<pre><code class="lang-go hljs">2020-12-23T22:19:06.150 0800 INFO zap/zap_demo.go:29 errorField {"error": "demo err"} main.errorStacktraceDemo /zap/zap_demo.go:31 main.main /zap/zap_demo.go:20 runtime.main /usr/local/go/src/runtime/proc.go:204 ------ runtime.main /usr/local/go/src/runtime/proc.go:204 2020-12-23T22:19:06.150 0800 INFO zap/zap_demo.go:34 stacktrace default {"default stack": "main.errorStacktraceDemo\n\t/zap/zap_demo.go:34\nmain.main\n\t/zap/zap_demo.go:20\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:204"} 2020-12-23T22:19:06.150 0800 INFO zap/zap_demo.go:35 stacktrace skip 2 {"skip 2": "runtime.main\n\t/usr/local/go/src/runtime/proc.go:204"}</code></code></pre><h2>小结</h2>

zap提供了Error及Stack方法用于创建ErrorType类型的error及StringType的stacktrace;ErrorType类型的Field使用的是encodeError方法;takeStacktrace方法通过runtime.Callers来获取frames,之后遍历frames,将其拼接为string。

<h2>doc</h2><ul><li>zap</li></ul> 到此这篇关于“ 聊聊golang的zap的error”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
聊聊golang的zap的error
聊聊golang的zap的Sink
阿里旺旺聊天记录不见怎么找回?阿里旺旺不显示聊天记录怎么办?
Golang 网络编程实现的高并发聊天程序
聊聊,如何快速搭建部署GO开发环境和部署服务?
QQ会员中的聊天记录漫游设计缺陷分析
[Golang]简单的聊天室实现
go随聊-消息摘要算法MD5
PHP聊天室_WebSocket技术实战
Golang 高频面试题七问

[关闭]
~ ~