mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
feat: enhanced error handling module
This commit is contained in:
parent
18ab6c52ec
commit
4615d7dd4e
8 changed files with 112 additions and 30 deletions
|
@ -1,9 +1,10 @@
|
|||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/yusing/go-proxy/pkg/json"
|
||||
)
|
||||
|
||||
// baseError is an immutable wrapper around an error.
|
||||
|
@ -48,17 +49,6 @@ func (err *baseError) Error() string {
|
|||
return err.Err.Error()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (err *baseError) MarshalJSON() ([]byte, error) {
|
||||
//nolint:errorlint
|
||||
switch err := err.Err.(type) {
|
||||
case Error, *withSubject:
|
||||
return json.Marshal(err)
|
||||
case json.Marshaler:
|
||||
return err.MarshalJSON()
|
||||
case interface{ MarshalText() ([]byte, error) }:
|
||||
return err.MarshalText()
|
||||
default:
|
||||
return json.Marshal(err.Error())
|
||||
}
|
||||
func (err *baseError) MarshalJSONTo(buf []byte) []byte {
|
||||
return json.MarshalTo(err.Err, buf)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,10 @@ type Builder struct {
|
|||
rwLock
|
||||
}
|
||||
|
||||
type multiline struct {
|
||||
*Builder
|
||||
}
|
||||
|
||||
// NewBuilder creates a new Builder.
|
||||
//
|
||||
// If about is not provided, the Builder will not have a subject
|
||||
|
@ -78,12 +82,15 @@ func (b *Builder) Add(err error) *Builder {
|
|||
return b
|
||||
}
|
||||
|
||||
wrapped := wrap(err)
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
switch err := wrapped.(type) {
|
||||
b.add(err)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) add(err error) {
|
||||
switch err := err.(type) {
|
||||
case *baseError:
|
||||
b.errs = append(b.errs, err.Err)
|
||||
case *nestedError:
|
||||
|
@ -92,11 +99,11 @@ func (b *Builder) Add(err error) *Builder {
|
|||
} else {
|
||||
b.errs = append(b.errs, err)
|
||||
}
|
||||
case *MultilineError:
|
||||
b.add(&err.nestedError)
|
||||
default:
|
||||
panic("bug: should not reach here")
|
||||
b.errs = append(b.errs, err)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) Adds(err string) *Builder {
|
||||
|
@ -144,8 +151,9 @@ func (b *Builder) AddRange(errs ...error) *Builder {
|
|||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
b.errs = append(b.errs, nonNilErrs...)
|
||||
|
||||
for _, err := range nonNilErrs {
|
||||
b.add(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/logging"
|
||||
)
|
||||
|
||||
func log(msg string, err error, level zerolog.Level, logger ...*zerolog.Logger) {
|
||||
func log(_ string, err error, level zerolog.Level, logger ...*zerolog.Logger) {
|
||||
var l *zerolog.Logger
|
||||
if len(logger) > 0 {
|
||||
l = logger[0]
|
||||
} else {
|
||||
l = logging.GetLogger()
|
||||
}
|
||||
l.WithLevel(level).Msg(msg + ": " + err.Error())
|
||||
l.WithLevel(level).Msg(err.Error())
|
||||
}
|
||||
|
||||
func LogFatal(msg string, err error, logger ...*zerolog.Logger) {
|
||||
|
|
45
internal/gperr/multiline.go
Normal file
45
internal/gperr/multiline.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package gperr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type MultilineError struct {
|
||||
nestedError
|
||||
}
|
||||
|
||||
func Multiline() *MultilineError {
|
||||
return &MultilineError{}
|
||||
}
|
||||
|
||||
func (m *MultilineError) add(err error) {
|
||||
m.Extras = append(m.Extras, err)
|
||||
}
|
||||
|
||||
func (m *MultilineError) Addf(format string, args ...any) *MultilineError {
|
||||
m.add(fmt.Errorf(format, args...))
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MultilineError) Adds(s string) *MultilineError {
|
||||
m.add(newError(s))
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MultilineError) AddLines(lines any) *MultilineError {
|
||||
v := reflect.ValueOf(lines)
|
||||
if v.Kind() == reflect.Slice {
|
||||
for i := range v.Len() {
|
||||
switch v := v.Index(i).Interface().(type) {
|
||||
case string:
|
||||
m.add(newError(v))
|
||||
case error:
|
||||
m.add(v)
|
||||
default:
|
||||
m.add(fmt.Errorf("%v", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
38
internal/gperr/multiline_test.go
Normal file
38
internal/gperr/multiline_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package gperr
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMultiline(t *testing.T) {
|
||||
multiline := Multiline()
|
||||
multiline.Addf("line 1 %s", "test")
|
||||
multiline.Adds("line 2")
|
||||
multiline.AddLines([]any{1, "2", 3.0, net.IPv4(127, 0, 0, 1)})
|
||||
t.Error(New("result").With(multiline))
|
||||
t.Error(multiline.Subject("subject").Withf("inner"))
|
||||
}
|
||||
|
||||
func TestWrapMultiline(t *testing.T) {
|
||||
multiline := Multiline()
|
||||
var wrapper error = wrap(multiline)
|
||||
_, ok := wrapper.(*MultilineError)
|
||||
if !ok {
|
||||
t.Errorf("wrapper is not a MultilineError")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrependSubjectMultiline(t *testing.T) {
|
||||
multiline := Multiline()
|
||||
multiline.Addf("line 1 %s", "test")
|
||||
multiline.Adds("line 2")
|
||||
multiline.AddLines([]any{1, "2", 3.0, net.IPv4(127, 0, 0, 1)})
|
||||
multiline.Subject("subject")
|
||||
|
||||
builder := NewBuilder()
|
||||
builder.Add(multiline)
|
||||
require.Equal(t, len(builder.errs), len(multiline.Extras), builder.errs)
|
||||
}
|
|
@ -15,7 +15,7 @@ type nestedError struct {
|
|||
|
||||
func (err nestedError) Subject(subject string) Error {
|
||||
if err.Err == nil {
|
||||
err.Err = newError(subject)
|
||||
err.Err = PrependSubject(subject, errStr(""))
|
||||
} else {
|
||||
err.Err = PrependSubject(subject, err.Err)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/pkg/json"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||
)
|
||||
|
||||
|
@ -93,8 +94,7 @@ func (err *withSubject) Error() string {
|
|||
return sb.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (err *withSubject) MarshalJSON() ([]byte, error) {
|
||||
func (err *withSubject) MarshalJSONTo(buf []byte) []byte {
|
||||
subjects := slices.Clone(err.Subjects)
|
||||
slices.Reverse(subjects)
|
||||
|
||||
|
@ -102,5 +102,5 @@ func (err *withSubject) MarshalJSON() ([]byte, error) {
|
|||
"subjects": subjects,
|
||||
"err": err.Err,
|
||||
}
|
||||
return json.Marshal(reversed)
|
||||
return json.MarshalTo(reversed, buf)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/yusing/go-proxy/pkg/json"
|
||||
)
|
||||
|
||||
func newError(message string) error {
|
||||
|
|
Loading…
Add table
Reference in a new issue