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

news2024/11/24 9:16:55

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

Musings about error handling mechanisms in programming languages[2]

项目中 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 0nil
}

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 truenil
 }

 return falsenil

}

执行结果:

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 0nil
}

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 truenil
 }

 return falsenil

}

执行:

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), nilnil0)
 })
 return n
}
alt



参考:

golang 打印error的堆栈信息[3]

Go错误处理最佳实践[4]

参考资料

[1]

Error handling patterns: https://andreabergia.com/blog/2023/05/error-handling-patterns/

[2]

Musings about error handling mechanisms in programming languages: https://www.amazingcto.com/best-way-to-handle-errors-for-a-programming-language/

[3]

golang 打印error的堆栈信息: https://blog.csdn.net/fwhezfwhez/article/details/82854986

[4]

Go错误处理最佳实践: https://lailin.xyz/post/go-training-03.html

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/949989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决iPad故障问题的三种重置方式:出厂恢复、软重置和强制重启

这篇文章解释了如何重新启动iPad,以及如果它没有响应,如何强制它重新启动。它还包括如何重置iPad的其他选项。 一、重启iPad的最简单方法(所有型号) 基本的重新启动是最容易做到的,也是遇到硬件问题时应该尝试的第一件事。该过程不会删除你的数据或设置。请执行以下步骤…

源码编译安装opencv4.6.0,别的版本也行

1.下载opencv4.6.0 系统: ubuntu 1804 64位点我下载opencv 4.6.0 https://codeload.github.com/opencv/opencv/zip/refs/tags/4.6.0 2.编译安装opencv4.6.0 进入到下载目录下: unzip -qo opencv-4.6.0.zip -d ./ cd opencv-4.6.0 mkdir build cd build cmake -D…

六、事务-5.事务隔离级别

一、概念 从上至下,隔离级别越来越高,数据越来越安全,性能越来越低。 读未提交read uncommitted —— 隔离级别最低(数据安全性最差),性能最好串行化serializable ——隔离级别最高,性能最差 …

【GNN+异常检测】Deep Anomaly Detection on Attributed Networks

文章目录 论文简介摘要属性网络相关介绍存在的问题论文贡献1. 提出的模型 - Dominant2. 实验 论文简介 原文题目:Deep Anomaly Detection on Attributed Networks 中文题目:基于属性网络的深度异常检测 发表会议:SIAM International Conferen…

Java“魂牵”京东店铺所有商品数据接口,京东店铺所有商品API接口,京东API接口申请指南

要通过京东的API获取店铺所有商品数据,您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例,展示如何通过京东开放平台API获取整店商品数据: 首先,确保您已注册成为京东开放平台的开发者,…

快速提升代码性能:FastAPI多线程的利用

在现代网络应用中,高性能和快速响应是至关重要的,Python 的 FastAPI 框架以其出色的性能和简单易用的特点,成为了许多开发者的首选。然而,在某些场景下,单线程运行可能无法满足需求,这时候就需要考虑使用多…

【AutoLayout案例08-基于AutoLayout的动画 Objective-C语言】

一、好,那么,再给大家说一个什么呢 1.再给大家说一个,这么一个东西, 我们之前,通过frame,是不是可以通过animateWithDuration,可以执行动画吧 通过直接设置frame的方式,可以执行动画, 我们这里,通过约束,的方式, 也可以执行动画, 通过约束,也可以执行动画, …

基于FlaUI自动化+chatGPT实现微信自动回复

先看效果图 本次主要介绍如何实现自动回复: 1.将文件传输助手置顶,模拟鼠标点击文件传输助手; 2.一直刷新会话列表,有新的消息就需要回复内容; 3.当刷新到新的消息时,模拟鼠标点击到对应的会话人&#x…

擦除信道(erasure channel)

