merr/mctx: move stack code into merr
This commit is contained in:
parent
553a4854ea
commit
4303da03cc
33
merr/merr.go
33
merr/merr.go
@ -47,7 +47,10 @@ func wrap(e error, cp bool) *err {
|
||||
|
||||
er, ok := e.(*err)
|
||||
if !ok {
|
||||
return &err{err: e}
|
||||
return &err{
|
||||
err: e,
|
||||
attr: make(map[interface{}]interface{}, 1),
|
||||
}
|
||||
} else if !cp {
|
||||
return er
|
||||
}
|
||||
@ -100,19 +103,21 @@ func Value(e error, k interface{}) interface{} {
|
||||
// WrapSkip is like Wrap but also allows for skipping extra stack frames when
|
||||
// embedding the stack into the error.
|
||||
func WrapSkip(ctx context.Context, e error, skip int, kvs ...interface{}) error {
|
||||
prevCtx, _ := Value(e, attrKeyCtx).(context.Context)
|
||||
if prevCtx != nil {
|
||||
er := wrap(e, true)
|
||||
if _, ok := getStack(er); !ok {
|
||||
setStack(er, skip+1)
|
||||
}
|
||||
|
||||
if prevCtx, _ := er.attr[attrKeyCtx].(context.Context); prevCtx != nil {
|
||||
ctx = mctx.MergeAnnotations(prevCtx, ctx)
|
||||
}
|
||||
|
||||
if _, ok := mctx.Stack(ctx); !ok {
|
||||
ctx = mctx.WithStack(ctx, skip+1)
|
||||
}
|
||||
if len(kvs) > 0 {
|
||||
ctx = mctx.Annotate(ctx, kvs...)
|
||||
}
|
||||
|
||||
return WithValue(e, attrKeyCtx, ctx)
|
||||
er.attr[attrKeyCtx] = ctx
|
||||
return er
|
||||
}
|
||||
|
||||
// Wrap takes in an error and returns one which wraps it in merr's inner type,
|
||||
@ -122,9 +127,9 @@ func WrapSkip(ctx context.Context, e error, skip int, kvs ...interface{}) error
|
||||
// For convenience, extra annotation information can be passed in here as well
|
||||
// via the kvs argument. See mctx.Annotate for more information.
|
||||
//
|
||||
// This function automatically embeds stack information into the Context as it's
|
||||
// being stored, using mctx.WithStack, unless the error already has stack
|
||||
// information in it.
|
||||
// This function automatically embeds stack information into the error as it's
|
||||
// being stored, using WithStack, unless the error already has stack information
|
||||
// in it.
|
||||
func Wrap(ctx context.Context, e error, kvs ...interface{}) error {
|
||||
return WrapSkip(ctx, e, 1, kvs...)
|
||||
}
|
||||
@ -146,7 +151,7 @@ func ctx(e error) context.Context {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
if stack, ok := mctx.Stack(ctx); ok {
|
||||
if stack, ok := Stack(e); ok {
|
||||
ctx = mctx.Annotate(ctx, annotateKey("errLoc"), stack.String())
|
||||
}
|
||||
return ctx
|
||||
@ -156,9 +161,9 @@ func ctx(e error) context.Context {
|
||||
// or New. If none is embedded this uses context.Background().
|
||||
//
|
||||
// The returned Context will have annotated on it (see mctx.Annotate) the
|
||||
// underlying error's string (as returned by Error()) and the stack location in
|
||||
// the Context. Stack locations are automatically added by New and Wrap via
|
||||
// mctx.WithStack.
|
||||
// underlying error's string (as returned by Error()) and the error's stack
|
||||
// location. Stack locations are automatically added by New and Wrap via
|
||||
// WithStack.
|
||||
//
|
||||
// If this error is nil this returns context.Background().
|
||||
func Context(e error) context.Context {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package mctx
|
||||
package merr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@ -13,7 +12,7 @@ import (
|
||||
// stored when embedding stack traces in errors.
|
||||
var MaxStackSize = 50
|
||||
|
||||
type ctxStackKey int
|
||||
type stackKey int
|
||||
|
||||
// Stacktrace represents a stack trace at a particular point in execution.
|
||||
type Stacktrace struct {
|
||||
@ -73,22 +72,34 @@ func (s Stacktrace) FullString() string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// WithStack returns a Context with the current stacktrace embedded in it (as a
|
||||
func setStack(er *err, skip int) {
|
||||
stackSlice := make([]uintptr, MaxStackSize+skip)
|
||||
// incr skip once for WithStack, and once for runtime.Callers
|
||||
l := runtime.Callers(skip+2, stackSlice)
|
||||
er.attr[stackKey(0)] = Stacktrace{frames: stackSlice[:l]}
|
||||
}
|
||||
|
||||
// WithStack returns an error with the current stacktrace embedded in it (as a
|
||||
// Stacktrace type). If skip is non-zero it will skip that many frames from the
|
||||
// top of the stack. The frame containing the WithStack call itself is always
|
||||
// excluded.
|
||||
func WithStack(ctx context.Context, skip int) context.Context {
|
||||
stackSlice := make([]uintptr, MaxStackSize)
|
||||
// incr skip once for WithStack, and once for runtime.Callers
|
||||
l := runtime.Callers(skip+2, stackSlice)
|
||||
stack := Stacktrace{frames: stackSlice[:l]}
|
||||
|
||||
return context.WithValue(ctx, ctxStackKey(0), stack)
|
||||
//
|
||||
// This call always overwrites any previously existing stack information on the
|
||||
// error, as opposed to Wrap which only does so if the error didn't already have
|
||||
// any.
|
||||
func WithStack(e error, skip int) error {
|
||||
er := wrap(e, true)
|
||||
setStack(er, skip+1)
|
||||
return er
|
||||
}
|
||||
|
||||
// Stack returns the Stacktrace instance which was embedded by WithStack, or false if
|
||||
// none ever was.
|
||||
func Stack(ctx context.Context) (Stacktrace, bool) {
|
||||
stack, ok := ctx.Value(ctxStackKey(0)).(Stacktrace)
|
||||
func getStack(er *err) (Stacktrace, bool) {
|
||||
stack, ok := er.attr[stackKey(0)].(Stacktrace)
|
||||
return stack, ok
|
||||
}
|
||||
|
||||
// Stack returns the Stacktrace instance which was embedded by Wrap/WrapSkip, or
|
||||
// false if none ever was.
|
||||
func Stack(e error) (Stacktrace, bool) {
|
||||
return getStack(wrap(e, false))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package mctx
|
||||
package merr
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestStack(t *T) {
|
||||
foo := WithStack(context.Background(), 0)
|
||||
foo := New(context.Background(), "test")
|
||||
fooStack, ok := Stack(foo)
|
||||
massert.Fatal(t, massert.Equal(true, ok))
|
||||
|
Loading…
Reference in New Issue
Block a user