refactor mlog and merr to handle and format error messages better
This commit is contained in:
parent
360d41e2b8
commit
ba9bc6c46c
@ -178,3 +178,17 @@ func MergeAnnotations(ctx context.Context, ctxs ...context.Context) context.Cont
|
|||||||
}
|
}
|
||||||
return context.WithValue(ctx, ctxKeyAnnotation(0), &el{annotator: aa})
|
return context.WithValue(ctx, ctxKeyAnnotation(0), &el{annotator: aa})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ctxAnnotator struct {
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ca ctxAnnotator) Annotate(aa Annotations) {
|
||||||
|
EvaluateAnnotations(ca.ctx, aa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextAsAnnotator will return an Annotator which, when evaluated, will
|
||||||
|
// call EvaluateAnnotations on the given Context.
|
||||||
|
func ContextAsAnnotator(ctx context.Context) Annotator {
|
||||||
|
return ctxAnnotator{ctx}
|
||||||
|
}
|
||||||
|
11
merr/merr.go
11
merr/merr.go
@ -41,9 +41,14 @@ type Error struct {
|
|||||||
|
|
||||||
// Error implements the method for the error interface.
|
// Error implements the method for the error interface.
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
sb := strBuilderPool.Get().(*strings.Builder)
|
return e.Err.Error()
|
||||||
defer putStrBuilder(sb)
|
}
|
||||||
sb.WriteString(strings.TrimSpace(e.Err.Error()))
|
|
||||||
|
// FullError returns an error string which includes contextual annotations and
|
||||||
|
// stacktrace information.
|
||||||
|
func (e Error) FullError() string {
|
||||||
|
sb := new(strings.Builder)
|
||||||
|
sb.WriteString(strings.TrimSpace(e.Error()))
|
||||||
|
|
||||||
annotations := make(mctx.Annotations)
|
annotations := make(mctx.Annotations)
|
||||||
mctx.EvaluateAnnotations(e.Ctx, annotations)
|
mctx.EvaluateAnnotations(e.Ctx, annotations)
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/mediocregopher/mediocre-go-lib/v2/mtest/massert"
|
"github.com/mediocregopher/mediocre-go-lib/v2/mtest/massert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestError(t *testing.T) {
|
func TestFullError(t *testing.T) {
|
||||||
massert.Require(t, massert.Nil(Wrap(context.Background(), nil)))
|
massert.Require(t, massert.Nil(Wrap(context.Background(), nil)))
|
||||||
|
|
||||||
ctx := mctx.Annotate(context.Background(),
|
ctx := mctx.Annotate(context.Background(),
|
||||||
@ -27,7 +27,7 @@ func TestError(t *testing.T) {
|
|||||||
ccc
|
ccc
|
||||||
* d: weird key but ok
|
* d: weird key but ok
|
||||||
* line: merr/merr_test.go:22`
|
* line: merr/merr_test.go:22`
|
||||||
massert.Require(t, massert.Equal(exp, e.Error()))
|
massert.Require(t, massert.Equal(exp, e.(Error).FullError()))
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -39,11 +39,13 @@ func TestError(t *testing.T) {
|
|||||||
ccc
|
ccc
|
||||||
* d: weird key but ok
|
* d: weird key but ok
|
||||||
* line: merr/merr_test.go:34`
|
* line: merr/merr_test.go:34`
|
||||||
massert.Require(t, massert.Equal(exp, e.Error()))
|
massert.Require(t, massert.Equal(exp, e.(Error).FullError()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAs(t *testing.T) {
|
func TestAsIsError(t *testing.T) {
|
||||||
|
testST := newStacktrace(0)
|
||||||
|
|
||||||
ctxA := mctx.Annotate(context.Background(), "a", "1")
|
ctxA := mctx.Annotate(context.Background(), "a", "1")
|
||||||
ctxB := mctx.Annotate(context.Background(), "b", "2")
|
ctxB := mctx.Annotate(context.Background(), "b", "2")
|
||||||
errFoo := errors.New("foo")
|
errFoo := errors.New("foo")
|
||||||
@ -52,77 +54,80 @@ func TestAs(t *testing.T) {
|
|||||||
in error
|
in error
|
||||||
expAs error
|
expAs error
|
||||||
expIs error
|
expIs error
|
||||||
|
expStr string
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []test{
|
tests := []test{
|
||||||
{
|
{
|
||||||
in: nil,
|
in: nil,
|
||||||
expAs: nil,
|
|
||||||
expIs: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: errors.New("bar"),
|
in: errors.New("bar"),
|
||||||
expAs: nil,
|
expStr: "bar",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: Error{
|
in: Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
},
|
},
|
||||||
expAs: Error{
|
expAs: Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
},
|
},
|
||||||
expIs: errFoo,
|
expIs: errFoo,
|
||||||
|
expStr: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: fmt.Errorf("bar: %w", Error{
|
in: fmt.Errorf("bar: %w", Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
}),
|
}),
|
||||||
expAs: Error{
|
expAs: Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
},
|
},
|
||||||
expIs: errFoo,
|
expIs: errFoo,
|
||||||
|
expStr: "bar: foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: Wrap(ctxB, Error{
|
in: Wrap(ctxB, Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
}),
|
}),
|
||||||
expAs: Error{
|
expAs: Error{
|
||||||
Err: Error{
|
Err: Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
},
|
},
|
||||||
Ctx: mctx.MergeAnnotations(ctxA, ctxB),
|
Ctx: mctx.MergeAnnotations(ctxA, ctxB),
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
},
|
},
|
||||||
expIs: errFoo,
|
expIs: errFoo,
|
||||||
|
expStr: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: Wrap(ctxB, fmt.Errorf("%w", Error{
|
in: Wrap(ctxB, fmt.Errorf("bar: %w", Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
})),
|
})),
|
||||||
expAs: Error{
|
expAs: Error{
|
||||||
Err: fmt.Errorf("%w", Error{
|
Err: fmt.Errorf("bar: %w", Error{
|
||||||
Err: errFoo,
|
Err: errFoo,
|
||||||
Ctx: ctxA,
|
Ctx: ctxA,
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
}),
|
}),
|
||||||
Ctx: mctx.MergeAnnotations(ctxA, ctxB),
|
Ctx: mctx.MergeAnnotations(ctxA, ctxB),
|
||||||
Stacktrace: Stacktrace{frames: []uintptr{666}},
|
Stacktrace: testST,
|
||||||
},
|
},
|
||||||
expIs: errFoo,
|
expIs: errFoo,
|
||||||
|
expStr: "bar: foo",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +159,7 @@ func TestAs(t *testing.T) {
|
|||||||
massert.Equal(true, errors.Is(test.in, test.expIs)),
|
massert.Equal(true, errors.Is(test.in, test.expIs)),
|
||||||
"errors.Is(\ntest.in:%#v,\ntest.expIs:%#v,\n)", test.in, test.expIs,
|
"errors.Is(\ntest.in:%#v,\ntest.expIs:%#v,\n)", test.in, test.expIs,
|
||||||
),
|
),
|
||||||
|
massert.Equal(test.expStr, test.in.Error()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
27
mlog/mlog.go
27
mlog/mlog.go
@ -10,7 +10,7 @@ package mlog
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -19,8 +19,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
||||||
|
"github.com/mediocregopher/mediocre-go-lib/v2/merr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mlogAnnotation string
|
||||||
|
|
||||||
// Null is an instance of Logger which will write all Messages to /dev/null.
|
// Null is an instance of Logger which will write all Messages to /dev/null.
|
||||||
var Null = NewLogger(&LoggerOpts{
|
var Null = NewLogger(&LoggerOpts{
|
||||||
MessageHandler: NewMessageHandler(ioutil.Discard),
|
MessageHandler: NewMessageHandler(ioutil.Discard),
|
||||||
@ -299,7 +302,7 @@ func (l *Logger) Log(msg Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkMsg(ctx context.Context, lvl Level, descr string, annotators ...mctx.Annotator) Message {
|
func mkMsg(ctx context.Context, lvl Level, descr string) Message {
|
||||||
return Message{
|
return Message{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Level: lvl,
|
Level: lvl,
|
||||||
@ -307,6 +310,22 @@ func mkMsg(ctx context.Context, lvl Level, descr string, annotators ...mctx.Anno
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mkErrMsg(ctx context.Context, lvl Level, descr string, err error) Message {
|
||||||
|
var e merr.Error
|
||||||
|
if !errors.As(err, &e) {
|
||||||
|
ctx = mctx.Annotate(ctx, mlogAnnotation("errMsg"), err.Error())
|
||||||
|
return mkMsg(ctx, lvl, descr)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = mctx.Annotate(ctx,
|
||||||
|
mlogAnnotation("errMsg"), err.Error(),
|
||||||
|
mlogAnnotation("errCtx"), mctx.ContextAsAnnotator(e.Ctx),
|
||||||
|
mlogAnnotation("errLine"), e.Stacktrace.String(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return mkMsg(ctx, lvl, descr)
|
||||||
|
}
|
||||||
|
|
||||||
// Debug logs a LevelDebug message.
|
// Debug logs a LevelDebug message.
|
||||||
func (l *Logger) Debug(ctx context.Context, descr string) {
|
func (l *Logger) Debug(ctx context.Context, descr string) {
|
||||||
l.Log(mkMsg(ctx, LevelDebug, descr))
|
l.Log(mkMsg(ctx, LevelDebug, descr))
|
||||||
@ -324,7 +343,7 @@ func (l *Logger) WarnString(ctx context.Context, descr string) {
|
|||||||
|
|
||||||
// Warn logs a LevelWarn message, including information from the given error.
|
// Warn logs a LevelWarn message, including information from the given error.
|
||||||
func (l *Logger) Warn(ctx context.Context, descr string, err error) {
|
func (l *Logger) Warn(ctx context.Context, descr string, err error) {
|
||||||
l.Log(mkMsg(ctx, LevelWarn, fmt.Sprintf("%s: %s", descr, err)))
|
l.Log(mkErrMsg(ctx, LevelWarn, descr, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorString logs a LevelError message which is only a string.
|
// ErrorString logs a LevelError message which is only a string.
|
||||||
@ -334,7 +353,7 @@ func (l *Logger) ErrorString(ctx context.Context, descr string) {
|
|||||||
|
|
||||||
// Error logs a LevelError message, including information from the given error.
|
// Error logs a LevelError message, including information from the given error.
|
||||||
func (l *Logger) Error(ctx context.Context, descr string, err error) {
|
func (l *Logger) Error(ctx context.Context, descr string, err error) {
|
||||||
l.Log(mkMsg(ctx, LevelError, fmt.Sprintf("%s: %s", descr, err)))
|
l.Log(mkErrMsg(ctx, LevelError, descr, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal logs a LevelFatal message. A Fatal message automatically stops the
|
// Fatal logs a LevelFatal message. A Fatal message automatically stops the
|
||||||
|
@ -50,8 +50,8 @@ func TestLogger(t *T) {
|
|||||||
l.Error(ctx, "buz", errors.New("ERR"))
|
l.Error(ctx, "buz", errors.New("ERR"))
|
||||||
massert.Require(t,
|
massert.Require(t,
|
||||||
assertOut(`{"td":"<TD>","ts":<TS>,"level":"INFO","descr":"bar","level_int":30}`),
|
assertOut(`{"td":"<TD>","ts":<TS>,"level":"INFO","descr":"bar","level_int":30}`),
|
||||||
assertOut(`{"td":"<TD>","ts":<TS>,"level":"WARN","descr":"baz: ERR","level_int":20}`),
|
assertOut(`{"td":"<TD>","ts":<TS>,"level":"WARN","descr":"baz","level_int":20,"annotations":{"errMsg":"ERR"}}`),
|
||||||
assertOut(`{"td":"<TD>","ts":<TS>,"level":"ERROR","descr":"buz: ERR","level_int":10}`),
|
assertOut(`{"td":"<TD>","ts":<TS>,"level":"ERROR","descr":"buz","level_int":10,"annotations":{"errMsg":"ERR"}}`),
|
||||||
)
|
)
|
||||||
|
|
||||||
// annotate context
|
// annotate context
|
||||||
|
Loading…
Reference in New Issue
Block a user