improve route debug info, see HISTORY.md

Former-commit-id: ae245bae5fefa57c5f7663f7d1d661ec68ad366a
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-04-26 06:21:20 +03:00
parent 5d3c96947c
commit 77a79cae58
8 changed files with 115 additions and 39 deletions

View File

@ -180,7 +180,9 @@ Other Improvements:
- New Router [Wrapper](middleware/grpc). - New Router [Wrapper](middleware/grpc).
- New MVC `.Handle(ctrl, mvc.GRPC{...})` option which allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC clients only, see the [example here](_examples/mvc/grpc-compatible). - New MVC `.Handle(ctrl, mvc.GRPC{...})` option which allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC clients only, see the [example here](_examples/mvc/grpc-compatible).
- Improved logging (with `app.Logger().SetLevel("debug")`) for MVC-registered routes. - Improved tracing (with `app.Logger().SetLevel("debug")`) for routes. Example:
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug.png)
- New `iris.WithLowercaseRouting` option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching. - New `iris.WithLowercaseRouting` option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching.

View File

@ -31,7 +31,12 @@ func main() {
func newApp() *iris.Application { func newApp() *iris.Application {
app := iris.New() app := iris.New()
app.Logger().SetLevel("debug") // app.Configure(iris.WithLowercaseRouting) // OPTIONAL.
app.Logger().SetLevel("debug").SetTimeFormat("")
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<h1>Index Page</h1>")
})
ctrl := &myController{} ctrl := &myController{}
// Register gRPC server. // Register gRPC server.

View File

@ -51,25 +51,30 @@ func HandlerFileLine(h interface{}) (file string, line int) {
} }
// HandlerFileLineRel same as `HandlerFileLine` but it returns the path as relative to the "workingDir". // HandlerFileLineRel same as `HandlerFileLine` but it returns the path as relative to the "workingDir".
func HandlerFileLineRel(h interface{}, workingDir string) (string, int) { func HandlerFileLineRel(h interface{}, workingDir string) (file string, line int) {
file, line := HandlerFileLine(h) file, line = HandlerFileLine(h)
if relFile, err := filepath.Rel(workingDir, file); err == nil { if relFile, err := filepath.Rel(workingDir, file); err == nil {
if !strings.HasPrefix(relFile, "..") {
// Only if it's relative to this path, not parent.
file = "./" + relFile file = "./" + relFile
} }
return file, line
} }
// MainHandlerName tries to find the main handler than end-developer return
}
// 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) { func MainHandlerName(handlers Handlers) (name string, index int) {
for i := 0; i < len(handlers); i++ { for i := 0; i < len(handlers); i++ {
name = HandlerName(handlers[i]) name = HandlerName(handlers[i])
index = i
if !strings.HasPrefix(name, "github.com/kataras/iris/v12") || 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.StripPrefix") ||
strings.HasPrefix(name, "github.com/kataras/iris/v12/core/router.FileServer") { strings.HasPrefix(name, "github.com/kataras/iris/v12/core/router.FileServer") {
break break
} }
} }
return return

View File

