diff --git a/_examples/routing/overview/main.go b/_examples/routing/overview/main.go index 1ba7fe6c..7fd55671 100644 --- a/_examples/routing/overview/main.go +++ b/_examples/routing/overview/main.go @@ -6,6 +6,7 @@ import ( func main() { app := iris.New() + app.Logger().SetLevel("debug").SetTimeFormat("") // GET: http://localhost:8080 app.Get("/", info) @@ -31,17 +32,15 @@ func main() { // Static assets - // GET: http://localhost:8080/assets/css/bootstrap.min.css - // maps to ./public/assets/css/bootstrap.min.css file at system location. - // GET: http://localhost:8080/assets/js/react.min.js - // maps to ./public/assets/js/react.min.js file at system location. + // GET: http://localhost:8080/assets/css/main.css + // maps to ./public/assets/css/main.css file at system location. app.HandleDir("/assets", "./public/assets") /* OR - // GET: http://localhost:8080/js/react.min.js - // maps to ./public/assets/js/react.min.js file at system location. - app.HandleDir("/js", "./public/assets/js") + // GET: http://localhost:8080/css/main.css + // maps to ./public/assets/css/main.css file at system location. + app.HandleDir("/css", "./public/assets/css") // GET: http://localhost:8080/css/bootstrap.min.css // maps to ./public/assets/css/bootstrap.min.css file at system location. @@ -90,7 +89,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") // Subdomains, depends on the host, you have to edit the hosts or nginx/caddy's configuration if you use them. // diff --git a/_examples/routing/overview/public/assets/css/main.css b/_examples/routing/overview/public/assets/css/main.css new file mode 100644 index 00000000..7db3df1d --- /dev/null +++ b/_examples/routing/overview/public/assets/css/main.css @@ -0,0 +1,3 @@ +body { + background-color: black; +} diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 397a181b..2b618447 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -349,11 +349,14 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio options := getDirOptions(opts...) h := FileServer(directory, options) + description := directory + fileName, lineNumber := context.HandlerFileLine(h) // take those before StripPrefix. // if subdomain, we get the full path of the path only, // because a subdomain can have parties as well // and we need that path to call the `StripPrefix`. - if _, fullpath := splitSubdomainAndPath(joinPath(api.relativePath, requestPath)); fullpath != "/" { + _, fullpath := splitSubdomainAndPath(joinPath(api.relativePath, requestPath)) + if fullpath != "/" { h = StripPrefix(fullpath, h) } @@ -394,7 +397,12 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio } for _, route := range routes { - route.MainHandlerName = `HandleDir(directory: "` + directory + `")` + if route.Method == http.MethodHead { + } else { + route.SetDescription(description) + route.SetSourceLine(fileName, lineNumber) + } + if _, err := api.routes.register(route, api.routeRegisterRule); err != nil { api.errors.Add(err) break @@ -884,8 +892,8 @@ func (api *APIBuilder) StaticContent(reqPath string, cType string, content []byt // // Returns the GET *Route. func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route { + description := favPath favPath = Abs(favPath) - f, err := os.Open(favPath) if err != nil { api.errors.Addf("favicon: file or directory %s not found: %w", favPath, err) @@ -924,7 +932,7 @@ func (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route { reqPath = requestPath[0] } - return api.registerResourceRoute(reqPath, h) + return api.registerResourceRoute(reqPath, h).SetDescription(description) } // OnErrorCode registers an error http status code diff --git a/core/router/route.go b/core/router/route.go index df95b341..84a553a5 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -145,6 +145,23 @@ func (r *Route) SetStatusOffline() bool { return r.ChangeMethod(MethodNone) } +// SetDescription 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 { + r.Description = description + return r +} + +// SetSourceLine sets the route's source caller, useful for debugging. +// Returns the `Route` itself. +func (r *Route) SetSourceLine(fileName string, lineNumber int) *Route { + r.SourceFileName = fileName + r.SourceLineNumber = lineNumber + return r +} + // RestoreStatus will try to restore the status of this route instance, i.e if `SetStatusOffline` called on a "GET" route, // then this function will make this route available with "GET" HTTP Method. // Note if that you want to set status online for an offline registered route then you should call the `ChangeMethod` instead. @@ -324,6 +341,44 @@ func (r *Route) ResolvePath(args ...string) string { return formattedPath } +var ignoreHandlersTraces = [...]string{ + "iris/macro/handler.MakeHandler.func1", + "iris/core/router.(*APIBuilder).Favicon.func1", + "iris/core/router.StripPrefix.func1", +} + +func ignoreHandlerTrace(name string) bool { + for _, ignore := range ignoreHandlersTraces { + if name == ignore { + return true + } + } + + return false +} + +func traceHandlerFile(name, line string, number int, seen map[string]struct{}) string { + file := fmt.Sprintf("(%s:%d)", line, number) + + if _, printed := seen[file]; printed { + return "" + } + + seen[file] = struct{}{} + + // trim the path for Iris' internal middlewares, e.g. + // irs/mvc.GRPC.Apply.func1 + if internalName := "github.com/kataras/iris/v12"; strings.HasPrefix(name, internalName) { + name = strings.Replace(name, internalName, "iris", 1) + } + + if ignoreHandlerTrace(name) { + return "" + } + + return fmt.Sprintf("\n ⬝ %s %s", name, file) +} + // Trace returns some debug infos as a string sentence. // Should be called after `Build` state. // @@ -371,10 +426,12 @@ func (r *Route) Trace() string { // (@route_rel_location) s += fmt.Sprintf(" (%s:%d)", r.RegisterFileName, r.RegisterLineNumber) + seen := make(map[string]struct{}) + // 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) + s += traceHandlerFile(r.MainHandlerName, r.SourceFileName, r.SourceLineNumber, seen) } wd, _ := os.Getwd() @@ -385,19 +442,13 @@ func (r *Route) Trace() string { continue } - // trim the path for Iris' internal middlewares, e.g. - // irs/mvc.GRPC.Apply.func1 - if internalName := "github.com/kataras/iris/v12"; strings.HasPrefix(name, internalName) { - name = strings.Replace(name, internalName, "iris", 1) - } - 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) + s += traceHandlerFile(name, file, line, seen) } return s