golang简单错误处理与分类原语插件库errors的使用

Golang简单错误处理与分类原语插件库errors的使用

Neuron Logo

errors包提供了简单的Golang错误和分类原语。

Class

包定义了一个极速的分类系统。Class是一个uint32包装器,由MajorMinorIndex子分类组成。每个子分类有不同的位长度:Major占8位,Minor占10位,Index占14位,总共32位。

示例:

00000010101000101000010011001111 分解为:
00000010 - major (8 bit)
        1010001010 - minor (10 bit)
                  00010011001111 - index (14 bit)

Class可以通过三种方式组成:

  • 仅Major - Class是Major单例
  • Major和Minor - 不需要三重子分类
  • Major、Minor和Index - 需要分解

使用NewMajorMustNewMajor函数创建MajorNewMinorMustNewMinor创建MinorNewIndexMustNewIndex创建Index

接口

包提供了简单的错误处理接口和函数,可以创建简单和详细的分类错误。

ClassError

ClassError接口通过Class方法提供错误分类。

DetailedError

DetailedError是用于存储和处理人类可读细节、包含实例ID和运行时调用操作的错误接口。实现了ClassErrorDetailerOperationerIndexererror接口。

Detailer

Detailer接口允许设置和获取人类可读的细节 - 完整句子。

Operationer

OperationError接口用于获取运行时操作信息。

Indexer

Indexer接口用于获取每个错误实例的唯一’ID’。

错误处理

包包含两种错误结构实现:

  • 简单错误结构
  • 详细错误结构

简单错误

简单错误实现了ClassError接口。它是轻量级错误,仅包含消息和其类。

使用NewNewf函数创建。

示例:

import "github.com/neuronlabs/errors"
// 假设我们已经定义了一些ClassInvalidRequest
var ClassInvalidInput errors.Class

func createValue(input int) error {
    if input < 0 {
        return errors.New(ClassInvalidInput, "provided input lower than zero")
    }

    if input > 50 {
        return errors.Newf(ClassInvalidInput, "provided input value: '%d' is not valid", input) 
    }
    // 执行逻辑
    return nil
}

详细错误

详细错误结构(detailedError)实现了DetailedError

它包含关于给定错误实例的许多信息:

  • 人类可读的Details
  • 运行时函数调用Operations
  • 唯一错误实例ID

使用NewDetNewDetf函数创建详细错误。

示例

import (
    "fmt"
    "os"

    "github.com/neuronlabs/errors"
)

var (
    ClInputInvalidValue errors.Class
    ClInputNotProvided  errors.Class
)

func init() {
    initClasses()
}

func initClasses() {
    inputMjr := errors.MustNewMajor()
    invalidValueMnr := errors.MustNewMinor(inputMjr)
    ClInputInvalidValue = errors.MustNewMinorClass(inputMjr, invalidValueMnr)
    
    inputNotProvidedMnr := errors.MustNewMinor(inputMjr)
    ClInputNotProvided = errors.MustNewMinorClass(inputMjr, inputNotProvidedMnr)
}


func main() {
    input, err := getInput()
    if err == nil {
        // 一切正常
        os.Exit(0)
    }

    if classed, ok := err.(errors.ClassError); ok {
        if classed.Class() == ClInputNotProvided {
            fmt.Println("No required integer arguments provided.")
            os.Exit(1)
        }
    }

    var details string
    detailed, ok := err.(errors.DetailedError)
    if ok {
        details = detailed.Details()
    } else {
        details = err.Error()
    }
    fmt.Printf("Invalid input value provided: '%s'\n", details)
    os.Exit(1)    
}


func checkInput(input int) error {
    if input < 0 {
        err := errors.NewDet(ClassInputInvalidValue, "provided input lower than zero")        
        err.SetDetailsf("The input value provided to the function is invalid. The value must be greater than zero.")
        return err
    }

    if input > 50 {
        err := errors.NewDetf(ClassInvalidInput, "provided input value: '%d' is not valid", input) 
        err.SetDetailsf("The input value: '%d' provided to the function is invalid. The value can't be greater than '50'.", input)
        return err
    }
    // 执行逻辑
    return nil
}

