diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index ed1177df0..406958701 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -32,20 +32,24 @@ func niceCallFunc(f func()) { if v, ok := exception.(error); ok && gerror.HasStack(v) { // It's already an error that has stack info. panic(v) - } else { - // Create a new error with stack info. - // Note that there's a skip pointing the start stacktrace - // of the real error point. - if v, ok := exception.(error); ok { - if gerror.Code(v) != gcode.CodeNil { - panic(v) - } else { - panic(gerror.WrapCodeSkip(gcode.CodeInternalError, 1, v, "")) - } - } else { - panic(gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception)) - } } + // Create a new error with stack info. + // Note that there's a skip pointing the start stacktrace + // of the real error point. + if v, ok := exception.(error); ok { + if gerror.Code(v) != gcode.CodeNil { + panic(v) + } else { + panic(gerror.WrapCodeSkip( + gcode.CodeInternalError, 1, v, "exception recovered", + )) + } + } else { + panic(gerror.NewCodeSkipf( + gcode.CodeInternalError, 1, "exception recovered: %+v", exception, + )) + } + } } }() diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index be1399734..2d0d91fdb 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -53,7 +53,7 @@ type Request struct { formMap map[string]interface{} // Form parameters map, which is nil if there's no form data from client. bodyMap map[string]interface{} // Body parameters map, which might be nil if there're no body content. error error // Current executing error of the request. - exit bool // A bool marking whether current request is exited. + exitAll bool // A bool marking whether current request is exited. parsedHost string // The parsed host name for current host used by GetHost function. clientIp string // The parsed client ip for current host used by GetClientIp function. bodyContent []byte // Request body content. @@ -139,7 +139,7 @@ func (r *Request) Exit() { // ExitAll exits executing of current and following HTTP handlers. func (r *Request) ExitAll() { - r.exit = true + r.exitAll = true panic(exceptionExitAll) } @@ -150,7 +150,7 @@ func (r *Request) ExitHook() { // IsExited checks and returns whether current request is exited. func (r *Request) IsExited() bool { - return r.exit + return r.exitAll } // GetHeader retrieves and returns the header value with given `key`. diff --git a/net/ghttp/ghttp_z_unit_feature_log_test.go b/net/ghttp/ghttp_z_unit_feature_log_test.go index 7a2d58896..259d991b6 100644 --- a/net/ghttp/ghttp_z_unit_feature_log_test.go +++ b/net/ghttp/ghttp_z_unit_feature_log_test.go @@ -45,7 +45,7 @@ func Test_Log(t *testing.T) { client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) t.Assert(client.GetContent(ctx, "/hello"), "hello") - t.Assert(client.GetContent(ctx, "/error"), "custom error") + t.Assert(client.GetContent(ctx, "/error"), "exception recovered: custom error") var ( logPath1 = gfile.Join(logDir, gtime.Now().Format("Y-m-d")+".log") diff --git a/net/ghttp/ghttp_z_unit_feature_middleware_basic_test.go b/net/ghttp/ghttp_z_unit_feature_middleware_basic_test.go index 2837abb35..6886ebefd 100644 --- a/net/ghttp/ghttp_z_unit_feature_middleware_basic_test.go +++ b/net/ghttp/ghttp_z_unit_feature_middleware_basic_test.go @@ -709,6 +709,6 @@ func Test_Middleware_Panic(t *testing.T) { client := g.Client() client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - t.Assert(client.GetContent(ctx, "/"), "error") + t.Assert(client.GetContent(ctx, "/"), "exception recovered: error") }) } diff --git a/os/gbuild/gbuild.go b/os/gbuild/gbuild.go index 4783e477f..92f140736 100644 --- a/os/gbuild/gbuild.go +++ b/os/gbuild/gbuild.go @@ -9,12 +9,13 @@ package gbuild import ( "context" + "runtime" + "github.com/gogf/gf/v2" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/encoding/gbase64" "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/internal/json" - "runtime" ) var ( diff --git a/os/gcmd/gcmd_command_help.go b/os/gcmd/gcmd_command_help.go index a07ca23b2..7f9ff472a 100644 --- a/os/gcmd/gcmd_command_help.go +++ b/os/gcmd/gcmd_command_help.go @@ -204,7 +204,12 @@ type printLineBriefInput struct { } func (c *Command) printLineBrief(in printLineBriefInput) { - for i, line := range gstr.SplitAndTrim(in.Brief, "\n") { + briefArray := gstr.SplitAndTrim(in.Brief, "\n") + if len(briefArray) == 0 { + // If command brief is empty, it just prints its command name. + briefArray = []string{""} + } + for i, line := range briefArray { var lineStr string if i == 0 { lineStr = fmt.Sprintf( diff --git a/os/gcmd/gcmd_command_object.go b/os/gcmd/gcmd_command_object.go index af03f6628..ddb540b29 100644 --- a/os/gcmd/gcmd_command_object.go +++ b/os/gcmd/gcmd_command_object.go @@ -245,17 +245,6 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co // ============================================================================================= command.FuncWithValue = func(ctx context.Context, parser *Parser) (out interface{}, err error) { ctx = context.WithValue(ctx, CtxKeyParser, parser) - - defer func() { - if exception := recover(); exception != nil { - if v, ok := exception.(error); ok && gerror.HasStack(v) { - err = v - } else { - err = gerror.New(`exception recovered:` + gconv.String(exception)) - } - } - }() - var ( data = gconv.Map(parser.GetOptAll()) argIndex = 0 diff --git a/os/gcmd/gcmd_command_run.go b/os/gcmd/gcmd_command_run.go index a4f3848c0..cb63774ed 100644 --- a/os/gcmd/gcmd_command_run.go +++ b/os/gcmd/gcmd_command_run.go @@ -31,9 +31,17 @@ func (c *Command) Run(ctx context.Context) { func (c *Command) RunWithValue(ctx context.Context) (value interface{}) { value, err := c.RunWithValueError(ctx) if err != nil { - if gerror.Code(err) == gcode.CodeNotFound { + var ( + code = gerror.Code(err) + detail = code.Detail() + ) + if code.Code() == gcode.CodeNotFound.Code() { fmt.Printf("ERROR: %s\n", gstr.Trim(err.Error())) - c.Print() + if lastCmd, ok := detail.(*Command); ok { + lastCmd.Print() + } else { + c.Print() + } } else { fmt.Printf("%+v\n", err) } @@ -64,23 +72,34 @@ func (c *Command) RunWithValueError(ctx context.Context) (value interface{}, err args = args[1:] // Find the matched command and run it. - if subCommand, newCtx := c.searchCommand(ctx, args); subCommand != nil { - return subCommand.doRun(newCtx, parser) + lastCmd, foundCmd, newCtx := c.searchCommand(ctx, args) + if foundCmd != nil { + return foundCmd.doRun(newCtx, parser) } // Print error and help command if no command found. err = gerror.NewCodef( - gcode.CodeNotFound, - `command "%s" not found for arguments "%s"`, + gcode.WithCode(gcode.CodeNotFound, lastCmd), + `command "%s" not found for command "%s", command line: %s`, gstr.Join(args, " "), + c.Name, gstr.Join(os.Args, " "), ) return } func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{}, err error) { - ctx = context.WithValue(ctx, CtxKeyCommand, c) + defer func() { + if exception := recover(); exception != nil { + if v, ok := exception.(error); ok && gerror.HasStack(v) { + err = v + } else { + err = gerror.Newf(`exception recovered: %+v`, exception) + } + } + }() + ctx = context.WithValue(ctx, CtxKeyCommand, c) // Check built-in help command. if parser.ContainsOpt(helpOptionName) || parser.ContainsOpt(helpOptionNameShort) { if c.HelpFunc != nil { @@ -154,12 +173,11 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error) } // searchCommand recursively searches the command according given arguments. -func (c *Command) searchCommand(ctx context.Context, args []string) (*Command, context.Context) { +func (c *Command) searchCommand(ctx context.Context, args []string) (lastCmd, foundCmd *Command, newCtx context.Context) { if len(args) == 0 { - return nil, ctx + return c, nil, ctx } for _, cmd := range c.commands { - // Recursively searching the command. if cmd.Name == args[0] { leftArgs := args[1:] @@ -167,16 +185,16 @@ func (c *Command) searchCommand(ctx context.Context, args []string) (*Command, c // it then gives all its left arguments to it. if cmd.hasArgumentFromIndex() { ctx = context.WithValue(ctx, CtxKeyArguments, leftArgs) - return cmd, ctx + return c, cmd, ctx } // Recursively searching. if len(leftArgs) == 0 { - return cmd, ctx + return c, cmd, ctx } return cmd.searchCommand(ctx, leftArgs) } } - return nil, ctx + return c, nil, ctx } func (c *Command) hasArgumentFromIndex() bool { diff --git a/os/gcmd/gcmd_z_unit_test.go b/os/gcmd/gcmd_z_unit_test.go index d3da644e4..5d03658e3 100644 --- a/os/gcmd/gcmd_z_unit_test.go +++ b/os/gcmd/gcmd_z_unit_test.go @@ -11,6 +11,7 @@ package gcmd_test import ( "context" "fmt" + "os" "testing" "github.com/gogf/gf/v2/errors/gcode" @@ -222,3 +223,35 @@ gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./dock _ = c.RunWithError(ctx) }) } + +func Test_Command_NotFound(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c0 := &gcmd.Command{ + Name: "c0", + } + c1 := &gcmd.Command{ + Name: "c1", + FuncWithValue: func(ctx context.Context, parser *gcmd.Parser) (interface{}, error) { + return nil, nil + }, + } + c21 := &gcmd.Command{ + Name: "c21", + FuncWithValue: func(ctx context.Context, parser *gcmd.Parser) (interface{}, error) { + return nil, nil + }, + } + c22 := &gcmd.Command{ + Name: "c22", + FuncWithValue: func(ctx context.Context, parser *gcmd.Parser) (interface{}, error) { + return nil, nil + }, + } + t.AssertNil(c0.AddCommand(c1)) + t.AssertNil(c1.AddCommand(c21, c22)) + + os.Args = []string{"c0", "c1", "c23", `--test="abc"`} + err := c0.RunWithError(gctx.New()) + t.Assert(err.Error(), `command "c1 c23" not found for command "c0", command line: c0 c1 c23 --test="abc"`) + }) +} diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index 98f85f6d0..e977b25fb 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -10,6 +10,7 @@ import ( "context" "github.com/gogf/gf/v2/container/gtype" + "github.com/gogf/gf/v2/errors/gerror" ) // Entry is the timing job. @@ -45,9 +46,13 @@ func (entry *Entry) Run() { } go func() { defer func() { - if err := recover(); err != nil { - if err != panicExit { - panic(err) + if exception := recover(); exception != nil { + if exception != panicExit { + if v, ok := exception.(error); ok && gerror.HasStack(v) { + panic(v) + } else { + panic(gerror.Newf(`exception recovered: %+v`, exception)) + } } else { entry.Close() return