我的golang error 最佳实践

mb6013c3565bec6 · · 410 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

背景

在完成API接口、公共包给他人使用等类似工作时,除了对外提供功能外,文档也是非常重要的内容。文档输出的是可复制的能力,使用者通过文档快速学习了解使用,而不用多次找到开发人员重复答疑。

其中问的最多最频繁的就是错误码定义了,通常是需要给出所有返回错误的文档。

本文将分享我golang中error最佳实践,该实践不仅编码非常简单,而且做到了代码即文档,写完就自动生成了交付文档。

直接上代码

package api

import (
    "fmt"
    "runtime"
    "strconv"
    "strings"
)

type Error int16

const (
    ErrOK = Error(0)
    // 大于0的异常属于业务调用方错误,通常是需要调用方自行处理
    ErrForbidden    = Error(1000)
    ErrParams       = Error(1001)
    ErrInvalidToken = Error(2001)
    // 小于0的错误码为系统内部异常,通常是需要开发运维去解决的
    ErrBugInvalidData = Error(-1001)
    ErrDbFail         = Error(-1002)
)

func (me Error) detail() (string, string) {
    switch me {
    case ErrOK:
        return "OK", "正常"
    case ErrDbFail:
        return "ErrDbFail", "数据库操作异常,内部服务请转运维处理"
    case ErrBugInvalidData:
        return "ErrBugInvalidData", "内部产生了异常数据,属于BUG请开发处理"
    case ErrForbidden:
        return "ErrForbidden", "未授权"
    case ErrParams:
        return "ErrParams", "请求参数错误"
    case ErrInvalidToken:
        return "ErrInvalidToken", "鉴权Token不合法"
    default:
        return "Unknown", strconv.Itoa(int(me))
    }
}

// ##### 以下代码无需任何变更 #####

func (me Error) Error() string {
    s, _ := me.detail()
    return s
}

// 转整数,返回错误码时使用
func (me Error) Code() int16 { return int16(me) }

// 生成文档
func (me Error) Doc() string {
    sb := &strings.Builder{}
    _, file, _, _ := runtime.Caller(0)
    sb.WriteString(file)
    sb.WriteString("\n")
    fn := func(sb *strings.Builder, start, end, step int16) {
        for {
            e := Error(start)
            s, d := e.detail()
            if "Unknown" != s {
                sb.WriteString(fmt.Sprintf("%d %s %s\n", e.Code(), s, d))
            }
            if start == end {
                break
            } else {
                start += step
            }
        }
    }
    fn(sb, 0, 0, 1)
    fn(sb, 1, 32767, 1)
    fn(sb, -1, -32768, -1)
    return sb.String()
}复制代码

一行代码生成文档

fmt.Println(Error(0).Doc())

输出:
/woo/litim/errdef.go
0 OK 正常
1000 ErrForbidden 未授权
1001 ErrParams 请求参数错误
2001 ErrInvalidToken 鉴权Token不合法
-1001 ErrBugInvalidData 内部产生了异常数据,属于BUG请开发处理
-1002 ErrDbFail 数据库操作异常,内部服务请转运维处理复制代码

其它优点

  1. 作为error类型非常通用同时做到了const定义,防止运行时修改。这样外部调用接口后可以通过返回的Error做判断
err:=api.Call()
if err == api.ErrForbidden {
}复制代码
  1. 只需三行代码增加新的Error,非常方便
const ErrXXXX=Error(6) // 定义

// 加入case 返回错误提示
switch me {
 case ErrXXXX:
        return "ErrXXXX", "something wrong"
}复制代码
  1. 编译阶段检查防止Error Code重复定义
    switch不允许重复的case项,如果重复会编译错误,智能的ide将自动定位到重复的项

  2. 通过大于0和小于0来将错误进行分类,快速定位
    故障单瞄一眼心里就有底,大于0业务方先查起,小于0低调点默默的找问题。降低强势扯皮最后啪啪啪打脸发生概率。

  3. 建议
    将错误码从正负1000开始,999以内的作为救命用保留。祝愿您永远用不到999(救救救)以内。


有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:mb6013c3565bec6

查看原文:我的golang error 最佳实践

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

410 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传