func getInput() (int, error) {
    if len(os.Args) == 0 {
        return errors.New(ClInputNotProvided, "no input provided")
    }

    input, err := strconv.Atoi(os.Args[0])
    if err != nil {
        err := errors.NewDetf(ClInputInvalidValue, "provided input is not an integer")        
        err.SetDetail(err.Error())
        return 0, err
    }

    if err = checkInput(input); err != nil {
        return 0, err
    }
    return input, nil
}

更多关于golang简单错误处理与分类原语插件库errors的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang简单错误处理与分类原语插件库errors的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 错误处理与分类:errors库的使用

在Go语言中,错误处理是一个核心概念。标准库提供了基本的错误处理机制,而第三方库如github.com/pkg/errors提供了更强大的功能。下面我将介绍如何使用这些工具进行错误处理和分类。

标准库错误处理

Go标准库提供了基本的错误接口和创建方法:

package main

import (
	"errors"
	"fmt"
)

func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("division by zero")
	}
	return a / b, nil
}

func main() {
	result, err := divide(10, 0)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Println("Result:", result)
}

pkg/errors 库

github.com/pkg/errors 提供了更强大的错误处理功能:

安装

go get github.com/pkg/errors

基本用法

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

func readFile() error {
	// 模拟底层错误
	return errors.New("file not found")
}

func process() error {
	err := readFile()
	if err != nil {
		return errors.Wrap(err, "process failed")
	}
	return nil
}

func main() {
	err := process()
	if err != nil {
		fmt.Printf("Error: %+v\n", err)
		// 输出:
		// Error: process failed: file not found
		// main.process
		//         /path/to/file.go:15
		// main.main
		//         /path/to/file.go:21
	}
}

错误分类

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

// 定义错误类型
var (
	ErrNotFound     = errors.New("not found")
	ErrUnauthorized = errors.New("unauthorized")
)

func checkAccess(role string) error {
	if role != "admin" {
		return errors.Wrap(ErrUnauthorized, "access denied")
	}
	return nil
}

func main() {
	err := checkAccess("user")
	switch {
	case errors.Is(err, ErrUnauthorized):
		fmt.Println("Unauthorized access attempt")
		fmt.Printf("Original error: %v\n", errors.Cause(err))
	case errors.Is(err, ErrNotFound):
		fmt.Println("Resource not found")
	default:
		fmt.Println("Unknown error")
	}
}

高级用法

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type ValidationError struct {
	Field   string
	Message string
}

func (e ValidationError) Error() string {
	return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

func validateInput(input string) error {
	if len(input) < 5 {
		return ValidationError{
			Field:   "input",
			Message: "too short, minimum 5 characters",
		}
	}
	return nil
}

func processInput(input string) error {
	err := validateInput(input)
	if err != nil {
		return errors.Wrap(err, "input validation failed")
	}
	return nil
}

func main() {
	err := processInput("abc")
	var valErr ValidationError
	if errors.As(err, &valErr) {
		fmt.Printf("Validation error: %v\n", valErr)
		fmt.Printf("Field: %s, Message: %s\n", valErr.Field, valErr.Message)
	}
}

最佳实践

  1. 使用errors.Wrap在错误链中添加上下文信息
  2. 使用errors.Is检查特定错误
  3. 使用errors.As提取特定错误类型
  4. 在应用边界记录错误(如HTTP handler、main函数等)
  5. 避免过度包装错误,保持错误链简洁
package main

import (
	"fmt"
	"github.com/pkg/errors"
)

func databaseQuery() error {
	return errors.New("connection timeout")
}

func businessLogic() error {
	err := databaseQuery()
	if err != nil {
		return errors.Wrap(err, "failed to execute query")
	}
	return nil
}

func apiHandler() error {
	err := businessLogic()
	if err != nil {
		// 在应用边界记录完整堆栈
		fmt.Printf("API error: %+v\n", err)
		return errors.New("internal server error") // 对客户端隐藏细节
	}
	return nil
}

func main() {
	err := apiHandler()
	if err != nil {
		fmt.Println("Client error:", err)
	}
}

通过合理使用这些技术,你可以构建健壮且易于调试的错误处理系统。pkg/errors库特别适合需要错误堆栈跟踪和错误分类的中大型项目。

回到顶部