Go 에러 랩퍼

박재훈·2023년 4월 4일
0

GO

목록 보기
16/23

Error Wrapper

에러를 몇가지 유용한 함수들로 랩핑한다.

제네릭을 사용하기 때문에 Go1.18+ 버전이 필요하다.

Description

errwrap은 에러의 랩핑, 타입변환, 타입체킹에 관한 함수들을 제공해준다. 이 함수들은 Factory에 의해 생성되며, 이로 인해 에러 랩핑이 좀 더 시스템적으로 이루어질 수 있다.

제네릭을 사용하는 이유는 에러를 구별하기 위해서이다. 에러는 분명 서로 다른 메소드들에서 랩핑될 것이고, 그렇기에 그 메소드가 속한 타입을 에러의 타입으로 지정해두면 더 구별하기가 쉬워질 수 있다.

BaseError

먼저 New 함수를 이용하여 기본 에러를 만들 수 있다. errwrapNew, Newf, NewTyped, NewTypedf와 같이 4가지의 New 함수를 제공한다.

errwrap.New("new")
errwrap.Newf("%s%s", "new", "f")
errwrap.NewTyped[T]("newTyped")
errwrap.NewTypedf[T]("%s%s", "newTyped", "f")

이렇게 생성된 에러는 아래에서 설명할 wrappedError처럼 포맷팅 될 수 있다.

Factory

Go의 error 인터페이스 인스턴스는 이 패키지의 wrappedError로 랩핑 될 수 있다. 랩핑하려면 먼저 팩토리를 통해 랩퍼를 생성해야 한다.

factory는 이 에러가 발생한 곳의 타입을 제네릭으로 필요로 한다. 또한 에러가 무엇인지 설명할 메시지도 필요로 한다.

wrapper, assertor := Factory[UserController]("user controller")

// format specifier supplied
wrapper, assertor := Factoryf[UserController]("%s controller", "user")

factorywrapperassertor 두 함수를 반환한다. wrapper는 에러를 랩핑할 수 있다. 랩핑 방법은 그냥 인자로 에러를 넣으면 된다.

err = wrapper(err)

에러 랩핑의 깊이가 깊을 경우 assertor를 사용해서 특정 에러를 선택할 수 있다. 그럼 assertor는 타입 변환된 에러와 성공 여부를 리턴한다. 만약 실패할 경우 첫번째 반환값은 nil이 된다.
assertor의 사용법은 wrapper와 같다.

asserted, ok := assertor(err)

fmt.Println(asserted)  // user controller: ...

Checker

checkererrors.Is와 같이 동작하며 비교하려는 에러의 타입과 동일한지 알 수 있다. checker를 만드려면 wrapper가 필요하다. wrapperWithChecker의 인자가 되며 checker를 생성할 수 있다.

checker := WithChecker(&wrapper)

checker 생성 후 wrapper는 확인 가능한 에러를 만들 수 있다. checker의 사용법은 아래와 같다:

if checker(err) {
    fmt.Println("same")
}

Format

wrappedError는 에러 스택 저장을 통해 에러를 자세히 출력할 수 있으며 %+v 또는 %+s를 통해 가능하다.

fmt.Printf("%+v\n", err)

# this will be shown like

github.com/p9595jh/errwrap.TestChecker
    /Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:69
github.com/p9595jh/errwrap.TestChecker
    /Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:68
github.com/p9595jh/errwrap.TestChecker
    /Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:61
github.com/p9595jh/errwrap.TestChecker
    /Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:60

Benchmark

Comparison with github.com/pkg/errors.

Test code:

func BenchmarkWrappedError(b *testing.B) {
	w, _ := Factory[NONE]("test")
	err := New("sample")
	for i := 0; i < b.N; i++ {
		err := err
		for j := 0; j < 100; j++ {
			err = w(err)
		}
		_ = err
	}
}

func BenchmarkErrors(b *testing.B) {
	err := errors.New("sample")
	for i := 0; i < b.N; i++ {
		err := err
		for j := 0; j < 100; j++ {
			err = errors.Wrap(err, "test")
		}
		_ = err
	}
}

Run:

$ go test -bench=. -benchtime=10s -benchmem -count 5

goos: darwin
goarch: amd64
pkg: github.com/p9595jh/errwrap
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkWrappedError-12          165193             72756 ns/op           28000 B/op        300 allocs/op
BenchmarkWrappedError-12          152595             72621 ns/op           28000 B/op        300 allocs/op
BenchmarkWrappedError-12          155252             73013 ns/op           28000 B/op        300 allocs/op
BenchmarkWrappedError-12          175780             72772 ns/op           28000 B/op        300 allocs/op
BenchmarkWrappedError-12          149059             71417 ns/op           28000 B/op        300 allocs/op
BenchmarkErrors-12                152004             82268 ns/op           33600 B/op        400 allocs/op
BenchmarkErrors-12                150874             78414 ns/op           33600 B/op        400 allocs/op
BenchmarkErrors-12                152422             79438 ns/op           33600 B/op        400 allocs/op
BenchmarkErrors-12                155005             85094 ns/op           33600 B/op        400 allocs/op
BenchmarkErrors-12                115872             88641 ns/op           33600 B/op        400 allocs/op
Wrappable in 10sns/opB/opallocs/op
errwrap159575.872515.828000300
github.com/pkg/errors145235.48277133600400

github.com/pkg/errors에 비해 처리 능력이 약 9.87% 향상되었다. 그 외에도 전반적으로 향상되었다.

How to use

go get github.com/p9595jh/errwrap
import "github.com/p9595jh/errwrap"
profile
생각대로 되지 않을 때, 비로소 코딩은 재미있는 법.

0개의 댓글