grpc/common/recoveryOpts.go (58 lines of code) (raw):
package common
import (
"fmt"
"net/url"
"os"
"runtime/debug"
"strings"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func ParseStack(stack string) (string, string) {
lines := strings.Split(stack, "panic.go") // split the stack into before panic and after panic
trace := strings.Split(lines[1], "\n") // split trace into lines
// Example Input
// panic({0xac4000, 0xd00610})
// /usr/local/go1.19/src/runtime/panic.go:884 +0x212
// go.goms.io/aks/rp/mygreeterv3/server/internal/server.(*Server).SayHello (0x0?, {0xd0a960?, 0xc0007fa270?}, 0xc00073d1d0)
// /root/aks-rp/mygreeterv3/server/internal/server/api.go:34 +0x299
var file = ""
var linenum = ""
for _, line := range trace {
// file name and number of what caused panic
if strings.Contains(line, ".go:") {
parts := strings.Split(line, " ")
fileAndLine := strings.Split(parts[0], ":")
file = strings.TrimSpace(fileAndLine[0])
linenum = fileAndLine[1]
break
}
}
return file, linenum
}
func GetRecoveryOpts() []recovery.Option {
getFileAndLineNum := func(p any) (err error) {
// get the file and line number where the panic occurred
// panic is terminated by custom recovery function and program continues
stack := debug.Stack()
file, linenum := ParseStack(string(stack))
// TODO: allow users to customize or we auto populate this repo path.
base := "https://msazure.visualstudio.com/CloudNativeCompute/_git/aks-rp"
path := file
if strings.Contains(path, "aks-rp") {
filepath := strings.SplitN(file, "aks-rp", 2)
path = filepath[1]
}
version := "GB" + os.Getenv("AKS_BIN_VERSION_GITBRANCH")
params := url.Values{}
params.Add("path", path)
params.Add("version", version)
params.Add("line", linenum)
query, err := url.QueryUnescape(params.Encode())
if err != nil {
fmt.Println(err)
// skip query if url.QueryUnescape() fails
query = ""
}
url := ""
// url is not generated as file is not in aks-rp
if path != file {
url = base + "?" + query
}
// format the error message with the file and line number
return status.Errorf(codes.Internal, "panic_message: %v, file: %s, line: %s, url: %s", p, file, linenum, url)
}
opts := []recovery.Option{
recovery.WithRecoveryHandler(getFileAndLineNum),
}
return opts
}