From 4639e84ad5bd63e37d3c0b2cdf36c839fa505163 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Mon, 31 Mar 2025 05:36:47 -0400 Subject: [PATCH] feat: recover from panic --- cmd/joj3/main.go | 18 +++- cmd/joj3/stage.go | 5 +- internal/stage/run.go | 246 +++++++++++++++++++++++------------------- 3 files changed, 153 insertions(+), 116 deletions(-) diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go index d593a8c..96e25d4 100644 --- a/cmd/joj3/main.go +++ b/cmd/joj3/main.go @@ -146,8 +146,18 @@ func mainImpl() (err error) { } func main() { - if err := mainImpl(); err != nil { - slog.Error("main exit", "error", err) - os.Exit(1) - } + var err error + exitCode := 0 + defer func() { + if r := recover(); r != nil { + slog.Error("panic recovered", "panic", r) + exitCode = 2 + } + if err != nil { + slog.Error("main exit", "error", err) + exitCode = 1 + } + os.Exit(exitCode) + }() + err = mainImpl() } diff --git a/cmd/joj3/stage.go b/cmd/joj3/stage.go index bf6560f..dcd6daa 100644 --- a/cmd/joj3/stage.go +++ b/cmd/joj3/stage.go @@ -22,7 +22,10 @@ func generateStages(confStages []conf.ConfStage, groups []string) ( ) { stages := []stage.Stage{} existNames := map[string]bool{} - for _, s := range confStages { + for i, s := range confStages { + if s.Name == "" { + s.Name = fmt.Sprintf("stage-%d", i) + } if s.Group != "" { var ok bool for _, group := range groups { diff --git a/internal/stage/run.go b/internal/stage/run.go index 4366f3a..920af45 100644 --- a/internal/stage/run.go +++ b/internal/stage/run.go @@ -16,140 +16,164 @@ func Run(stages []Stage) ( var tmpParserResults []ParserResult slog.Info("stage run start") for _, stage := range stages { - slog.Info("stage start", "name", stage.Name) - slog.Info( - "executor run start", - "stageName", stage.Name, - "name", stage.Executor.Name, - ) - slog.Debug( - "executor run start", - "stageName", stage.Name, - "name", stage.Executor.Name, - "cmds", stage.Executor.Cmds, - ) - executor, ok := executorMap[stage.Executor.Name] - if !ok { - slog.Error( - "executor not found", + func() { + defer func() { + if r := recover(); r != nil { + slog.Error( + "stage panic recovered", + "stageName", stage.Name, + "panic", r, + ) + stageResults = append(stageResults, StageResult{ + Name: stage.Name, + Results: []ParserResult{ + { + Score: 0, + Comment: "JOJ3 internal error. " + + "Please contact the administrator.\n", + }, + }, + ForceQuit: true, + }) + forceQuitStageName = stage.Name + err = fmt.Errorf("panic in stage %s: %v", stage.Name, r) + } + }() + slog.Info("stage start", "name", stage.Name) + slog.Info( + "executor run start", "stageName", stage.Name, "name", stage.Executor.Name, ) - err = fmt.Errorf("executor not found: %s", stage.Executor.Name) - return stageResults, forceQuitStageName, err - } - executorResults, err = executor.Run(stage.Executor.Cmds) - if err != nil { - slog.Error( - "executor run error", + slog.Debug( + "executor run start", "stageName", stage.Name, "name", stage.Executor.Name, - "error", err, + "cmds", stage.Executor.Cmds, ) - return stageResults, forceQuitStageName, err - } - for i, executorResult := range executorResults { + executor, ok := executorMap[stage.Executor.Name] + if !ok { + slog.Error( + "executor not found", + "stageName", stage.Name, + "name", stage.Executor.Name, + ) + err = fmt.Errorf("executor not found: %s", stage.Executor.Name) + return + } + executorResults, err = executor.Run(stage.Executor.Cmds) + if err != nil { + slog.Error( + "executor run error", + "stageName", stage.Name, + "name", stage.Executor.Name, + "error", err, + ) + return + } + for i, executorResult := range executorResults { + slog.Debug( + "executor run done", + "stageName", stage.Name, + "case", i, + "name", stage.Executor.Name, + "result", executorResult, + ) + } slog.Debug( "executor run done", "stageName", stage.Name, - "case", i, "name", stage.Executor.Name, - "result", executorResult, + "summary", SummarizeExecutorResults(executorResults), ) - } - slog.Debug( - "executor run done", - "stageName", stage.Name, - "name", stage.Executor.Name, - "summary", SummarizeExecutorResults(executorResults), - ) - parserResults = []ParserResult{} - stageDetail := StageDetail{ - Name: stage.Name, - CaseDetails: make([]CaseDetail, len(executorResults)), - } - parserScoresMap := map[string][]int{} - for _, parser := range stage.Parsers { - parserScoresMap[parser.Name] = make([]int, len(executorResults)) - } - for _, stageParser := range stage.Parsers { - slog.Info( - "parser run start", - "stageName", stage.Name, - "name", stageParser.Name, - ) - slog.Debug( - "parser run start", - "stageName", stage.Name, - "name", stageParser.Name, - "conf", stageParser.Conf, - ) - parser, ok := parserMap[stageParser.Name] - if !ok { - slog.Error( - "parser not found", + parserResults = []ParserResult{} + stageDetail := StageDetail{ + Name: stage.Name, + CaseDetails: make([]CaseDetail, len(executorResults)), + } + parserScoresMap := map[string][]int{} + for _, parser := range stage.Parsers { + parserScoresMap[parser.Name] = make([]int, len(executorResults)) + } + for _, stageParser := range stage.Parsers { + slog.Info( + "parser run start", "stageName", stage.Name, "name", stageParser.Name, ) - err = fmt.Errorf("parser not found: %s", stageParser.Name) - return stageResults, forceQuitStageName, err - } - var parserForceQuit bool - tmpParserResults, parserForceQuit, err = parser.Run( - executorResults, stageParser.Conf) - if err != nil { - slog.Error( - "parser run error", + slog.Debug( + "parser run start", "stageName", stage.Name, "name", stageParser.Name, - "error", err, + "conf", stageParser.Conf, ) - forceQuitStageName = stage.Name - break - } - for i, parserResult := range tmpParserResults { - parserScoresMap[stageParser.Name][i] += parserResult.Score - } - if parserForceQuit { - slog.Error( - "parser force quit", + parser, ok := parserMap[stageParser.Name] + if !ok { + slog.Error( + "parser not found", + "stageName", stage.Name, + "name", stageParser.Name, + ) + err = fmt.Errorf("parser not found: %s", stageParser.Name) + return + } + var parserForceQuit bool + tmpParserResults, parserForceQuit, err = parser.Run( + executorResults, stageParser.Conf) + if err != nil { + slog.Error( + "parser run error", + "stageName", stage.Name, + "name", stageParser.Name, + "error", err, + ) + forceQuitStageName = stage.Name + break + } + for i, parserResult := range tmpParserResults { + parserScoresMap[stageParser.Name][i] += parserResult.Score + } + if parserForceQuit { + slog.Error( + "parser force quit", + "stageName", stage.Name, + "name", stageParser.Name, + ) + forceQuitStageName = stage.Name + } + slog.Debug( + "parser run done", "stageName", stage.Name, "name", stageParser.Name, + "results", tmpParserResults, ) - forceQuitStageName = stage.Name - } - slog.Debug( - "parser run done", - "stageName", stage.Name, - "name", stageParser.Name, - "results", tmpParserResults, - ) - if len(parserResults) == 0 { - parserResults = tmpParserResults - } else { - for i := range len(parserResults) { - parserResults[i].Score += tmpParserResults[i].Score - parserResults[i].Comment += tmpParserResults[i].Comment + if len(parserResults) == 0 { + parserResults = tmpParserResults + } else { + for i := range len(parserResults) { + parserResults[i].Score += tmpParserResults[i].Score + parserResults[i].Comment += tmpParserResults[i].Comment + } } } - } - for i := range executorResults { - caseDetail := CaseDetail{ - Index: i, - ExecutorResult: executorResults[i], - ParserScores: make(map[string]int), + for i := range executorResults { + caseDetail := CaseDetail{ + Index: i, + ExecutorResult: executorResults[i], + ParserScores: make(map[string]int), + } + for name, scores := range parserScoresMap { + caseDetail.ParserScores[name] = scores[i] + } + stageDetail.CaseDetails[i] = caseDetail } - for name, scores := range parserScoresMap { - caseDetail.ParserScores[name] = scores[i] - } - stageDetail.CaseDetails[i] = caseDetail - } - stageResults = append(stageResults, StageResult{ - Name: stage.Name, - Results: parserResults, - ForceQuit: forceQuitStageName != "", - }) - slog.Debug("stage done", "name", stage.Name, "stageDetail", stageDetail) + stageResults = append(stageResults, StageResult{ + Name: stage.Name, + Results: parserResults, + ForceQuit: forceQuitStageName != "", + }) + slog.Debug("stage done", "name", stage.Name, "stageDetail", stageDetail) + }() if forceQuitStageName != "" { break }