go语言错误处理(二)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: go语言错误处理

go语言错误处理(一)https://developer.aliyun.com/article/1391521


log 包实现了简单的日志功能:默认的 log 对象向标准错误输出中写入并打印每条日志信息的日期和时间。除了 Println 和 Printf 函数,其它的致命性函数都会在写完日志信息后调用 os.Exit(1),那些退出函数也是如此。而 Panic 效果的函数会在写完日志信息后调用 panic;可以在程序必须中止或发生了临界错误时使用它们.下面展示 panic,defer 和 recover 怎么结合使用的完整例子:

// panic_recover.go
package main
import (
"fmt"
)
func badCall() {
panic("bad end")
}
func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panicing %s\r\n", e)
}
}()
badCall()
fmt.Printf("After bad call\r\n") // <-- wordt niet bereikt
}
func main() {
fmt.Printf("Calling test\r\n")
test()
fmt.Printf("Test completed\r\n")
}

输出:

Calling test

Panicing bad end

Test completed

defer-panic-recover 在某种意义上也是一种像 if , for 这样的控制流机制。

自定义包中的错误处理和 panicking

这是所有自定义包实现者应该遵守的最佳实践:

1)在包内部,总是应该从 panic 中 recover:不允许显式的超出包范围的 panic()。

2)向包的调用者返回错误值(而不是 panic)。

在包内部,特别是在非导出函数中有很深层次的嵌套调用时,对主调函数来说用 panic 来表示应该被翻译成错误的错误场景是很有用的。

// parse.go
package parse
import (
"fmt"
"strings"
"strconv"
)
// ParseError 表示将单词转换为整数时出错。
type ParseError struct {
Index int // 以空格分隔的单词列表的索引。
Word string // 生成分析错误的单词。
Err error // 引发此错误的原始错误(如果有)。
}
// 
func (e *ParseError) String() string {
return fmt.Sprintf("pkg parse: error parsing %q as int", e.Word)
}
// Parse 将 put 中以空格分隔的单词解析为整数。
func Parse(input string) (numbers []int, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
fields := strings.Fields(input)
numbers = fields2numbers(fields)
return
}
func fields2numbers(fields []string) (numbers []int) {
if len(fields) == 0 {
panic("no words to parse")
}
for idx, field := range fields {
num, err := strconv.Atoi(field)
if err != nil {
panic(&ParseError{idx, field, err})
}
numbers = append(numbers, num)
}
return
}
// panic_package.go
package main
import (
"fmt"
"./parse/parse"
)
func main() {
var examples = []string{
"1 2 3 4 5",
"100 50 25 12.5 6.25",
"2 + 2 = 4",
"1st class",
"",
}
for _, ex := range examples {
fmt.Printf("Parsing %q:\n ", ex)
nums, err := parse.Parse(ex)
if err != nil {
fmt.Println(err) 
continue
}
fmt.Println(nums)
}
}

输出:

Parsing “1 2 3 4 5”:

[1 2 3 4 5]

Parsing “100 50 25 12.5 6.25”:

pkg parse: error parsing “12.5” as int

Parsing “2 + 2 = 4”:

pkg parse: error parsing “+” as int

Parsing “1st class”:

pkg parse: error parsing “1st” as int

Parsing “”:

pkg: no words to parse

一种用闭包处理错误的模式

每当函数返回时,我们应该检查是否有错误发生:但是这会导致重复乏味的代码。结合

defer/panic/recover 机制和闭包可以得到一个我们马上要讨论的更加优雅的模式。不过这个模式只有当所有的函数都是同一种签名时可用,这样就有相当大的限制。一个很好的使用它的例子是 web 应用,所有的处理函数都是下面这样:

func handler1(w http.ResponseWriter, r *http.Request) { ... }

假设所有的函数都有这样的签名:

func f(a type1, b type2)

参数的数量和类型是不相关的。

我们给这个类型一个名字:

fType1 = func f(a type1, b type2)

在我们的模式中使用了两个帮助函数:

1)check:这是用来检查是否有错误和 panic 发生的函数:func check(err error) { if err != nil { panic(err) } }

2)errorhandler:这是一个包装函数。接收一个 fType1 类型的函数 fn 并返回一个调用 fn 的函数。里面就包含有 defer/recover 机制。

func errorHandler(fn fType1) fType1 {
return func(a type1, b type2) {
defer func() {
if e, ok := recover().(error); ok {
log.Printf(“run time panic: %v”, err)
}
}()
fn(a, b)
}
}

当错误发生时会 recover 并打印在日志中;除了简单的打印,应用也可以用 template 包为用户生成自定义的输出。check() 函数会在所有的被调函数中调用,像这样:

func f1(a type1, b type2) {
...
f, _, err := // call function/method
check(err)
t, err := // call function/method
check(err)
_, err2 := // call function/method
check(err2)
...
}

通过这种机制,所有的错误都会被 recover,并且调用函数后的错误检查代码也被简化为调用 check(err)即可。在这种模式下,不同的错误处理必须对应不同的函数类型;

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
14天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
57 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
1月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
42 7
|
1月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
1月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
103 71
|
1月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
107 67
|
1月前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
106 62
|
1月前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
9天前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
42 12
|
1月前
|
存储 Go
go语言中映射
go语言中映射
38 11
|
1月前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
40 12
下一篇
开通oss服务