如何拦截 `runtime.Goexit()` 信号

huangapple go评论62阅读模式
英文:

How to intercept `runtime.Goexit()` signal

问题

package main

import (
	"fmt"
	"path"
	"runtime"
	"strconv"
	"time"

	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
)

var (
	Errors map[string]string
	err    error
)

func CheckErr(err error) {
	if err != nil {
		pc, file, line, _ := runtime.Caller(1)
		color.New(color.FgRed).PrintfFunc()(" [app]")
		color.New(color.FgMagenta).PrintfFunc()(fmt.Sprintf("%v:", time.Now().Format("15:04:05.00000")))
		color.New(color.FgGreen).PrintfFunc()(fmt.Sprintf("%v] %v:%v:", path.Ext(runtime.FuncForPC(pc).Name())[1:], path.Base(file), line))
		color.Yellow(" %v", err)
		Errors["app "+time.Now().Format("15:04:05.00000")+" "+path.Ext(runtime.FuncForPC(pc).Name())[1:]+" "+path.Base(file)+":"+strconv.Itoa(line)] = err.Error()
		runtime.Goexit()
	}
}

func test(hello string) (string, error) {
	// ...
	if err != nil {
		return "", err
	}
	text := hello + " world"
	return text, nil
}

func main() {
	router := gin.Default()

	router.POST("/hello", func(context *gin.Context) {
		message, err := test("hello")
		if err != nil {
			CheckErr(err)
		}
		context.JSON(200, gin.H{
			"message": message,
			"Errors":  Errors,
		})
	})

	router.Run(":8080")
}
  • CheckErr 是一个收集错误日志、打印错误并退出的函数
  • main 是一个 gin 服务
  • test 是 gin 的数据获取函数
  • 由于 test 需要返回数据,所以不能使用 go 协程

> 当 test 失败时,我如何返回到 gin 并返回错误消息?

英文:
package main

import 	(
	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
	"runtime"
)

var (
	Errors map[string]string
	err error
)

func CheckErr(err error) {
	if err != nil {
		pc, file, line, _ := runtime.Caller(1)
		color.New(color.FgRed).PrintfFunc()(" [app]")
		color.New(color.FgMagenta).PrintfFunc()("%v:", time.Now().Format("15:04:05.00000"))
		color.New(color.FgGreen).PrintfFunc()("%v] %v:%v:", path.Ext(runtime.FuncForPC(pc).Name())[1:], path.Base(file), line)
		color.Yellow(" %v", err)
		Errors["app "+time.Now().Format("15:04:05.00000")+" "+path.Ext(runtime.FuncForPC(pc).Name())[1:]+" "+path.Base(file)+":"+strconv.Itoa(line)] = err.Error()
		runtime.Goexit()
	}
}

func test(hello string) string{
	// ...
	if err!=nil{
		CheckErr(err)
	} 
	text := hello+" world"
	return test
}

func main() {
	req.POST("/hello", func(context *gin.Context) {
		message:=test("hello")
		context.JSON(200, gin.H{
			"message": message,
			"Errors": Errors,
		})
	}
}
  • CheckErr is a function that collects error logs, prints errors and exits
  • main is a gin service
  • test is the data acquisition function of gin
  • Cannot use go coroutine because test needs to return data

> How can I go back to gin and return the error message when test fails?

答案1

得分: 2

如果你坚持使用runtime.Goexit(),那么你必须使用另一个goroutine。结合使用chan或者sync.WaitGroup,它可以正常工作,因为Goexit会运行所有的延迟调用。

// Goexit在终止goroutine之前运行所有的延迟调用。因为Goexit不是panic,所以在这些延迟函数中的任何recover调用都将返回nil。

func CheckErr(err error, done chan struct{}) {
	defer func() { done <- struct{}{} }()
	if err != nil {
		// ...
		runtime.Goexit()
	}
}

func test(hello string) string {
	// ...
	if err != nil {
		done := make(chan struct{})
		go CheckErr(err, done)
		<-done
	}
	text := hello + " world"
	return text
}
英文:

If you insist on using runtime.Goexit() then you have to use another goroutine. Together with chan or sync.WaitGroup it works just fine since goexit runs all deferred calls.

> // Goexit runs all deferred calls before terminating the goroutine. Because Goexit
// is not a panic, any recover calls in those deferred functions will return nil.

func CheckErr(err error, done chan struct{}) {
defer func() { done &lt;- struct{}{} }()
if err != nil {
// ...
runtime.Goexit()
}
}
func test(hello string) string {
// ...
if err != nil {
done := make(chan struct{})
go CheckErr(err, done)
&lt;-done
}
text := hello + &quot; world&quot;
return text
}

huangapple
  • 本文由 发表于 2022年3月14日 23:34:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/71470340.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定