@ -444,7 +444,9 @@ func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handl
mainHandlers := context.Handlers(handlers) mainHandlers := context.Handlers(handlers)
// before join the middleware + handlers + done handlers and apply the execution rules. // before join the middleware + handlers + done handlers and apply the execution rules.
possibleMainHandlerName := context.MainHandlerName(mainHandlers) possibleMainHandlerName, mainHandlerIndex := context.MainHandlerName(mainHandlers)
wd, _ := os.Getwd()
mainHandlerFileName, mainHandlerFileNumber := context.HandlerFileLineRel(handlers[mainHandlerIndex], wd)
// TODO: for UseGlobal/DoneGlobal that doesn't work. // TODO: for UseGlobal/DoneGlobal that doesn't work.
applyExecutionRules(api.handlerExecutionRules, &beginHandlers, &doneHandlers, &mainHandlers) applyExecutionRules(api.handlerExecutionRules, &beginHandlers, &doneHandlers, &mainHandlers)
@ -470,8 +472,11 @@ func (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handl
continue continue
} }
route.SourceFileName = filename route.SourceFileName = mainHandlerFileName
route.SourceLineNumber = line route.SourceLineNumber = mainHandlerFileNumber
route.RegisterFileName = filename
route.RegisterLineNumber = line
// Add UseGlobal & DoneGlobal Handlers // Add UseGlobal & DoneGlobal Handlers
route.Use(api.beginGlobalHandlers...) route.Use(api.beginGlobalHandlers...)
@ -1011,8 +1016,8 @@ func getCaller() (string, int) {
} }
if !strings.Contains(file, "/kataras/iris") || if !strings.Contains(file, "/kataras/iris") ||
strings.Contains(file, "/kataras/iris/_examples") || strings.Contains(file, "_examples") ||
strings.Contains(file, "iris-contrib/examples") { strings.Contains(file, "examples") {
if relFile, err := filepath.Rel(wd, file); err == nil { if relFile, err := filepath.Rel(wd, file); err == nil {
file = "./" + relFile file = "./" + relFile
} }

View File

@ -2,12 +2,16 @@ package router
import ( import (
"fmt" "fmt"
"net/http"
"os"
"strings" "strings"
"time" "time"
"github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/macro" "github.com/kataras/iris/v12/macro"
"github.com/kataras/iris/v12/macro/handler" "github.com/kataras/iris/v12/macro/handler"
"github.com/kataras/pio"
) )
// Route contains the information about a registered Route. // Route contains the information about a registered Route.
@ -37,8 +41,12 @@ type Route struct {
FormattedPath string `json:"formattedPath"` FormattedPath string `json:"formattedPath"`
// the source code's filename:filenumber that this route was created from. // the source code's filename:filenumber that this route was created from.
SourceFileName string SourceFileName string `json:"sourceFileName"`
SourceLineNumber int SourceLineNumber int `json:"sourceLineNumber"`
// where the route registered.
RegisterFileName string `json:"registerFileName"`
RegisterLineNumber int `json:"registerLineNumber"`
// StaticSites if not empty, refers to the system (or virtual if embedded) directory // StaticSites if not empty, refers to the system (or virtual if embedded) directory
// and sub directories that this "GET" route was registered to serve files and folders // and sub directories that this "GET" route was registered to serve files and folders
@ -317,34 +325,82 @@ func (r *Route) ResolvePath(args ...string) string {
} }
// Trace returns some debug infos as a string sentence. // Trace returns some debug infos as a string sentence.
// Should be called after Build. // Should be called after `Build` state.
//
// It prints the @method: @path (@description) (@route_rel_location)
// * @handler_name (@handler_rel_location)
// * @second_handler ...
// If route and handler line:number locations are equal then the second is ignored.
func (r *Route) Trace() string { func (r *Route) Trace() string {
printfmt := fmt.Sprintf("[%s:%d] %s:", r.SourceFileName, r.SourceLineNumber, r.Method) // Color the method.
color := pio.Black
switch r.Method {
case http.MethodGet:
color = pio.Green
case http.MethodPost:
color = pio.Magenta
case http.MethodPut:
color = pio.Blue
case http.MethodDelete:
color = pio.Red
case http.MethodConnect:
color = pio.Green
case http.MethodHead:
color = pio.Green
case http.MethodPatch:
color = pio.Blue
case http.MethodOptions:
color = pio.Gray
case http.MethodTrace:
color = pio.Yellow
}
path := r.Tmpl().Src
if r.Subdomain != "" { if r.Subdomain != "" {
printfmt += fmt.Sprintf(" %s", r.Subdomain) path = fmt.Sprintf("%s %s", r.Subdomain, path)
} }
printfmt += fmt.Sprintf(" %s", r.Tmpl().Src)
// @method: @path
s := fmt.Sprintf("%s: %s", pio.Rich(r.Method, color), path)
// (@description)
if r.Description != "" { if r.Description != "" {
printfmt += fmt.Sprintf(" (%s)", r.Description) s += fmt.Sprintf(" %s", pio.Rich(r.Description, pio.Cyan, pio.Underline))
} }
mainHandlerName := r.MainHandlerName // (@route_rel_location)
if !strings.HasSuffix(mainHandlerName, ")") { s += fmt.Sprintf(" (%s:%d)", r.RegisterFileName, r.RegisterLineNumber)
mainHandlerName += "()"
// if the main handler is not an anonymous function (so, differs from @route_rel_location)
// then * @handler_name (@handler_rel_location)
if r.SourceFileName != r.RegisterFileName || r.SourceLineNumber != r.RegisterLineNumber {
s += fmt.Sprintf("\n ⬝ %s (%s:%d)", r.MainHandlerName, r.SourceFileName, r.SourceLineNumber)
} }
if l := r.RegisteredHandlersLen(); l > 1 { wd, _ := os.Getwd()
printfmt += fmt.Sprintf(" -> %s and %d more", mainHandlerName, l-1)
} else { for _, h := range r.Handlers {
printfmt += fmt.Sprintf(" -> %s", mainHandlerName) name := context.HandlerName(h)
if name == r.MainHandlerName {
continue
} }
// printfmt := fmt.Sprintf("%s: %s >> %s", r.Method, r.Subdomain+r.Tmpl().Src, r.MainHandlerName) // trim the path for Iris' internal middlewares, e.g.
// if l := len(r.Handlers); l > 0 { // irs/mvc.GRPC.Apply.func1
// printfmt += fmt.Sprintf(" and %d more", l) if internalName := "github.com/kataras/iris/v12"; strings.HasPrefix(name, internalName) {
// } name = strings.Replace(name, internalName, "iris", 1)
return printfmt // without new line. }
file, line := context.HandlerFileLineRel(h, wd)
if file == r.RegisterFileName && line == r.RegisterLineNumber {
continue
}
// * @handler_name (@handler_rel_location)
s += fmt.Sprintf("\n ⬝ %s (%s:%d)", name, file, line)
}
return s
} }
type routeReadOnlyWrapper struct { type routeReadOnlyWrapper struct {

3
go.mod
View File

@ -21,7 +21,8 @@ require (
github.com/iris-contrib/pongo2 v0.0.1 github.com/iris-contrib/pongo2 v0.0.1
github.com/iris-contrib/schema v0.0.1 github.com/iris-contrib/schema v0.0.1
github.com/json-iterator/go v1.1.9 github.com/json-iterator/go v1.1.9
github.com/kataras/golog v0.0.10 github.com/kataras/golog v0.0.11
github.com/kataras/pio v0.0.3
github.com/kataras/neffos v0.0.14 github.com/kataras/neffos v0.0.14
github.com/kataras/sitemap v0.0.5 github.com/kataras/sitemap v0.0.5
github.com/klauspost/compress v1.9.7 github.com/klauspost/compress v1.9.7

12
iris.go
View File

@ -641,7 +641,7 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
srv.Addr = addr srv.Addr = addr
} }
app.logger.Debugf("Host: addr is %s", srv.Addr) // app.logger.Debugf("Host: addr is %s", srv.Addr)
// create the new host supervisor // create the new host supervisor
// bind the constructed server and return it // bind the constructed server and return it
@ -657,21 +657,21 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
app.config.vhost = netutil.ResolveVHost(srv.Addr) app.config.vhost = netutil.ResolveVHost(srv.Addr)
} }
app.logger.Debugf("Host: virtual host is %s", app.config.vhost) // app.logger.Debugf("Host: virtual host is %s", app.config.vhost)
// the below schedules some tasks that will run among the server // the below schedules some tasks that will run among the server
if !app.config.DisableStartupLog { if !app.config.DisableStartupLog {
// show the available info to exit from app. // show the available info to exit from app.
su.RegisterOnServe(host.WriteStartupLogOnServe(app.logger.Printer.Output)) // app.logger.Writer -> Info su.RegisterOnServe(host.WriteStartupLogOnServe(app.logger.Printer.Output)) // app.logger.Writer -> Info
app.logger.Debugf("Host: register startup notifier") // app.logger.Debugf("Host: register startup notifier")
} }
if !app.config.DisableInterruptHandler { if !app.config.DisableInterruptHandler {
// when CTRL+C/CMD+C pressed. // when CTRL+C/CMD+C pressed.
shutdownTimeout := 5 * time.Second shutdownTimeout := 5 * time.Second
host.RegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout)) host.RegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout))
app.logger.Debugf("Host: register server shutdown on interrupt(CTRL+C/CMD+C)") // app.logger.Debugf("Host: register server shutdown on interrupt(CTRL+C/CMD+C)")
} }
su.IgnoredErrors = append(su.IgnoredErrors, app.config.IgnoreServerErrors...) su.IgnoredErrors = append(su.IgnoredErrors, app.config.IgnoreServerErrors...)
@ -1017,7 +1017,9 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
app.Configure(withOrWithout...) app.Configure(withOrWithout...)
app.tryStartTunneling() app.tryStartTunneling()
app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1) if len(app.Hosts) > 0 {
app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1 /* +1 the current */)
}
// this will block until an error(unless supervisor's DeferFlow called from a Task). // this will block until an error(unless supervisor's DeferFlow called from a Task).
err := serve(app) err := serve(app)

View File

@ -59,7 +59,7 @@ func (g GRPC) Apply(c *ControllerActivator) {
if route := c.Handle(http.MethodPost, path, m.Name, pre); route != nil { if route := c.Handle(http.MethodPost, path, m.Name, pre); route != nil {
route.Description = "gRPC" route.Description = "gRPC"
if g.Strict { if g.Strict {
route.Description = "-only" route.Description += "-only"
} }
} }
} }