golang错误处理追踪与日志记录插件库eris的使用

Golang错误处理追踪与日志记录插件库eris的使用

eris是一个Go语言的错误处理库,提供了可读的堆栈跟踪和灵活的格式化支持。

安装

go get github.com/rotisserie/eris

为什么选择eris

eris的设计目标是让你通过错误包装、堆栈跟踪和输出格式化获得更多对错误处理的掌控。它生成的错误输出清晰展示了从调用栈顶部到错误源的完整路径。

使用eris

创建错误

var (
  // 全局错误值在包装错误或检查错误类型时很有用
  ErrInternalServer = eris.New("error internal server")
)

func (req *Request) Validate() error {
  if req.ID == "" {
    // 或者在源头返回新错误
    return eris.New("error bad request")
  }
  return nil
}

包装错误

relPath, err := GetRelPath("/Users/roti/", resource.AbsPath)
if err != nil {
  // 如果你想添加上下文可以包装错误
  return nil, eris.Wrapf(err, "failed to get relative path for resource '%v'", resource.ID)
}

格式化和记录错误

// 使用默认格式将错误格式化为JSON并启用堆栈跟踪
formattedJSON := eris.ToJSON(err, true)
fmt.Println(json.Marshal(formattedJSON)) // 序列化为JSON并打印
logger.WithField("error", formattedJSON).Error() // 或者直接传递给日志记录器

// 将错误格式化为字符串并打印
formattedStr := eris.ToString(err, true)
fmt.Println(formattedStr)

解释eris堆栈跟踪

eris创建的错误包含自动管理的堆栈跟踪。默认情况下,堆栈跟踪和所有包装层遵循与Go的runtime包相反的顺序,这意味着原始调用方法首先显示,错误的根本原因最后显示。

反转堆栈跟踪和错误输出

// 创建带有错误和堆栈反转选项的默认格式
format := eris.NewDefaultStringFormat(eris.FormatOptions{
  InvertOutput: true, // 反转错误输出(包装错误先显示)
  WithTrace: true,    // 启用堆栈跟踪输出
  InvertTrace: true,  // 反转堆栈跟踪输出(调用栈顶部先显示)
})

// 将错误格式化为字符串并打印
formattedStr := eris.ToCustomString(err, format)
fmt.Println(formattedStr)

检查错误

eris提供了几种检查和比较错误类型的方法:

// eris.Is - 如果特定错误出现在错误链中的任何位置则返回true
ErrNotFound := eris.New("error not found")
_, err := db.Get(id)
if eris.Is(err, ErrNotFound) {
  return eris.Wrapf(err, "error getting resource '%v'", id)
}

// eris.As - 在错误链中查找与给定目标匹配的第一个错误
var target *NotFoundError
_, err := db.Get(id)
if errors.As(err, &target) {
    return target
}

// eris.Cause - 解包错误直到达到原因(即错误链中的第一个/根错误)
ErrNotFound := eris.New("error not found")
_, err := db.Get(id)
if eris.Cause(err) == ErrNotFound {
  return eris.Wrapf(err, "error getting resource '%v'", id)
}

自定义分隔符格式化

// 使用自定义分隔符将错误格式化为字符串
formattedStr := eris.ToCustomString(err, Format{
  FormatOptions: eris.FormatOptions{
    WithTrace: true,   // 启用堆栈跟踪输出
  },
  MsgStackSep: "\n",   // 错误消息和堆栈帧数据之间的分隔符
  PreStackSep: "\t",   // 每个堆栈帧开头的分隔符
  StackElemSep: " | ", // 每个堆栈帧元素之间的分隔符
  ErrorSep: "\n",      // 错误链中每个错误之间的分隔符
})
fmt.Println(formattedStr)

编写自定义输出格式

// 获取解包的错误对象
uErr := eris.Unpack(err)
// 只将根错误消息发送到日志服务器而不是完整的错误跟踪
sentry.CaptureMessage(uErr.ErrRoot.Msg)

发送错误跟踪到Sentry

eris支持使用Sentry Go客户端SDK将错误跟踪发送到Sentry。

完整示例

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/rotisserie/eris"
)

var (
	ErrNotFound = eris.New("resource not found")
)

