工程初始化
parent
dccad1b385
commit
006d5443c6
|
@ -0,0 +1,12 @@
|
||||||
|
package question
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"interview-one-stop-server/model"
|
||||||
|
"interview-one-stop-server/util/ierrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetQuestionList(ctx context.Context) ([]model.Question, ierrors.OneStopErr) {
|
||||||
|
|
||||||
|
return nil, ierrors.OneStopSuccess
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
service:
|
||||||
|
env: dev
|
||||||
|
domain: "http://aip-dev.nioint.com"
|
||||||
|
port: 7777
|
||||||
|
db:
|
||||||
|
dsn: "root:EKghQrcnBEcPZpTT@tcp(112.54.161.118:3306)/question_one_stop?charset=utf8mb4&parseTime=True&loc=Local"
|
|
@ -0,0 +1,6 @@
|
||||||
|
service:
|
||||||
|
env: prod
|
||||||
|
domain: "http://aip.nioint.com"
|
||||||
|
port: 7777
|
||||||
|
db:
|
||||||
|
dsn: "root:EKghQrcnBEcPZpTT@tcp(112.54.161.118:3306)/question_one_stop?charset=utf8mb4&parseTime=True&loc=Local""
|
|
@ -0,0 +1,88 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"interview-one-stop-server/constant"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var GlobalConf Config
|
||||||
|
|
||||||
|
func InitConfig() error {
|
||||||
|
v := viper.New()
|
||||||
|
v.AddConfigPath("./conf") // 设置读取的文件路径
|
||||||
|
v.AddConfigPath("../../conf") // 设置读取的文件路径
|
||||||
|
v.SetConfigType("yaml") // 设置文件的类型
|
||||||
|
|
||||||
|
env := os.Getenv("ENV_NAME")
|
||||||
|
switch env {
|
||||||
|
case constant.EnvNameTencentProd:
|
||||||
|
v.SetConfigName("conf_prod")
|
||||||
|
default:
|
||||||
|
v.SetConfigName("conf_dev")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试进行配置读取
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := v.Unmarshal(&GlobalConf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go modifyDefaultSwaggerHost()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyDefaultSwaggerHost() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf("linux cmd run failed,err is %v\n", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
path := "/app/swagger/dist/"
|
||||||
|
host := GlobalConf.Service.Domain[7:]
|
||||||
|
ph := ""
|
||||||
|
|
||||||
|
env := os.Getenv("ENV_NAME")
|
||||||
|
if env == "" {
|
||||||
|
host = "localhost:4004"
|
||||||
|
pwd, _ := os.Getwd()
|
||||||
|
path = pwd + "/swagger/dist/"
|
||||||
|
ph = "''"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf(`cp %s %s && sed -i %s ' s/UNKNOWNHOSTHHH/%s/g' %s `,
|
||||||
|
path+"swagger-initializer.js.bak", path+"swagger-initializer.js", ph, host, path+"swagger-initializer.js"))
|
||||||
|
|
||||||
|
output, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("无法获取命令的标准输出管道", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
fmt.Println("Linux命令执行失败,请检查命令输入是否有误", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取所有输出
|
||||||
|
bytes, err := io.ReadAll(output)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("打印异常,请检查")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
fmt.Println("Wait", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("打印内存信息:\n\n%s", bytes)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Service `mapstructure:"service"`
|
||||||
|
DB `mapstructure:"db"`
|
||||||
|
Dataset `mapstructure:"dataset"`
|
||||||
|
EventMesh `mapstructure:"eventmesh"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service 服务配置项
|
||||||
|
type Service struct {
|
||||||
|
Env string `mapstructure:"env"`
|
||||||
|
Domain string `mapstructure:"domain"`
|
||||||
|
Port string `mapstructure:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DB 数据库配置项
|
||||||
|
type DB struct {
|
||||||
|
DSN string `mapstructure:"dsn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dataset 数据集相关配置项
|
||||||
|
type Dataset struct {
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
LabelingHost string `mapstructure:"labeling_host"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventMesh 数据库配置项
|
||||||
|
type EventMesh struct {
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package constant
|
||||||
|
|
||||||
|
type ContextKey string
|
||||||
|
|
||||||
|
const TraceID ContextKey = "trace_id"
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvNameTencentProd string = "tencent-prod"
|
||||||
|
)
|
|
@ -0,0 +1,30 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"interview-one-stop-server/config"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gormDB *gorm.DB
|
||||||
|
|
||||||
|
func InitDB() error {
|
||||||
|
db, err := gorm.Open(mysql.Open(config.GlobalConf.DB.DSN))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gormDB = db
|
||||||
|
sqlDB, err := gormDB.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
|
||||||
|
sqlDB.SetMaxIdleConns(10)
|
||||||
|
// SetMaxOpenConns 设置打开数据库连接的最大数量。
|
||||||
|
sqlDB.SetMaxOpenConns(30)
|
||||||
|
// SetConnMaxLifetime 设置了连接可复用的最大时间。
|
||||||
|
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TableNameAnnotatingRecord 送标任务记录
|
||||||
|
TableNameAnnotatingRecord = "annotating_record"
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"interview-one-stop-server/biz/question"
|
||||||
|
"interview-one-stop-server/util/gin_util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleQueryQuestionList 获取题目列表
|
||||||
|
func HandleQueryQuestionList(c *gin.Context) {
|
||||||
|
l, e := question.GetQuestionList(gin_util.GetContextFromGin(c))
|
||||||
|
gin_util.GenGinResponse(c, e, l)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Start a web server
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"interview-one-stop-server/config"
|
||||||
|
"interview-one-stop-server/constant"
|
||||||
|
"interview-one-stop-server/db"
|
||||||
|
"interview-one-stop-server/util"
|
||||||
|
"interview-one-stop-server/util/log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ginzap "github.com/gin-contrib/zap"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := config.InitConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := gin.Default()
|
||||||
|
logger := log.GetLogger()
|
||||||
|
r.Use(ginzap.Ginzap(logger, time.RFC3339, true))
|
||||||
|
r.Use(ginzap.RecoveryWithZap(logger, true))
|
||||||
|
r.Use(TraceLog())
|
||||||
|
|
||||||
|
err = db.InitDB()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(context.Background(), "InitDB", fmt.Sprintf("db init failed, err is %s", err.Error()))
|
||||||
|
panic("db not ready")
|
||||||
|
}
|
||||||
|
|
||||||
|
post := GetPostRouter()
|
||||||
|
for k, v := range post {
|
||||||
|
r.POST(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
get := GetGetRouter()
|
||||||
|
for k, v := range get {
|
||||||
|
r.GET(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.GET("/ping", Ping)
|
||||||
|
if err := r.Run(fmt.Sprintf(":%s", config.GlobalConf.Service.Port)); err != nil {
|
||||||
|
log.Error(context.Background(), "main", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TraceLog() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
c.Set("ctx", context.WithValue(context.Background(), constant.TraceID, util.GenUUID()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type Question struct {
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"interview-one-stop-server/handler"
|
||||||
|
)
|
||||||
|
|
||||||
|
var post = map[string]gin.HandlerFunc{}
|
||||||
|
|
||||||
|
var get = map[string]gin.HandlerFunc{
|
||||||
|
"/api/v1/question/list": handler.HandleQueryQuestionList,
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPostRouter() map[string]gin.HandlerFunc {
|
||||||
|
return post
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGetRouter() map[string]gin.HandlerFunc {
|
||||||
|
return get
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ping(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"message": "pong",
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package gin_util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"interview-one-stop-server/util/ierrors"
|
||||||
|
"interview-one-stop-server/util/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenGinResponse(c *gin.Context, err ierrors.OneStopErr, data interface{}) {
|
||||||
|
if err.ErrNumber.Code != ierrors.Success.Code {
|
||||||
|
ctx, _ := c.Get("ctx")
|
||||||
|
funcName, _ := c.Get("func_name")
|
||||||
|
log.Error(ctx.(context.Context), funcName.(string), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"code": err.Code,
|
||||||
|
"message": err.Message,
|
||||||
|
"data": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContextFromGin(c *gin.Context) context.Context {
|
||||||
|
ctx, ok := c.Get("ctx")
|
||||||
|
if ok {
|
||||||
|
return ctx.(context.Context)
|
||||||
|
}
|
||||||
|
return context.Background()
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package ierrors
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ErrNumber struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrNumber) Error() string {
|
||||||
|
return err.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
type OneStopErr struct {
|
||||||
|
ErrNumber
|
||||||
|
Errord error // 保存内部错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *OneStopErr) Error() string {
|
||||||
|
return fmt.Sprintf("OneStop - code: %d, message: %s, error: %s", err.Code, err.Message, err.Errord)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOneStopErr(errN ErrNumber, err error) OneStopErr {
|
||||||
|
return OneStopErr{
|
||||||
|
ErrNumber: errN,
|
||||||
|
Errord: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var OneStopSuccess = NewOneStopErr(Success, nil)
|
||||||
|
|
||||||
|
//错误码一共四位,一二位为功能,三四位为具体错误
|
||||||
|
|
||||||
|
var (
|
||||||
|
Success = ErrNumber{
|
||||||
|
Code: 0,
|
||||||
|
Message: "SUCCEED",
|
||||||
|
}
|
||||||
|
/*** 公共错误码,一二位为10 ***/
|
||||||
|
|
||||||
|
ErrJsonParseFailed = ErrNumber{
|
||||||
|
Code: 1000,
|
||||||
|
Message: "JSON 转换失败",
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrHttpBodyReadFailed = ErrNumber{
|
||||||
|
Code: 1001,
|
||||||
|
Message: "请求body获取失败",
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrHttpDbOperateFailed = ErrNumber{
|
||||||
|
Code: 1002,
|
||||||
|
Message: "数据库操作异常",
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrHttpParamNotValid = ErrNumber{
|
||||||
|
Code: 1003,
|
||||||
|
Message: "入参不符合要求",
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,110 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
"interview-one-stop-server/util"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log *zap.Logger
|
||||||
|
|
||||||
|
type lumberjackSink struct {
|
||||||
|
*lumberjack.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lumberjackSink) Sync() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogger() *zap.Logger {
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLogger(logLevel string, logFolder string, dev bool) (*zap.Logger, error) {
|
||||||
|
var level zapcore.Level
|
||||||
|
switch logLevel {
|
||||||
|
case "debug":
|
||||||
|
level = zap.DebugLevel
|
||||||
|
case "info":
|
||||||
|
level = zap.InfoLevel
|
||||||
|
case "warning":
|
||||||
|
level = zap.WarnLevel
|
||||||
|
case "error":
|
||||||
|
level = zap.ErrorLevel
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown log level %s", logLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
encoderConfig := zapcore.EncoderConfig{
|
||||||
|
TimeKey: "ts",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "logger",
|
||||||
|
MessageKey: "msg",
|
||||||
|
StacktraceKey: "stacktrace",
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||||
|
EncodeTime: zapcore.RFC3339TimeEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
ConsoleSeparator: " ",
|
||||||
|
}
|
||||||
|
ll := lumberjack.Logger{
|
||||||
|
Filename: logFolder,
|
||||||
|
MaxSize: 100, // MB
|
||||||
|
MaxBackups: 31,
|
||||||
|
MaxAge: 31, // days
|
||||||
|
Compress: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = zap.RegisterSink("lumberjack", func(*url.URL) (zap.Sink, error) {
|
||||||
|
return lumberjackSink{
|
||||||
|
Logger: &ll,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
loggerConfig := zap.Config{
|
||||||
|
Level: zap.NewAtomicLevelAt(level),
|
||||||
|
Development: dev,
|
||||||
|
Encoding: "console",
|
||||||
|
EncoderConfig: encoderConfig,
|
||||||
|
OutputPaths: []string{"stderr", fmt.Sprintf("lumberjack:%s", logFolder)},
|
||||||
|
}
|
||||||
|
|
||||||
|
logger, err := loggerConfig.Build()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("build zap logger from config error: %v", err))
|
||||||
|
}
|
||||||
|
return logger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log, _ = newLogger("info", "logs/app.log", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(ctx context.Context, label string, msg string) {
|
||||||
|
log.Sugar().Error(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warn(ctx context.Context, label string, msg string) {
|
||||||
|
log.Sugar().Warnf(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(ctx context.Context, label string, msg string) {
|
||||||
|
log.Sugar().Info(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(ctx context.Context, label string, msg string) {
|
||||||
|
log.Sugar().Debug(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTraceIDFromContext(ctx context.Context) string {
|
||||||
|
traceID := ctx.Value("trace_id")
|
||||||
|
if traceID == nil {
|
||||||
|
return util.GenUUID()
|
||||||
|
}
|
||||||
|
return traceID.(string)
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"interview-one-stop-server/util"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLog(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.Background(), "trace_id", util.GenUUID())
|
||||||
|
|
||||||
|
err := errors.New("it's an error")
|
||||||
|
Error(ctx, "TestLog Error", fmt.Sprintf("err is %s,value is %+v", err.Error(), struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}{
|
||||||
|
"issue",
|
||||||
|
2,
|
||||||
|
}))
|
||||||
|
Warn(ctx, "TestLog Warn", fmt.Sprintf("err is %s,value is %v", errors.New("it's an error").Error(), struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}{
|
||||||
|
"issue",
|
||||||
|
2,
|
||||||
|
}))
|
||||||
|
Info(ctx, "TestLog Info", fmt.Sprintf("no error,value is %+v", struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}{
|
||||||
|
"issue",
|
||||||
|
2,
|
||||||
|
}))
|
||||||
|
Debug(ctx, "TestLog Debug", "it's end")
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func Int64ArrJoin(nums []int64, sep string) string {
|
||||||
|
res := ""
|
||||||
|
if len(nums) == 0 {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
for _, num := range nums {
|
||||||
|
res += strconv.Itoa(int(num)) + sep
|
||||||
|
}
|
||||||
|
return res[:len(res)-1]
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"interview-one-stop-server/constant"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateContextWithTraceID() context.Context {
|
||||||
|
return context.WithValue(context.Background(), constant.TraceID, GenUUID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenUUID() string {
|
||||||
|
return uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicToError Panic转换为error
|
||||||
|
func PanicToError(f func()) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = fmt.Errorf(PanicTrace(e))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicTrace panic调用链跟踪
|
||||||
|
func PanicTrace(err interface{}) string {
|
||||||
|
stackBuf := make([]byte, 4096)
|
||||||
|
n := runtime.Stack(stackBuf, false)
|
||||||
|
|
||||||
|
return fmt.Sprintf("panic: %v %s", err, stackBuf[:n])
|
||||||
|
}
|
Loading…
Reference in New Issue