From 2a4043a3c2edb244b06c008f0d1d916053b7243a Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Tue, 28 Apr 2020 22:34:36 +0300 Subject: [PATCH] more route info improvements Former-commit-id: ccbe95de0badb1bf448fcc443cecda60772716dc --- _examples/dependency-injection/basic/main.go | 2 + _examples/mvc/middleware/main.go | 2 + _examples/routing/overview/main.go | 2 +- cache/client/handler.go | 4 + context/handler.go | 134 ++++++++++++++++--- core/router/api_builder.go | 5 +- core/router/api_container.go | 10 +- core/router/route.go | 37 +---- middleware/basicauth/basicauth.go | 2 +- middleware/hcaptcha/hcaptcha.go | 2 +- middleware/logger/logger.go | 2 +- middleware/pprof/pprof.go | 2 +- middleware/recaptcha/recaptcha.go | 2 +- middleware/recover/recover.go | 2 +- 14 files changed, 147 insertions(+), 61 deletions(-) diff --git a/_examples/dependency-injection/basic/main.go b/_examples/dependency-injection/basic/main.go index d464f562..f19a091c 100644 --- a/_examples/dependency-injection/basic/main.go +++ b/_examples/dependency-injection/basic/main.go @@ -35,6 +35,8 @@ func configureAPI(api *iris.APIContainer) { func main() { app := iris.New() + app.Logger().SetLevel("debug") + app.ConfigureContainer(configureAPI) app.Listen(":8080") } diff --git a/_examples/mvc/middleware/main.go b/_examples/mvc/middleware/main.go index af3aacb6..6cd4e53c 100644 --- a/_examples/mvc/middleware/main.go +++ b/_examples/mvc/middleware/main.go @@ -14,6 +14,8 @@ var cacheHandler = cache.Handler(10 * time.Second) func main() { app := iris.New() + app.Logger().SetLevel("debug") + mvc.Configure(app, configure) // http://localhost:8080 diff --git a/_examples/routing/overview/main.go b/_examples/routing/overview/main.go index ab2d4c57..af35e66f 100644 --- a/_examples/routing/overview/main.go +++ b/_examples/routing/overview/main.go @@ -103,7 +103,7 @@ func main() { usersRoutes.Delete("/{id:uint64}", func(ctx iris.Context) { id, _ := ctx.Params().GetUint64("id") ctx.Writef("delete user by id: %d", id) - }).SetDescription("deletes a user") + }).Describe("deletes a user") // Subdomains, depends on the host, you have to edit the hosts or nginx/caddy's configuration if you use them. // diff --git a/cache/client/handler.go b/cache/client/handler.go index a50f5f7f..959c9bae 100644 --- a/cache/client/handler.go +++ b/cache/client/handler.go @@ -9,6 +9,10 @@ import ( "github.com/kataras/iris/v12/context" ) +func init() { + context.SetHandlerName("iris/cache/client.(*Handler).ServeHTTP-fm", "iris.cache") +} + // Handler the local cache service handler contains // the original response, the memory cache entry and // the validator for each of the incoming requests and post responses diff --git a/context/handler.go b/context/handler.go index 4d4924f8..35c70867 100644 --- a/context/handler.go +++ b/context/handler.go @@ -19,7 +19,7 @@ var ( ) var ( - handlerNames = make(map[*regexp.Regexp]string) + handlerNames = make(map[*nameExpr]string) handlerNamesMu sync.RWMutex ) @@ -39,10 +39,35 @@ func SetHandlerName(original string, replacement string) { } handlerNamesMu.Lock() - handlerNames[regexp.MustCompile(original)] = replacement + // If regexp syntax is wrong + // then its `MatchString` will compare through literal. Fixes an issue + // when a handler name is declared as it's and cause regex parsing expression error, + // e.g. `iris/cache/client.(*Handler).ServeHTTP-fm` + regex, _ := regexp.Compile(original) + handlerNames[&nameExpr{ + literal: original, + regex: regex, + }] = replacement handlerNamesMu.Unlock() } +type nameExpr struct { + regex *regexp.Regexp + literal string +} + +func (expr *nameExpr) MatchString(s string) bool { + if expr.literal == s { // if matches as string, as it's. + return true + } + + if expr.regex != nil { + return expr.regex.MatchString(s) + } + + return false +} + // A Handler responds to an HTTP request. // It writes reply headers and data to the Context.ResponseWriter() and then return. // Returning signals that the request is finished; @@ -79,20 +104,16 @@ func HandlerName(h interface{}) string { pc := valueOf(h).Pointer() name := runtime.FuncForPC(pc).Name() handlerNamesMu.RLock() - for regex, newName := range handlerNames { - if regex.String() == name { // if matches as string, as it's. - name = newName - break - } - - if regex.MatchString(name) { + for expr, newName := range handlerNames { + if expr.MatchString(name) { name = newName break } } + handlerNamesMu.RUnlock() - return name + return trimHandlerName(name) } // HandlerFileLine returns the handler's file and line information. @@ -118,21 +139,98 @@ func HandlerFileLineRel(h interface{}) (file string, line int) { // MainHandlerName tries to find the main handler that end-developer // registered on the provided chain of handlers and returns its function name. -func MainHandlerName(handlers Handlers) (name string, index int) { - for i := 0; i < len(handlers); i++ { - name = HandlerName(handlers[i]) - index = i - if !strings.HasPrefix(name, "github.com/kataras/iris/v12") || - strings.HasPrefix(name, "github.com/kataras/iris/v12/core/router.StripPrefix") || - strings.HasPrefix(name, "github.com/kataras/iris/v12/core/router.FileServer") { - break +func MainHandlerName(handlers ...interface{}) (name string, index int) { + if len(handlers) == 0 { + return + } + + if hs, ok := handlers[0].(Handlers); ok { + tmp := make([]interface{}, 0, len(hs)) + for _, h := range hs { + tmp = append(tmp, h) } + return MainHandlerName(tmp...) + } + + for i := 0; i < len(handlers); i++ { + name = HandlerName(handlers[i]) + if name == "" { + continue + } + + index = i + if !ingoreMainHandlerName(name) { + break + } } return } +func trimHandlerName(name string) string { + // trim the path for Iris' internal middlewares, e.g. + // irs/mvc.GRPC.Apply.func1 + if internalName := PackageName; strings.HasPrefix(name, internalName) { + name = strings.Replace(name, internalName, "iris", 1) + } + + if internalName := "github.com/iris-contrib/middleware"; strings.HasPrefix(name, internalName) { + name = strings.Replace(name, internalName, "iris-contrib", 1) + } + + name = strings.TrimSuffix(name, ".func1") + return name +} + +var ignoreHandlerNames = [...]string{ + "iris/macro/handler.MakeHandler", + "iris/hero.makeHandler.func2", + "iris/core/router.ExecutionOptions.buildHandler", + "iris/core/router.(*APIBuilder).Favicon", + "iris/core/router.StripPrefix", +} + +// IgnoreHandlerName compares a static slice of Iris builtin +// internal methods that should be ignored from trace. +// Some internal methods are kept out of this list for actual debugging. +func IgnoreHandlerName(name string) bool { + for _, ignore := range ignoreHandlerNames { + if name == ignore { + return true + } + } + + return false +} + +var ignoreMainHandlerNames = [...]string{ + "iris.cache", + "iris.basicauth", + "iris.hCaptcha", + "iris.reCAPTCHA", + "iris.profiling", + "iris.recover", +} + +// ingoreMainHandlerName reports whether a main handler of "name" should +// be ignored and continue to match the next. +// The ignored main handler names are literals and respects the `ignoreNameHandlers` too. +func ingoreMainHandlerName(name string) bool { + if IgnoreHandlerName(name) { + // If ignored at all, it can't be the main. + return true + } + + for _, ignore := range ignoreMainHandlerNames { + if name == ignore { + return true + } + } + + return false +} + // Filter is just a type of func(Handler) bool which reports whether an action must be performed // based on the incoming request. // diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 7ff19e9c..c24b5a55 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -399,7 +399,7 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio for _, route := range routes { if route.Method == http.MethodHead { } else { - route.SetDescription(description) + route.Describe(description) route.SetSourceLine(fileName, lineNumber) } @@ -453,6 +453,7 @@ func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handl // before join the middleware + handlers + done handlers and apply the execution rules. mainHandlerName, mainHandlerIndex := context.MainHandlerName(mainHandlers) + mainHandlerFileName, mainHandlerFileNumber := context.HandlerFileLineRel(handlers[mainHandlerIndex]) // re-calculate mainHandlerIndex in favor of the middlewares. @@ -939,7 +940,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route { reqPath = requestPath[0] } - return api.registerResourceRoute(reqPath, h).SetDescription(description) + return api.registerResourceRoute(reqPath, h).Describe(description) } // OnErrorCode registers an error http status code diff --git a/core/router/api_container.go b/core/router/api_container.go index 0e11da53..5a565f70 100644 --- a/core/router/api_container.go +++ b/core/router/api_container.go @@ -117,7 +117,15 @@ func (api *APIContainer) Done(handlersFn ...interface{}) { // See `OnError`, `RegisterDependency`, `Use`, `Done`, `Get`, `Post`, `Put`, `Patch` and `Delete` too. func (api *APIContainer) Handle(method, relativePath string, handlersFn ...interface{}) *Route { handlers := api.convertHandlerFuncs(relativePath, handlersFn...) - return api.Self.Handle(method, relativePath, handlers...) + route := api.Self.Handle(method, relativePath, handlers...) + + // Fix main handler name and source modified by execution rules wrapper. + route.MainHandlerName, route.MainHandlerIndex = context.MainHandlerName(handlersFn...) + if len(handlersFn) > route.MainHandlerIndex { + route.SourceFileName, route.SourceLineNumber = context.HandlerFileLineRel(handlersFn[route.MainHandlerIndex]) + } + + return route } // Get registers a route for the Get HTTP Method. diff --git a/core/router/route.go b/core/router/route.go index 5ad77d75..97344eea 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -145,11 +145,11 @@ func (r *Route) SetStatusOffline() bool { return r.ChangeMethod(MethodNone) } -// SetDescription sets the route's description +// Describe sets the route's description // that will be logged alongside with the route information // in DEBUG log level. // Returns the `Route` itself. -func (r *Route) SetDescription(description string) *Route { +func (r *Route) Describe(description string) *Route { r.Description = description return r } @@ -341,38 +341,10 @@ func (r *Route) ResolvePath(args ...string) string { return formattedPath } -var ignoreHandlersTraces = [...]string{ - "iris/macro/handler.MakeHandler", - "iris/hero.makeHandler.func2", - "iris/core/router.(*APIBuilder).Favicon", - "iris/core/router.StripPrefix", -} - -func ignoreHandlerTrace(name string) bool { - for _, ignore := range ignoreHandlersTraces { - if name == ignore { - return true - } - } - - return false -} - func traceHandlerFile(method, name, line string, number int) string { file := fmt.Sprintf("(%s:%d)", line, number) - // trim the path for Iris' internal middlewares, e.g. - // irs/mvc.GRPC.Apply.func1 - if internalName := context.PackageName; strings.HasPrefix(name, internalName) { - name = strings.Replace(name, internalName, "iris", 1) - } - if internalName := "github.com/iris-contrib/middleware"; strings.HasPrefix(name, internalName) { - name = strings.Replace(name, internalName, "iris-contrib", 1) - } - - name = strings.TrimSuffix(name, ".func1") - - if ignoreHandlerTrace(name) { + if context.IgnoreHandlerName(name) { return "" } @@ -457,7 +429,7 @@ func (r *Route) Trace(w io.Writer) { line int ) - if i == r.MainHandlerIndex { + if i == r.MainHandlerIndex && r.MainHandlerName != "" { // Main handler info can be programmatically // changed to be more specific, respect these changes. name = r.MainHandlerName @@ -466,7 +438,6 @@ func (r *Route) Trace(w io.Writer) { } else { name = context.HandlerName(h) file, line = context.HandlerFileLineRel(h) - // If a middleware, e.g (macro) which changes the main handler index, // skip it. if file == r.SourceFileName && line == r.SourceLineNumber { diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index 1851139a..72342019 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -13,7 +13,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/basicauth.*", "Basic Authentication") + context.SetHandlerName("iris/middleware/basicauth.*", "iris.basicauth") } type ( diff --git a/middleware/hcaptcha/hcaptcha.go b/middleware/hcaptcha/hcaptcha.go index 7b385e4f..db6bb010 100644 --- a/middleware/hcaptcha/hcaptcha.go +++ b/middleware/hcaptcha/hcaptcha.go @@ -11,7 +11,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/hcaptcha.*", "hCaptcha") + context.SetHandlerName("iris/middleware/hcaptcha.*", "iris.hCaptcha") } var ( diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 58b1c547..f770d2a6 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -12,7 +12,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/logger.*", "Request Logger") + context.SetHandlerName("iris/middleware/logger.*", "iris.logger") } type requestLoggerMiddleware struct { diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index f1e1309c..ee53b670 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -12,7 +12,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/pprof.*", "Profiling") + context.SetHandlerName("iris/middleware/pprof.*", "iris.profiling") } // New returns a new pprof (profile, cmdline, symbol, goroutine, heap, threadcreate, debug/block) Middleware. diff --git a/middleware/recaptcha/recaptcha.go b/middleware/recaptcha/recaptcha.go index c6cf9501..29eb7b9a 100644 --- a/middleware/recaptcha/recaptcha.go +++ b/middleware/recaptcha/recaptcha.go @@ -12,7 +12,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/recaptcha.*", "reCAPTCHA") + context.SetHandlerName("iris/middleware/recaptcha.*", "iris.reCAPTCHA") } const ( diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go index 809fd8cf..0ccc8d85 100644 --- a/middleware/recover/recover.go +++ b/middleware/recover/recover.go @@ -10,7 +10,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/recover.*", "Panic Recover") + context.SetHandlerName("iris/middleware/recover.*", "iris.recover") } func getRequestLogs(ctx context.Context) string {