func main() {
	// 模拟一个错误链
	err := getResource("123")
	if err != nil {
		// 检查错误类型
		if eris.Is(err, ErrNotFound) {
			log.Println("resource not found error occurred")
		}

		// 格式化为JSON并打印
		formattedJSON := eris.ToJSON(err, true)
		jsonData, _ := json.MarshalIndent(formattedJSON, "", "  ")
		fmt.Println("JSON format:")
		fmt.Println(string(jsonData))

		// 格式化为字符串并打印
		fmt.Println("\nString format:")
		fmt.Println(eris.ToString(err, true))

		// 使用自定义格式
		fmt.Println("\nCustom format:")
		customFormat := eris.NewDefaultStringFormat(eris.FormatOptions{
			InvertOutput: true,
			WithTrace:    true,
			InvertTrace:  true,
		})
		fmt.Println(eris.ToCustomString(err, customFormat))
	}
}

func getResource(id string) error {
	// 模拟一个底层错误
	dbErr := fmt.Errorf("database connection failed")

	// 包装错误
	if id == "123" {
		return eris.Wrapf(dbErr, "failed to get resource with id '%s'", id)
	}
	return nil
}

这个示例展示了eris的主要功能,包括错误创建、包装、格式化以及自定义输出。eris提供了灵活的错误处理方式,可以帮助开发者更好地理解和调试错误。


更多关于golang错误处理追踪与日志记录插件库eris的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang错误处理追踪与日志记录插件库eris的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang错误处理追踪与日志记录插件库eris使用指南

eris是一个强大的Go错误处理库,它提供了更好的错误包装、堆栈追踪和结构化错误处理能力。相比标准库的errors包,eris能提供更丰富的错误上下文信息。

安装eris

go get github.com/rotisserie/eris

基本使用

创建新错误

package main

import (
	"fmt"
	"github.com/rotisserie/eris"
)

func main() {
	// 创建新错误并自动捕获堆栈
	err := eris.New("something went wrong")
	fmt.Println(err)
	
	// 格式化错误
	err = eris.Errorf("invalid value: %v", "foo")
	fmt.Println(err)
}

错误包装

eris允许你包装错误并保留原始错误的上下文:

func processData(data string) error {
	if data == "" {
		return eris.New("empty data")
	}
	// 处理数据...
	return nil
}

func main() {
	err := processData("")
	if err != nil {
		// 包装错误并添加更多上下文
		wrappedErr := eris.Wrap(err, "failed to process data")
		fmt.Println(wrappedErr)
	}
}

错误堆栈追踪

eris会自动捕获错误的堆栈信息:

func main() {
	err := outerFunction()
	if err != nil {
		// 打印带堆栈的错误信息
		fmt.Println(eris.ToString(err, true))
	}
}

func outerFunction() error {
	return innerFunction()
}

func innerFunction() error {
	return eris.New("error occurred")
}

结构化错误处理

eris提供了方法来检查错误类型:

func main() {
	err := processData("invalid")

	// 检查是否是eris错误
	if eris.Is(err, eris.New("invalid data")) {
		fmt.Println("specific error occurred")
	}

	// 解包错误
	unwrapped := eris.Unwrap(err)
	fmt.Println("Unwrapped error:", unwrapped)
}

日志集成

eris可以与常见日志库集成:

import (
	"github.com/sirupsen/logrus"
	"github.com/rotisserie/eris"
)

func main() {
	logger := logrus.New()
	
	err := processData("")
	if err != nil {
		// 将eris错误转换为logrus字段
		logger.WithFields(eris.ToLogrusFields(err)).
			Error("Failed to process data")
	}
}

自定义错误格式

func main() {
	err := processData("")
	if err != nil {
		// JSON格式输出
		jsonStr := eris.ToJSON(err)
		fmt.Println(jsonStr)
		
		// 自定义格式
		fmt.Printf("%+v\n", err)  // 详细格式
		fmt.Printf("%v\n", err)   // 简洁格式
	}
}

最佳实践

  1. 在应用程序边界处包装错误:当错误跨越模块/层边界时进行包装
  2. 避免过度包装:不要在每个函数调用处都包装错误
  3. 添加有意义的上下文:包装时提供有助于调试的信息
  4. 使用eris.Is进行错误比较:而不是直接使用==比较
func serviceCall() error {
	// 模拟服务调用失败
	return eris.New("service unavailable")
}

func businessLogic() error {
	err := serviceCall()
	if err != nil {
		// 添加业务上下文
		return eris.Wrapf(err, "failed to execute business logic")
	}
	return nil
}

func main() {
	err := businessLogic()
	if eris.Is(err, eris.New("service unavailable")) {
		fmt.Println("Handle service unavailable error")
	}
	
	// 输出完整错误信息
	fmt.Printf("%+v\n", err)
}

eris通过提供丰富的错误上下文和堆栈信息,大大简化了Go应用程序的调试和错误处理流程。相比标准错误处理,它能让你更快定位问题根源。

回到顶部