mlog: remove ErrKV/ErrWithKV, merr.KV/merr.WithValue are used instead

This commit is contained in:
Brian Picciano 2019-01-14 23:55:22 -05:00
parent 56f3e71acd
commit 0e80e1fd3d
10 changed files with 34 additions and 124 deletions

View File

@ -4,14 +4,13 @@ This is a collection of packages which I use across many of my personal
projects. All packages intended to be used start with an `m`, packages not projects. All packages intended to be used start with an `m`, packages not
starting with `m` are for internal use within this set of packages. starting with `m` are for internal use within this set of packages.
Other third-party packages which integrate into these: ## Usage notes
* [merry](github.com/ansel1/merry): used by `mlog` to embed KV logging * In general, all checking of equality of errors, e.g. `err == io.EOF`, done on
information into `error` instances, it should be assumed that all errors errors returned from the packages in this project should be done using
returned from these packages are `merry.Error` instances. In cases where a `merr.Equal`, e.g. `merr.Equal(err, io.EOF)`. The `merr` package is used to
package has a specific error it might return and which might be checked for a wrap errors and embed further metadata in them, like stack traces and so
function to perform that equality check will be supplied as part of the forth.
package.
## Styleguide ## Styleguide

View File

@ -13,7 +13,7 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/mediocregopher/mediocre-go-lib/mlog" "github.com/mediocregopher/mediocre-go-lib/merr"
) )
var ( var (
@ -58,7 +58,7 @@ func (pk PublicKey) verify(s Signature, r io.Reader) error {
return err return err
} }
if err := rsa.VerifyPSS(&pk.PublicKey, crypto.SHA256, h.Sum(nil), s.sig, nil); err != nil { if err := rsa.VerifyPSS(&pk.PublicKey, crypto.SHA256, h.Sum(nil), s.sig, nil); err != nil {
return mlog.ErrWithKV(ErrInvalidSig, s) return merr.WithValue(ErrInvalidSig, "sig", s, true)
} }
return nil return nil
} }
@ -88,12 +88,12 @@ func (pk *PublicKey) UnmarshalText(b []byte) error {
str := string(b) str := string(b)
strEnc, ok := stripPrefix(str, pubKeyV0) strEnc, ok := stripPrefix(str, pubKeyV0)
if !ok || len(strEnc) <= hex.EncodedLen(8) { if !ok || len(strEnc) <= hex.EncodedLen(8) {
return mlog.ErrWithKV(errMalformedPublicKey, mlog.KV{"pubKeyStr": str}) return merr.WithValue(errMalformedPublicKey, "pubKeyStr", str, true)
} }
b, err := hex.DecodeString(strEnc) b, err := hex.DecodeString(strEnc)
if err != nil { if err != nil {
return mlog.ErrWithKV(err, mlog.KV{"pubKeyStr": str}) return merr.WithValue(err, "pubKeyStr", str, true)
} }
pk.E = int(binary.BigEndian.Uint64(b)) pk.E = int(binary.BigEndian.Uint64(b))
@ -184,17 +184,17 @@ func (pk *PrivateKey) UnmarshalText(b []byte) error {
str := string(b) str := string(b)
strEnc, ok := stripPrefix(str, privKeyV0) strEnc, ok := stripPrefix(str, privKeyV0)
if !ok { if !ok {
return mlog.ErrWithKV(errMalformedPrivateKey, mlog.KV{"privKeyStr": str}) return merr.Wrap(errMalformedPrivateKey)
} }
b, err := hex.DecodeString(strEnc) b, err := hex.DecodeString(strEnc)
if err != nil { if err != nil {
return mlog.ErrWithKV(err, mlog.KV{"privKeyStr": str}) return merr.Wrap(errMalformedPrivateKey)
} }
e, n := binary.Uvarint(b) e, n := binary.Uvarint(b)
if n <= 0 { if n <= 0 {
return mlog.ErrWithKV(errMalformedPrivateKey, mlog.KV{"privKeyStr": str}) return merr.Wrap(errMalformedPrivateKey)
} }
pk.PublicKey.E = int(e) pk.PublicKey.E = int(e)
b = b[n:] b = b[n:]
@ -205,7 +205,7 @@ func (pk *PrivateKey) UnmarshalText(b []byte) error {
} }
l, n := binary.Uvarint(b) l, n := binary.Uvarint(b)
if n <= 0 { if n <= 0 {
err = errMalformedPrivateKey err = merr.Wrap(errMalformedPrivateKey)
} }
b = b[n:] b = b[n:]
i := new(big.Int) i := new(big.Int)
@ -221,7 +221,7 @@ func (pk *PrivateKey) UnmarshalText(b []byte) error {
} }
if err != nil { if err != nil {
return mlog.ErrWithKV(err, mlog.KV{"privKeyStr": str}) return err
} }
return nil return nil
} }

