使用errors.Wrapf()代替log.Error()

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 使用errors.Wrapf()代替log.Error()

介绍不同语言的错误处理机制:Error handling patterns

Musings about error handling mechanisms in programming languages

项目中 main调func1,func1调取func2...

这样就会出现很多的 if err != nil { log.Printf()} , 在Kibana上查看时会搜到多条日志, 需要逐级定位, 确定错误抛出的点

希望只有一条, 能清晰看到整个堆栈调用信息


使用log.xxxx方法:

package main
import (
  "fmt"
  "log"
  "strconv"
  "time"
)
func init() {
  log.SetFlags(log.Lshortfile | log.LstdFlags)
}
func main() {
  str := "123a"
  rs, err := func1(str)
  if err != nil {
    log.Printf("err is (%+v)\n", err)
    return
  }
  fmt.Println("最终结果为:", rs)
}
func func1(str string) (int, error) {
  b, err := func2()
  if err != nil {
    log.Printf("There is func11111,  func2 err(%+v)\n", err)
  }
  if b == false {
    strInt, err := strconv.Atoi(str)
    if err != nil {
      log.Printf("There is func11111, err(%+v)\n", err)
    }
    return strInt, err
  }
  return 0, nil
}
func func2() (bool, error) {
  now := time.Now().Unix()
  endTimeStr := "2021-08-06 20:00:0000"
  endtime, err := time.ParseInLocation("2006-01-02 15:04:05", endTimeStr, time.Local)
  if err != nil {
    log.Printf("There is func22222, err(%+v)\n", err)
    return false, err
  }
  if endtime.Unix() > now {
    return true, nil
  }
  return false, nil
}

执行结果:

2021/06/07 21:52:56 vs.go:56: There is func22222, err(parsing time "2021-08-06 20:00:0000": extra text: "00")
2021/06/07 21:52:56 vs.go:33: There is func11111,  func2 err(parsing time "2021-08-06 20:00:0000": extra text: "00")
2021/06/07 21:52:56 vs.go:40: There is func11111, err(strconv.Atoi: parsing "123a": invalid syntax)
2021/06/07 21:52:56 vs.go:20: err is (strconv.Atoi: parsing "123a": invalid syntax)

使用errors.Wrapf方法:

package main
import (
  "fmt"
  "github.com/pkg/errors"
  "strconv"
  "time"
)
func main() {
  str := "123a"
  rs, err := func1(str)
  if err != nil {
    fmt.Printf("err: %+v\n", err)
    //fmt.Println("err:", lastErr) //必须%+v才会打印完整堆栈信息,否则只打印错误信息
    return
  }
  fmt.Println("最终结果为:", rs)
}
func func1(str string) (int, error) {
  b, err := func2()
  if err != nil {
    err = errors.Wrapf(err, "There is func11111, func2 err, b is(%b) \n", b)
  }
  if b == false {
    var strInt int
    strInt, err = strconv.Atoi(str)
    if err != nil {
      err = errors.Wrapf(err, "There is func11111,str is(%s)\n", str)
    }
    return strInt, err
  }
  return 0, nil
}
func func2() (bool, error) {
  now := time.Now().Unix()
  endTimeStr := "2021-08-06 20:00:0000"
  endtime, err := time.ParseInLocation("2006-01-02 15:04:05", endTimeStr, time.Local)
  if err != nil {
    err = errors.Wrapf(err, "There is func22222,endTimeStr is(%s)\n", endTimeStr)
    return false, err
  }
  if endtime.Unix() > now {
    return true, nil
  }
  return false, nil
}

执行:

err: strconv.Atoi: parsing "123a": invalid syntax
There is func11111,str is(123a)
main.func1
        /Users/fliter/go/src/shuang/llog/1.go:39
main.main
        /Users/fliter/go/src/shuang/llog/1.go:13
runtime.main
        /usr/local/Cellar/go/1.16.3/libexec/src/runtime/proc.go:225
runtime.goexit
        /usr/local/Cellar/go/1.16.3/libexec/src/runtime/asm_amd64.s:1371

注意赋值这步, 必不可少!

有一个问题, 即对于f1调f2,f2调f3这种, 如果f3发生error, 可以逐级将error抛出.

但如果一个方法里有两个error, 则第二条会覆盖掉第一条,如上 err = errors.Wrapf(err, "There is func11111, func2 err, b is(%b) \n", b)这一条就被覆盖了

// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error {
  if err == nil {
    return nil
  }
  err = &withMessage{
    cause: err,
    msg:   fmt.Sprintf(format, args...),
  }
  return &withStack{
    err,
    callers(),
  }
}
func callers() *stack {
  const depth = 32
  var pcs [depth]uintptr
  n := runtime.Callers(3, pcs[:])
  var st stack = pcs[0:n]
  return &st
}
// Callers fills the slice pc with the return program counters of function invocations
// on the calling goroutine's stack. The argument skip is the number of stack frames
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
// 1 identifying the caller of Callers.
// It returns the number of entries written to pc.
//
// To translate these PCs into symbolic information such as function
// names and line numbers, use CallersFrames. CallersFrames accounts
// for inlined functions and adjusts the return program counters into
// call program counters. Iterating over the returned slice of PCs
// directly is discouraged, as is using FuncForPC on any of the
// returned PCs, since these cannot account for inlining or return
// program counter adjustment.
func Callers(skip int, pc []uintptr) int {
  // runtime.callers uses pc.array==nil as a signal
  // to print a stack trace. Pick off 0-length pc here
  // so that we don't let a nil pc slice get to it.
  if len(pc) == 0 {
    return 0
  }
  return callers(skip, pc)
}
func callers(skip int, pcbuf []uintptr) int {
  sp := getcallersp()
  pc := getcallerpc()
  gp := getg()
  var n int
  systemstack(func() {
    n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
  })
  return n
}

微信截图_20230925184703.png

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
Java 数据库连接 数据库
ERROR 11848
ERROR 11848
166 1
|
7月前
|
SQL
WARNING: too many parse errors' in the 12.2 Alert.log
WARNING: too many parse errors' in the 12.2 Alert.log
73 2
|
7月前
|
NoSQL Redis
解决(error) ERR Errors trying to SHUTDOWN. Check logs.问题~
解决(error) ERR Errors trying to SHUTDOWN. Check logs.问题~
207 0
ERR Errors trying to SHUTDOWN. Check logs.
ERR Errors trying to SHUTDOWN. Check logs.
122 0
|
JavaScript 前端开发 开发者
Error,Error,到底什么是Error
总所周知,当系统运行出现错误的时候,就会抛出一个 Error ,那么这个 Error 是什么?它是怎么来的?它又是怎么被抛出的?它又是怎么被捕获的?这些问题,我们一起来探讨一下。
234 0
|
关系型数据库 MySQL C++
Error:fatal error C1010: unexpected end of file while looking for precompiled head
Error:fatal error C1010: unexpected end of file while looking for precompiled head
120 0
C中error的使用
C中error的使用
98 0
|
Java 应用服务中间件 Windows
|
应用服务中间件 nginx
常见error
小榕
857 0