定义 二进制擦除通道(BEC)是一种信道模型。发送端发送一个比特(0或1),接收端要么正确接收该比特(0或1),要么以概率 P e P_{e} Pe​接收到该比特没有被接收的信息(即“擦除…

公司电脑文件数据透明加密、防泄密系统

一套利用驱动层透明加密技术实现电子文件安全加密的防护产品,从源头上保障数据安全和使用安全的加密系统。该系统遵循基于文件生命周期安全防护的思想,集成了密码学、访问控制和审计跟踪等技术手段,对企事业单位电子文件的存储、访问、传播和…

【UE 材质】实现角度渐变材质、棋盘纹理材质

目标 步骤 一、角度渐变材质 1. 首先通过“Mask”节点将"Texture Coordinate" 节点的R、G通道分离 2. 通过“RemapValueRange”节点将0~1范围映射到-1~1 可以看到此时R通道效果: G通道效果: 继续补充如下节点 二、棋盘纹理材质 原视频链接&…

Docker(三) 创建Docker镜像

一、在Docker中拉取最基本的Ubuntu系统镜像 搜索Ubuntu镜像 Explore Dockers Container Image Repository | Docker Hub 下载镜像 docker pull ubuntu:22.04 二、在镜像中添加自己的内容 使用ubuntu镜像创建容器 docker run -it ubuntu:20.04 /bin/bash 在容器中创建了一个文…

成集云 | 多维表格自动化管理jira Server项目 | 解决方案

源系统成集云目标系统 方案介绍 基于成集云集成平台,在多维表格中的需求任务信息自动创建、更新同步至 Jira Server 的指定项目中,实现多维表格中一表管理 Jira Server 中的项目进度。 维格表是一种新一代的团队数据协作和项目管理工具&…

day-01 Docker

一、docker简介 Docker 是一种开源的容器化平台,它可以帮助开发人员将应用程序及其依赖项打包成一个独立的、可移植的容器,而无需担心环境差异和依赖问题。通过使用 Docker,您可以更轻松地创建、分发和运行应用程序,无论是在开发、…

Apipost: 开发者们的瑞士军刀

在当今的数字化时代,数据流通是推动社会进步的关键因素之一。其中,API(应用编程接口)已经成为跨平台数据交互的标准。然而,API开发和管理并非易事,Apipost一体化研发协作赋能平台,支持从API设计…

并发编程(四)---死锁 synchronized、volatile详解

一.关键字 1.synchronized关键字: a.重量级锁,功能完整的锁 b.修饰类、方法、静态方法、代码块,但不可以在声明的时候修饰 c.synchronized是实现同步的基础,Java中不管是对象还是方法,都可加上锁。synchronized分为三…

音频应用编程

目录 ALSA 概述alsa-lib 简介sound 设备节点alsa-lib 移植编写一个简单地alsa-lib 应用程序一些基本概念打开PCM 设备设置硬件参数 ALPHA I.MX6U 开发板支持音频,板上搭载了音频编解码芯片WM8960,支持播放以及录音功能! 本章我们来学习Linux …

探索散列表和哈希表:高效存储与快速检索的魔法

文章目录 散列函数的原理散列表和哈希表的概念与操作解决冲突的方法案例分析:电话簿的实现拓展:性能与碰撞结论 🎉欢迎来到数据结构学习专栏~探索散列表和哈希表:高效存储与快速检索的魔法 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#…

【模拟集成电路】反馈系统加载效应——基础到进阶(三)

【模拟集成电路】反馈系统加载效应——基础到进阶(三) -----------------------文末附往期文章链接-------------------- 1.概述2.二端口网络方法2.1二端口网络模型2.2电压-电压反馈的加载2.2电流-电压反馈的加载2.3电压-电流反馈的加载2.4电流-电流反馈…

一文速学-让神经网络不再神秘,一天速学神经网络基础-输出层(四)

前言 思索了很久到底要不要出深度学习内容,毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新,很多坑都没有填满,而且现在深度学习的文章和学习课程都十分的多,我考虑了很久决定还是得出神经网络系列文章,不…