zoukankan      html  css  js  c++  java
  • Go-errors第三方包学习

    写程序中难免会遇到 error 类型的值, 对于处理 或者 创建 error 的方法, go 标准库里 只有简单的 error.Error() 返回 string (错误的文本信息),

    这样对于调试代码获得的信息非常有限, 所以这里安装了一个 第三方 error 包 github.com/pkg/errors

    首先是 new 方法

    Go 语言使用 error 类型来返回函数执行过程中遇到的错误,如果返回的 error 值为 nil,则表示未遇到错误,否则 error 会返回一个字符串,用于说明遇到了什么错误。通俗的说,error就是一个接口而已,定义如下:

    type error interface {
        Error() string
    
    }

    New方法
    将字符串 text 包装成一个 error 对象返回
    New returns an error that formats as the given text.

    func New(text string) error

    例子:
    看看io.go中的定义:

    var ErrShortWrite    = errors.New("short write")
    var ErrShortBuffer   = errors.New("short buffer")
    var EOF              = errors.New("EOF")
    var ErrUnexpectedEOF = errors.New("unexpected EOF")
    var ErrNoProgress    = errors.New("multiple Read calls return no data or error")
    package main
    
    import (
       "fmt"
       "github.com/pkg/errors"
    )
    
    func main() {
       err := errors.New("create error")
       fmt.Printf("%+v", err)
    }

    输出:

    create error
    main.main
        /Users/qq/Desktop/code/go/pkgErrors/main.go:9
    runtime.main
        /usr/local/go/src/runtime/proc.go:200
    runtime.goexit
        /usr/local/go/src/runtime/asm_amd64.s:1337
    Process finished with exit code 0

    显然 相对于 go 标准库里的 error ,这里创建的 error 更详细, 主要是打印出了堆栈

    来看一下 new 函数做了些什么

    func New(message string) error {
       return &fundamental{
          msg:   message,
          stack: callers(),
       }
    }

    可以看到 new 函数 接受一个 string 代表你需要展示的错误描述, 返回一个实现了 error 接口的结构体 fundamental

    其中 fundamental 结构体的 stack 调用了 callers() 方法, 正是这个方法,才能让我们的 error 能返回 错误的堆栈信息

    func callers() *stack {
       const depth = 32
       var pcs [depth]uintptr
       n := runtime.Callers(3, pcs[:])
       var st stack = pcs[0:n]
       return &st
    }

    注意, 这里 callers()只是调用了 runtime.Callers 拿到了对应的指针,并没有拿到对应的文件名和行, 并且这里的 depth 值限制了 最多保存 32 次堆栈

    可以用 debug 工具验证一下

     可以看到 stack 只是一个保存了指针的数组, 那么为什么 format 格式化输出的时候就出现了详细的 文件名和行号呢?

    重点2

    fundamental 实现了 Format 方法, 让我们的格式化输出不打印原本的结构体, 转而输出这个方法想输出的东西

    func (f *fundamental) Format(s fmt.State, verb rune) {
       switch verb {
       case 'v':
          if s.Flag('+') {
             io.WriteString(s, f.msg)
             f.stack.Format(s, verb)
             return
          }
          fallthrough
       case 's':
          io.WriteString(s, f.msg)
       case 'q':
          fmt.Fprintf(s, "%q", f.msg)
       }
    }

    刚才我们知道 堆栈信息保存在 stack 这个成员属性里面, 所以看下 stack.Format 做了些什么

    func (s *stack) Format(st fmt.State, verb rune) {
       switch verb {
       case 'v':
          switch {
          case st.Flag('+'):
             for _, pc := range *s {
                f := Frame(pc)
                fmt.Fprintf(st, "
    %+v", f)
             }
          }
       }
    }

    可以看到 嵌套的很深, 我们在第二个 format 方法依然没看到具体 获取文件名的方法, 可以看到 这个 format 只针对 %+v 这种做处理了, 这也是为什么我们只能通过 %+v 才能打印到堆栈信息的原因, 这里将我们的 指针数组做出了循环, 然后每个指针去创建了 Frame 的结构体

    func (f Frame) Format(s fmt.State, verb rune) {
       switch verb {
       case 's':
          switch {
          case s.Flag('+'):
             pc := f.pc()
             fn := runtime.FuncForPC(pc)
             if fn == nil {
                io.WriteString(s, "unknown")
             } else {
                file, _ := fn.FileLine(pc)
                fmt.Fprintf(s, "%s
    	%s", fn.Name(), file)
             }
          default:
             io.WriteString(s, path.Base(f.file()))
          }
       case 'd':
          fmt.Fprintf(s, "%d", f.line())
       case 'n':
          name := runtime.FuncForPC(f.pc()).Name()
          io.WriteString(s, funcname(name))
       case 'v':
          f.Format(s, 's')
          io.WriteString(s, ":")
          f.Format(s, 'd')
       }
    }

    终于找到 熟悉的 runtime.FuncForPc() 和 FileLine 函数了

    代码量不算多, 但是通过接口的这种隐形关系, 能自动帮我们实现这种实用的功能真的很棒。

    对于这个包还有个好处是 他是可以兼容 官方的 error 接口的,里面也有互相转换的方法。

  • 相关阅读:
    网络编程TCP
    collections模块
    异常处理
    hashlib模块
    configparse模块
    logging模块
    序列化模块
    os模块
    时间模块
    random模块
  • 原文地址:https://www.cnblogs.com/Paul-watermelon/p/12192777.html
Copyright © 2011-2022 走看看