more route info improvements

Former-commit-id: ccbe95de0badb1bf448fcc443cecda60772716dc
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-04-28 22:34:36 +03:00
parent 128cd255cb
commit 2a4043a3c2
14 changed files with 147 additions and 61 deletions

View File

@ -35,6 +35,8 @@ func configureAPI(api *iris.APIContainer) {
func main() { func main() {
app := iris.New() app := iris.New()
app.Logger().SetLevel("debug")
app.ConfigureContainer(configureAPI) app.ConfigureContainer(configureAPI)
app.Listen(":8080") app.Listen(":8080")
} }

View File

@ -14,6 +14,8 @@ var cacheHandler = cache.Handler(10 * time.Second)
func main() { func main() {
app := iris.New() app := iris.New()
app.Logger().SetLevel("debug")
mvc.Configure(app, configure) mvc.Configure(app, configure)
// http://localhost:8080 // http://localhost:8080

View File

@ -103,7 +103,7 @@ func main() {
usersRoutes.Delete("/{id:uint64}", func(ctx iris.Context) { usersRoutes.Delete("/{id:uint64}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint64("id") id, _ := ctx.Params().GetUint64("id")
ctx.Writef("delete user by id: %d", 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. // Subdomains, depends on the host, you have to edit the hosts or nginx/caddy's configuration if you use them.
// //

View File

@ -9,6 +9,10 @@ import (
"github.com/kataras/iris/v12/context" "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 // Handler the local cache service handler contains
// the original response, the memory cache entry and // the original response, the memory cache entry and
// the validator for each of the incoming requests and post responses // the validator for each of the incoming requests and post responses

View File

@ -19,7 +19,7 @@ var (
) )
var ( var (
handlerNames = make(map[*regexp.Regexp]string) handlerNames = make(map[*nameExpr]string)
handlerNamesMu sync.RWMutex handlerNamesMu sync.RWMutex
) )
@ -39,10 +39,35 @@ func SetHandlerName(original string, replacement string) {
} }
handlerNamesMu.Lock() 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() 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. // A Handler responds to an HTTP request.
// It writes reply headers and data to the Context.ResponseWriter() and then return. // It writes reply headers and data to the Context.ResponseWriter() and then return.
// Returning signals that the request is finished; // Returning signals that the request is finished;
@ -79,20 +104,16 @@ func HandlerName(h interface{}) string {
pc := valueOf(h).Pointer() pc := valueOf(h).Pointer()
name := runtime.FuncForPC(pc).Name() name := runtime.FuncForPC(pc).Name()
handlerNamesMu.RLock() handlerNamesMu.RLock()
for regex, newName := range handlerNames { for expr, newName := range handlerNames {
if regex.String() == name { // if matches as string, as it's. if expr.MatchString(name) {
name = newName
break
}
if regex.MatchString(name) {
name = newName name = newName
break break
} }
} }
handlerNamesMu.RUnlock() handlerNamesMu.RUnlock()
return name return trimHandlerName(name)
} }
// HandlerFileLine returns the handler's file and line information. // 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 // MainHandlerName tries to find the main handler that end-developer
// registered on the provided chain of handlers and returns its function name. // registered on the provided chain of handlers and returns its function name.
func MainHandlerName(handlers Handlers) (name string, index int) { func MainHandlerName(handlers ...interface{}) (name string, index int) {
for i := 0; i < len(handlers); i++ { if len(handlers) == 0 {
name = HandlerName(handlers[i]) return
index = i }
if !strings.HasPrefix(name, "github.com/kataras/iris/v12") ||
strings.HasPrefix(name, "github.com/kataras/iris/v12/core/router.StripPrefix") || if hs, ok := handlers[0].(Handlers); ok {
strings.HasPrefix(name, "github.com/kataras/iris/v12/core/router.FileServer") { tmp := make([]interface{}, 0, len(hs))
break 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 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 // Filter is just a type of func(Handler) bool which reports whether an action must be performed
// based on the incoming request. // based on the incoming request.
// //

View File

@ -399,7 +399,7 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
for _, route := range routes { for _, route := range routes {
if route.Method == http.MethodHead { if route.Method == http.MethodHead {
} else { } else {
route.SetDescription(description) route.Describe(description)
route.SetSourceLine(fileName, lineNumber) 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. // before join the middleware + handlers + done handlers and apply the execution rules.
mainHandlerName, mainHandlerIndex := context.MainHandlerName(mainHandlers) mainHandlerName, mainHandlerIndex := context.MainHandlerName(mainHandlers)
mainHandlerFileName, mainHandlerFileNumber := context.HandlerFileLineRel(handlers[mainHandlerIndex]) mainHandlerFileName, mainHandlerFileNumber := context.HandlerFileLineRel(handlers[mainHandlerIndex])
// re-calculate mainHandlerIndex in favor of the middlewares. // re-calculate mainHandlerIndex in favor of the middlewares.
@ -939,7 +940,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
reqPath = requestPath[0] reqPath = requestPath[0]
} }
return api.registerResourceRoute(reqPath, h).SetDescription(description) return api.registerResourceRoute(reqPath, h).Describe(description)
} }
// OnErrorCode registers an error http status code // OnErrorCode registers an error http status code

View File

@ -117,7 +117,15 @@ func (api *APIContainer) Done(handlersFn ...interface{}) {
// See `OnError`, `RegisterDependency`, `Use`, `Done`, `Get`, `Post`, `Put`, `Patch` and `Delete` too. // See `OnError`, `RegisterDependency`, `Use`, `Done`, `Get`, `Post`, `Put`, `Patch` and `Delete` too.
func (api *APIContainer) Handle(method, relativePath string, handlersFn ...interface{}) *Route { func (api *APIContainer) Handle(method, relativePath string, handlersFn ...interface{}) *Route {
handlers := api.convertHandlerFuncs(relativePath, handlersFn...) 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. // Get registers a route for the Get HTTP Method.

View File

@ -145,11 +145,11 @@ func (r *Route) SetStatusOffline() bool {
return r.ChangeMethod(MethodNone) 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 // that will be logged alongside with the route information
// in DEBUG log level. // in DEBUG log level.
// Returns the `Route` itself. // Returns the `Route` itself.
func (r *Route) SetDescription(description string) *Route { func (r *Route) Describe(description string) *Route {
r.Description = description r.Description = description
return r return r
} }
@ -341,38 +341,10 @@ func (r *Route) ResolvePath(args ...string) string {
return formattedPath 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 { func traceHandlerFile(method, name, line string, number int) string {
file := fmt.Sprintf("(%s:%d)", line, number) file := fmt.Sprintf("(%s:%d)", line, number)
// trim the path for Iris' internal middlewares, e.g. if context.IgnoreHandlerName(name) {
// 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) {
return "" return ""
} }
@ -457,7 +429,7 @@ func (r *Route) Trace(w io.Writer) {
line int line int
) )
if i == r.MainHandlerIndex { if i == r.MainHandlerIndex && r.MainHandlerName != "" {
// Main handler info can be programmatically // Main handler info can be programmatically
// changed to be more specific, respect these changes. // changed to be more specific, respect these changes.
name = r.MainHandlerName name = r.MainHandlerName
@ -466,7 +438,6 @@ func (r *Route) Trace(w io.Writer) {
} else { } else {
name = context.HandlerName(h) name = context.HandlerName(h)
file, line = context.HandlerFileLineRel(h) file, line = context.HandlerFileLineRel(h)
// If a middleware, e.g (macro) which changes the main handler index, // If a middleware, e.g (macro) which changes the main handler index,
// skip it. // skip it.
if file == r.SourceFileName && line == r.SourceLineNumber { if file == r.SourceFileName && line == r.SourceLineNumber {

View File

@ -13,7 +13,7 @@ import (
) )
func init() { func init() {
context.SetHandlerName("iris/middleware/basicauth.*", "Basic Authentication") context.SetHandlerName("iris/middleware/basicauth.*", "iris.basicauth")
} }
type ( type (

View File

@ -11,7 +11,7 @@ import (
) )
func init() { func init() {
context.SetHandlerName("iris/middleware/hcaptcha.*", "hCaptcha") context.SetHandlerName("iris/middleware/hcaptcha.*", "iris.hCaptcha")
} }
var ( var (

View File

@ -12,7 +12,7 @@ import (
) )
func init() { func init() {
context.SetHandlerName("iris/middleware/logger.*", "Request Logger") context.SetHandlerName("iris/middleware/logger.*", "iris.logger")
} }
type requestLoggerMiddleware struct { type requestLoggerMiddleware struct {

View File

@ -12,7 +12,7 @@ import (
) )
func init() { 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. // New returns a new pprof (profile, cmdline, symbol, goroutine, heap, threadcreate, debug/block) Middleware.

View File

@ -12,7 +12,7 @@ import (
) )
func init() { func init() {
context.SetHandlerName("iris/middleware/recaptcha.*", "reCAPTCHA") context.SetHandlerName("iris/middleware/recaptcha.*", "iris.reCAPTCHA")
} }
const ( const (

View File

@ -10,7 +10,7 @@ import (
) )
func init() { func init() {
context.SetHandlerName("iris/middleware/recover.*", "Panic Recover") context.SetHandlerName("iris/middleware/recover.*", "iris.recover")
} }
func getRequestLogs(ctx context.Context) string { func getRequestLogs(ctx context.Context) string {