View File

@ -7,7 +7,7 @@ import (
"io" "io"
"time" "time"
"github.com/mediocregopher/mediocre-go-lib/mlog" "github.com/mediocregopher/mediocre-go-lib/merr"
) )
// Secret contains a set of bytes which are inteded to remain secret within some // Secret contains a set of bytes which are inteded to remain secret within some
@ -76,9 +76,9 @@ func (s Secret) sign(r io.Reader) (Signature, error) {
func (s Secret) verify(sig Signature, r io.Reader) error { func (s Secret) verify(sig Signature, r io.Reader) error {
sigB, err := s.signRaw(r, uint8(len(sig.sig)), sig.salt, sig.t) sigB, err := s.signRaw(r, uint8(len(sig.sig)), sig.salt, sig.t)
if err != nil { if err != nil {
return mlog.ErrWithKV(err, sig) return merr.WithValue(err, "sig", sig, true)
} else if !hmac.Equal(sigB, sig.sig) { } else if !hmac.Equal(sigB, sig.sig) {
return mlog.ErrWithKV(ErrInvalidSig, sig) return merr.WithValue(ErrInvalidSig, "sig", sig, true)
} }
return nil return nil
} }

View File

@ -4,7 +4,7 @@ import (
. "testing" . "testing"
"time" "time"
"github.com/ansel1/merry" "github.com/mediocregopher/mediocre-go-lib/merr"
"github.com/mediocregopher/mediocre-go-lib/mrand" "github.com/mediocregopher/mediocre-go-lib/mrand"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -43,9 +43,9 @@ func TestSecretSignVerify(t *T) {
assert.NotEqual(t, prevSig.String(), thisSigStr) assert.NotEqual(t, prevSig.String(), thisSigStr)
assert.NotEqual(t, prevWeakSig.String(), thisWeakSigStr) assert.NotEqual(t, prevWeakSig.String(), thisWeakSigStr)
err := VerifyString(secret, prevSig, thisStr) err := VerifyString(secret, prevSig, thisStr)
assert.True(t, merry.Is(err, ErrInvalidSig)) assert.True(t, merr.Equal(err, ErrInvalidSig))
err = VerifyString(secret, prevWeakSig, thisStr) err = VerifyString(secret, prevWeakSig, thisStr)
assert.True(t, merry.Is(err, ErrInvalidSig)) assert.True(t, merr.Equal(err, ErrInvalidSig))
} }
prevStr = thisStr prevStr = thisStr
prevSig = thisSig prevSig = thisSig

View File

@ -9,7 +9,7 @@ import (
"io" "io"
"time" "time"
"github.com/mediocregopher/mediocre-go-lib/mlog" "github.com/mediocregopher/mediocre-go-lib/merr"
) )
var ( var (
@ -66,12 +66,12 @@ func (s *Signature) UnmarshalText(b []byte) error {
str := string(b) str := string(b)
strEnc, ok := stripPrefix(str, sigV0) strEnc, ok := stripPrefix(str, sigV0)
if !ok || len(strEnc) < hex.EncodedLen(10) { if !ok || len(strEnc) < hex.EncodedLen(10) {
return mlog.ErrWithKV(errMalformedSig, mlog.KV{"sigStr": str}) return merr.WithValue(errMalformedSig, "sigStr", str, true)
} }
b, err := hex.DecodeString(strEnc) b, err := hex.DecodeString(strEnc)
if err != nil { if err != nil {
return mlog.ErrWithKV(err, mlog.KV{"sigStr": str}) return merr.WithValue(err, "sigStr", str, true)
} }
unixNano, b := int64(binary.BigEndian.Uint64(b[:8])), b[8:] unixNano, b := int64(binary.BigEndian.Uint64(b[:8])), b[8:]
@ -81,7 +81,7 @@ func (s *Signature) UnmarshalText(b []byte) error {
if err != nil { if err != nil {
return nil return nil
} else if len(b) < 1+int(b[0]) { } else if len(b) < 1+int(b[0]) {
err = mlog.ErrWithKV(errMalformedSig, mlog.KV{"sigStr": str}) err = merr.WithValue(errMalformedSig, "sigStr", str, true)
return nil return nil
} }
out := b[1 : 1+b[0]] out := b[1 : 1+b[0]]

View File

@ -4,7 +4,7 @@ import (
. "testing" . "testing"
"time" "time"
"github.com/ansel1/merry" "github.com/mediocregopher/mediocre-go-lib/merr"
"github.com/mediocregopher/mediocre-go-lib/mrand" "github.com/mediocregopher/mediocre-go-lib/mrand"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -36,7 +36,7 @@ func TestSignerVerifier(t *T) {
if prevStr != "" { if prevStr != "" {
assert.NotEqual(t, prevSig.String(), thisSigStr) assert.NotEqual(t, prevSig.String(), thisSigStr)
err := VerifyString(secret, prevSig, thisStr) err := VerifyString(secret, prevSig, thisStr)
assert.True(t, merry.Is(err, ErrInvalidSig)) assert.True(t, merr.Equal(err, ErrInvalidSig))
} }
prevStr = thisStr prevStr = thisStr
prevSig = thisSig prevSig = thisSig

View File

@ -9,7 +9,7 @@ import (
"errors" "errors"
"time" "time"
"github.com/mediocregopher/mediocre-go-lib/mlog" "github.com/mediocregopher/mediocre-go-lib/merr"
) )
var errMalformedUUID = errors.New("malformed UUID string") var errMalformedUUID = errors.New("malformed UUID string")
@ -68,11 +68,11 @@ func (u *UUID) UnmarshalText(b []byte) error {
str := string(b) str := string(b)
strEnc, ok := stripPrefix(str, uuidV0) strEnc, ok := stripPrefix(str, uuidV0)
if !ok || len(strEnc) != hex.EncodedLen(16) { if !ok || len(strEnc) != hex.EncodedLen(16) {
return mlog.ErrWithKV(errMalformedUUID, mlog.KV{"uuidStr": str}) return merr.WithValue(errMalformedUUID, "uuidStr", str, true)
} }
b, err := hex.DecodeString(strEnc) b, err := hex.DecodeString(strEnc)
if err != nil { if err != nil {
return mlog.ErrWithKV(err, mlog.KV{"uuidStr": str}) return merr.WithValue(err, "uuidStr", str, true)
} }
u.b = b u.b = b
return nil return nil

View File

@ -2,52 +2,10 @@ package mlog
import ( import (
"context" "context"
"fmt"
"path/filepath"
"github.com/ansel1/merry"
) )
type kvKey int type kvKey int
// ErrWithKV embeds the merging of a set of KVs into an error, returning a new
// error instance. If the error already has a KV embedded in it then the
// returned error will have the merging of them all, with the given KVs taking
// precedence.
func ErrWithKV(err error, kvs ...KVer) merry.Error {
if err == nil {
return nil
}
merr := merry.WrapSkipping(err, 1)
var kv KV
if exKV := merry.Value(merr, kvKey(0)); exKV != nil {
kv = mergeInto(exKV.(KV), kvs...)
} else {
kv = Merge(kvs...).KV()
}
return merr.WithValue(kvKey(0), kv)
}
// ErrKV returns a KV which includes the KV embedded in the error by ErrWithKV,
// if any. The KV will also include an "err" field containing the output of
// err.Error(), and an "errSrc" field if the given error is a merry error which
// contains an embedded stacktrace.
func ErrKV(err error) KVer {
var kv KV
if kvi := merry.Value(err, kvKey(0)); kvi != nil {
kv = kvi.(KV)
} else {
kv = KV{}
}
kv["err"] = err.Error()
if fileFull, line := merry.Location(err); fileFull != "" {
file, dir := filepath.Base(fileFull), filepath.Dir(fileFull)
dir = filepath.Base(dir) // only want the first dirname, ie the pkg name
kv["errSrc"] = fmt.Sprintf("%s/%s:%d", dir, file, line)
}
return kv
}
// CtxWithKV embeds a KV into a Context, returning a new Context instance. If // CtxWithKV embeds a KV into a Context, returning a new Context instance. If
// the Context already has a KV embedded in it then the returned error will have // the Context already has a KV embedded in it then the returned error will have
// the merging of the two, with the given KVs taking precedence. // the merging of the two, with the given KVs taking precedence.

View File

@ -2,60 +2,11 @@ package mlog
import ( import (
"context" "context"
"strings"
. "testing" . "testing"
"github.com/ansel1/merry"
"github.com/mediocregopher/mediocre-go-lib/mtest/massert" "github.com/mediocregopher/mediocre-go-lib/mtest/massert"
) )
func TestErrKV(t *T) {
assertErrKV := func(err error, exp KV) massert.Assertion {
got := KV(ErrKV(err).KV())
errSrc := got["errSrc"]
delete(got, "errSrc")
return massert.All(
massert.Not(massert.Nil(errSrc)),
massert.Equal(true, strings.HasPrefix(errSrc.(string), "mlog/errctx_test.go")),
massert.Equal(exp, got),
)
}
err := merry.New("foo")
massert.Fatal(t, assertErrKV(err, KV{"err": err.Error()}))
kv := KV{"a": "a"}
err2 := ErrWithKV(err, kv)
massert.Fatal(t, massert.All(
assertErrKV(err, KV{"err": err.Error()}),
assertErrKV(err2, KV{"err": err.Error(), "a": "a"}),
))
// changing the kv now shouldn't do anything
kv["a"] = "b"
massert.Fatal(t, massert.All(
assertErrKV(err, KV{"err": err.Error()}),
assertErrKV(err2, KV{"err": err.Error(), "a": "a"}),
))
// a new ErrWithKV shouldn't affect the previous one
err3 := ErrWithKV(err2, KV{"b": "b"})
massert.Fatal(t, massert.All(
assertErrKV(err, KV{"err": err.Error()}),
assertErrKV(err2, KV{"err": err2.Error(), "a": "a"}),
assertErrKV(err3, KV{"err": err3.Error(), "a": "a", "b": "b"}),
))
// make sure precedence works
err4 := ErrWithKV(err3, KV{"b": "bb"})
massert.Fatal(t, massert.All(
assertErrKV(err, KV{"err": err.Error()}),
assertErrKV(err2, KV{"err": err2.Error(), "a": "a"}),
assertErrKV(err3, KV{"err": err3.Error(), "a": "a", "b": "b"}),
assertErrKV(err4, KV{"err": err4.Error(), "a": "a", "b": "bb"}),
))
}
func TestCtxKV(t *T) { func TestCtxKV(t *T) {
ctx := context.Background() ctx := context.Background()
massert.Fatal(t, massert.Equal(KV{}, CtxKV(ctx))) massert.Fatal(t, massert.Equal(KV{}, CtxKV(ctx)))

View File

@ -10,7 +10,7 @@
// Examples: // Examples:
// //
// Info("Something important has occurred") // Info("Something important has occurred")
// Error("Could not open file", llog.KV{"filename": filename}, llog.ErrKV(err)) // Error("Could not open file", llog.KV{"filename": filename}, merr.KV(err))
// //
package mlog package mlog
@ -22,6 +22,8 @@ import (
"sort" "sort"
"strconv" "strconv"
"sync" "sync"
"github.com/mediocregopher/mediocre-go-lib/merr"
) )
// Truncate is a helper function to truncate a string to a given size. It will // Truncate is a helper function to truncate a string to a given size. It will
@ -339,7 +341,7 @@ func (l *Logger) Log(msg Message) {
} }
if err := l.h(msg); err != nil { if err := l.h(msg); err != nil {
go l.Error("Logger.Handler returned error", ErrKV(err)) go l.Error("Logger.Handler returned error", merr.KV(err))
return return
} }