错误处理与日志
1. Go错误处理基础
Go使用简单的error接口处理错误:
// 基本错误处理
file, err := os.Open("config.json")
if err != nil {
// 处理错误
log.Printf("无法打开配置文件: %v", err)
return err
}
defer file.Close()
// 错误比较
if err == io.EOF {
// 处理EOF
}
// 自定义错误
var ErrNotFound = errors.New("未找到")
func findUser(id int) (*User, error) {
if id <= 0 {
return nil, ErrNotFound
}
// ...
}
2. 错误包装与上下文
Go 1.13+引入了错误包装:
import "errors"
func processFile(path string) error {
data, err := readFile(path)
if err != nil {
return fmt.Errorf("处理文件失败: %w", err)
}
// ...
}
func readFile(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("读取文件 %s 失败: %w", path, err)
}
return data, nil
}
// 使用errors.Is检查特定错误
err := processFile("config.json")
if errors.Is(err, os.ErrNotExist) {
// 处理文件不存在的特殊情况
}
3. 自定义错误类型
对于更复杂的错误场景,可以定义自定义错误类型:
type HTTPError struct {
Code int
Message string
Err error
}
func (e *HTTPError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%d: %s (%v)", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
func NewHTTPError(code int, message string, err error) *HTTPError {
return &HTTPError{
Code: code,
Message: message,
Err: err,
}
}
// 使用示例
func getUserHandler(w http.ResponseWriter, r *http.Request) {
user, err := getUser(r.URL.Query().Get("id"))
if err != nil {
httpErr := NewHTTPError(http.StatusNotFound, "用户未找到", err)
log.Println(httpErr)
http.Error(w, httpErr.Message, httpErr.Code)
return
}
json.NewEncoder(w).Encode(user)
}
4. 日志记录基础
Go标准库log提供基本日志功能:
// 基本日志配置
log.SetPrefix("WEB: ")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
// 不同级别日志
log.Println("普通日志") // 标准输出
log.Printf("%s日志", "格式化")
log.Fatalln("致命错误") // 输出后调用os.Exit(1)
log.Panicln("恐慌日志") // 输出后调用panic()
5. 结构化日志(使用zap)
企业应用推荐使用结构化日志库如zap:
import "go.uber.org/zap"
func main() {
// 生产环境配置(高性能JSON格式)
logger, _ := zap.NewProduction()
defer logger.Sync() // 刷新缓冲
// 开发环境配置(易读控制台格式)
// logger, _ := zap.NewDevelopment()
// 记录结构化日志
logger.Info("用户登录",
zap.String("username", "alice"),
zap.Int("attempt", 3),
zap.Duration("duration", time.Second),
)
// 错误日志示例
if err := doSomething(); err != nil {
logger.Error("操作失败",
zap.String("module", "payment"),
zap.Error(err),
)
}
}
6. Gin框架中的错误处理
func main() {
r := gin.New()
// 全局恢复中间件
r.Use(gin.Recovery())
// 自定义错误处理
r.Use(func(c *gin.Context) {
c.Next() // 处理请求
// 检查是否有错误
if len(c.Errors) > 0 {
for _, err := range c.Errors {
log.Printf("请求错误: %v", err)
}
c.JSON(http.StatusInternalServerError, gin.H{
"errors": c.Errors.Errors(),
})
}
})
r.GET("/", func(c *gin.Context) {
if err := doSomething(); err != nil {
c.Error(err) // 收集错误
return
}
c.String(200, "OK")
})
r.Run(":8080")
}
提示:良好的错误处理和日志记录是系统可维护性的关键。建议在项目早期就建立统一的错误处理规范。
