MST

星途 面试题库

面试题:Go 语言中 panic、recover 与 defer 在复杂业务错误日志处理中的协同

在一个处理用户交易的复杂 Go 程序中,有多个函数嵌套调用的场景。例如,`func processTransaction(userID int, amount float64)` 函数会调用其他验证、扣减余额、记录流水等函数。如果在交易过程中发生了不可恢复的错误(如余额不足、系统内部错误等),需要使用 panic 抛出错误,并在最外层使用 recover 捕获,同时利用 defer 在每个可能出错的函数层级记录详细的错误日志(包括错误发生的函数名、错误信息等),请描述实现思路并给出关键代码示例。
14.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 在每个可能出错的函数内部使用 defer 来记录错误日志。可以通过获取当前函数名和传递的错误信息来记录详细日志。
  2. processTransaction 函数内部,对可能出错的函数调用使用 recover 来捕获 panic 抛出的错误,并记录日志。

关键代码示例

package main

import (
	"fmt"
	"runtime"
)

// 记录错误日志的函数
func logError(funcName string, err error) {
	fmt.Printf("Error in function %s: %v\n", funcName, err)
}

// 模拟验证函数
func validate(userID int, amount float64) error {
	// 假设余额不足
	if amount > 100 {
		return fmt.Errorf("余额不足")
	}
	return nil
}

// 模拟扣减余额函数
func deductBalance(userID int, amount float64) error {
	// 假设系统内部错误
	return fmt.Errorf("系统内部错误")
}

// 模拟记录流水函数
func recordTransaction(userID int, amount float64) error {
	// 假设无错误
	return nil
}

// 处理交易的主函数
func processTransaction(userID int, amount float64) {
	defer func() {
		if r := recover(); r != nil {
			// 获取当前函数名
			pc, _, _, _ := runtime.Caller(1)
			funcName := runtime.FuncForPC(pc).Name()
			if err, ok := r.(error); ok {
				logError(funcName, err)
			} else {
				logError(funcName, fmt.Errorf("%v", r))
			}
		}
	}()

	// 验证
	if err := validate(userID, amount); err != nil {
		panic(err)
	}

	// 扣减余额
	if err := deductBalance(userID, amount); err != nil {
		panic(err)
	}

	// 记录流水
	if err := recordTransaction(userID, amount); err != nil {
		panic(err)
	}
}

测试代码

func main() {
	processTransaction(1, 150)
}

在上述代码中:

  1. logError 函数用于记录错误日志,包含函数名和错误信息。
  2. validatedeductBalancerecordTransaction 是模拟的交易相关函数,可能会返回错误。
  3. processTransaction 函数内部对这些函数调用进行错误处理,使用 deferrecover 机制记录错误日志。
  4. main 函数用于测试 processTransaction 函数的错误处理功能。