diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 64b72fc76..d58b83ab2 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -9,6 +9,7 @@ package gerror import ( "bytes" "fmt" + "github.com/gogf/gf/internal/intlog" "io" "runtime" "strings" @@ -22,7 +23,7 @@ type Error struct { } const ( - gFILTER_KEY = "github.com/gogf/gf/" + gFILTER_KEY = "/errors/gerror/gerror" ) var ( @@ -130,6 +131,19 @@ func formatSubStack(st stack, buffer *bytes.Buffer) { if strings.Contains(file, gFILTER_KEY) { continue } + if !intlog.IsInGFDevelop() { + // Avoid GF stacks if not in GF development. + if strings.Contains(file, "github.com/gogf/gf/") { + continue + } + if strings.Contains(file, "github.com/gogf/gf@") { + continue + } + } + // Avoid stack string like "" + if strings.Contains(file, "<") { + continue + } if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { continue } diff --git a/internal/empty/empty.go b/internal/empty/empty.go index 6c9a632d8..6268fa4eb 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -70,3 +70,24 @@ func IsEmpty(value interface{}) bool { } return false } + +// IsNil checks whether given is nil. +// Note that it's using reflect feature which affects performance a little bit. +func IsNil(value interface{}) bool { + if value == nil { + return true + } + rv := reflect.ValueOf(value) + switch rv.Kind() { + case reflect.Chan, + reflect.Map, + reflect.Slice, + reflect.Array, + reflect.Func, + reflect.Ptr, + reflect.Interface, + reflect.UnsafePointer: + return rv.IsNil() + } + return false +} diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index 4ad11d629..c0fa61042 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -31,6 +31,11 @@ func init() { } } +// IsInGFDevelop checks and returns whether current process is in GF development. +func IsInGFDevelop() bool { + return isInGFDevelop +} + // Print prints with newline using fmt.Println. // The parameter can be multiple variables. func Print(v ...interface{}) { diff --git a/net/ghttp/ghttp_unit_param_struct_test.go b/net/ghttp/ghttp_unit_param_struct_test.go index bf140ef9a..7f531145f 100644 --- a/net/ghttp/ghttp_unit_param_struct_test.go +++ b/net/ghttp/ghttp_unit_param_struct_test.go @@ -22,6 +22,7 @@ func Test_Params_Struct(t *testing.T) { type User struct { Id int Name string + Time *time.Time Pass1 string `params:"password1"` Pass2 string `params:"password2" gvalid:"passwd1 @required|length:2,20|password3#||密码强度不足"` } diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 84ebe7968..d33010369 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -10,9 +10,12 @@ package gconv import ( "encoding/json" "fmt" + "github.com/gogf/gf/internal/empty" + "github.com/gogf/gf/os/gtime" "reflect" "strconv" "strings" + "time" "github.com/gogf/gf/encoding/gbinary" ) @@ -149,6 +152,7 @@ func Runes(i interface{}) []rune { } // String converts to string. +// It's most common used converting function. func String(i interface{}) string { if i == nil { return "" @@ -186,7 +190,20 @@ func String(i interface{}) string { return string(value) case []rune: return string(value) + case *time.Time: + if value == nil { + return "" + } + return value.String() + case *gtime.Time: + if value == nil { + return "" + } + return value.String() default: + if empty.IsNil(value) { + return "" + } if f, ok := value.(apiString); ok { // If the variable implements the String() interface, // then use that interface to perform the conversion