Golangエラー処理
1.1Golang公式ライブラリのエラーサポート
(1)Golangエラーは比較的軽量であり、エラーエラーは組み込みパッケージの下にエラーインターフェイスを実装するだけで済みます。
type error interface {
Error() string
}
(2)Golandのデフォルトのサポートは、エラーパッケージの下の実装として実装されています
// errors.go文件
package errors
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
(3)ラップのサポートは1.3以降でサポートされ、主にUnwrap、Is、およびAsメソッドでサポートされます。また、%wを使用してラップ関数を実装しますが、スタック情報は含まれていません
// wrap.go
func Unwrap(err error) error {
u, ok := err.(interface {
Unwrap() error
})
if !ok {
return nil
}
return u.Unwrap()
}
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
for {
if isComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
// TODO: consider supporting target.Is(err). This would allow
// user-definable predicates, but also may allow for coping with sloppy
// APIs, thereby making it easier to get away with them.
if err = Unwrap(err); err == nil {
return false
}
}
}
func As(err error, target interface{}) bool {
if target == nil {
panic("errors: target cannot be nil")
}
val := reflectlite.ValueOf(target)
typ := val.Type()
if typ.Kind() != reflectlite.Ptr || val.IsNil() {
panic("errors: target must be a non-nil pointer")
}
if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) {
panic("errors: *target must be interface or implement error")
}
targetType := typ.Elem()
for err != nil {
if reflectlite.TypeOf(err).AssignableTo(targetType) {
val.Elem().Set(reflectlite.ValueOf(err))
return true
}
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
err = Unwrap(err)
}
return false
}
var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
1.2golang公式ライブラリによって実装された拡張パッケージ/エラー
拡張エラーのサポート:https://github.com/pkg/errors
pkg / errorsは、公式ライブラリエラーのパッケージを提供します。エラー情報を提供するだけでなく、エラースタック情報を追跡することもできます。
1.3golangのエラー型処理の姿勢
(1)事前定義された特定のエラーであるSentinel Errorは、errors.New()メソッドによって作成された定義済みエラーを指します。例:var Cancelled = errors.New( "context cancelled")
(2)エラータイプ、自己定義エラータイプは、エラータイプを自分で定義し、エラーインターフェイスを実装することを意味します
type MyError struct {
Msg string
File string;
Line int
}
func (e *MyError) Error() string {
return fmt.Sprintf("%s:%d:%s",e.File,e.Line,e.Msg)
}
func test() error {
return &MyError{"Something happened","server.go",42}
}
(3)不透明なエラー、透過的なエラーは、次のように、特定のエラーコンテンツ情報を提供せずに、エラーが発生したかどうかに関する外部情報のみを提供することを指します。
type temporary interface {
Tempporary() bool
}
func IsTemporary(err error) bool {
te,ok := err.(temporary)
return ok && te.Temporary()
}
IsTemporary()メソッドを外部に提供して、特定の情報を外部に提供せずに、エラーかどうかを検出します
1.4golangエラー処理の実践
1.ビジネスシステムアプリケーションで、自分で作成した関数がエラーを返す場合は、errors.Newまたはerrors.Errorfを使用してエラー(保存されたスタック情報)を返します(ここで使用されるpkg / errorsパッケージ)
2.ビジネスシステムアプリケーションで、ビジネスシステムまたはプロジェクトの他のパッケージによって返されたエラーの場合、エラーは直接(パッケージなしで)返されます。たとえば、サービスレイヤーはdaoを呼び出します。
3.ビジネスシステムアプリケーションで、会社のサードパーティライブラリ、標準ライブラリ、または基本ライブラリが呼び出された場合は、WrapまたはWrapfを使用して根本原因をラップします(最小のエラーメッセージ)。
4.プログラムの先頭または作業中のゴルーチンの先頭にログを印刷し、%+ vを使用してスタック情報を記録します。ログが記録されている場合は、エラーを上位レベルにスローする必要はありません。
5.トップレベルでerrors.Causeを使用して根本エラー(根本原因)を取得し、センチネルエラーを判断します
6.ビジネスシステムアプリケーションでは、エラーコード(統合エラーコード)を使用して外の世界に戻ります
1.5単純な擬似コードの実装
var (
ErrDataNotFound = errors.New("record not found")
)
type User struct {
Id uint64 `json:"Id"`
Name string `json:"name"`
Age int32 `json:"age"`
}
type Dao interface {
Get(id uint64) interface{}
List() interface{}
Create()
Update()
Delete(id uint64)
}
type UserDao struct {}
// Dao层获取到底层错误,使用errors的Wrap进行包装
fuc(user *UserDao) Get(id uint64) (*User, error) {
user := User{}
err := db.Where("id = ?",id).Find(user).Error
if errors.Is(err,sql.ErrNoRows){
retrun errors.Wrap(err,fmt.Sprintf("find user null,user id: %v",id))
}
return &user,nil
}
// 业务层获取到错误直接往上层抛
type UserService struct {}
func (s *Service) FindUserByID(userID int) (*model.User, error) {
return dao.FindUserByID(userID)
}