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