mirror of
https://github.com/kataras/iris.git
synced 2025-12-18 02:17:05 +00:00
mvc: struct field and method dependency logs on debug level. Read HISTORY.md
- remove Party.GetReporter - Read HISTORY.md
This commit is contained in:
@@ -163,6 +163,11 @@ func (c *ControllerActivator) Name() string {
|
||||
return c.fullName
|
||||
}
|
||||
|
||||
// RelName returns the path relatively to the main package.
|
||||
func (c *ControllerActivator) RelName() string {
|
||||
return strings.TrimPrefix(c.fullName, "main.")
|
||||
}
|
||||
|
||||
// Router is the standard Iris router's public API.
|
||||
// With this you can register middleware, view layouts, subdomains, serve static files
|
||||
// and even add custom standard iris handlers as normally.
|
||||
@@ -243,7 +248,7 @@ func (c *ControllerActivator) DependenciesReadOnly() []*hero.Dependency {
|
||||
|
||||
// Dependencies returns a value which can manage the controller's dependencies.
|
||||
func (c *ControllerActivator) Dependencies() *hero.Container {
|
||||
return c.app.container
|
||||
return c.app.container // although the controller's one are: c.injector.Container
|
||||
}
|
||||
|
||||
// checks if a method is already registered.
|
||||
@@ -293,8 +298,8 @@ func (c *ControllerActivator) activate() {
|
||||
return
|
||||
}
|
||||
|
||||
c.parseHTTPErrorHandler()
|
||||
c.parseMethods()
|
||||
c.parseHTTPErrorHandler()
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) parseHTTPErrorHandler() {
|
||||
@@ -316,7 +321,7 @@ func (c *ControllerActivator) parseMethod(m reflect.Method) {
|
||||
httpMethod, httpPath, err := parseMethod(c.app.Router.Macros(), m, c.isReservedMethod)
|
||||
if err != nil {
|
||||
if err != errSkip {
|
||||
c.addErr(fmt.Errorf("MVC: fail to parse the route path and HTTP method for '%s.%s': %v", c.fullName, m.Name, err))
|
||||
c.logErrorf("MVC: fail to parse the route path and HTTP method for '%s.%s': %v", c.fullName, m.Name, err)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -325,8 +330,8 @@ func (c *ControllerActivator) parseMethod(m reflect.Method) {
|
||||
c.Handle(httpMethod, httpPath, m.Name)
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) addErr(err error) bool {
|
||||
return c.app.Router.GetReporter().Err(err) != nil
|
||||
func (c *ControllerActivator) logErrorf(format string, args ...interface{}) {
|
||||
c.Router().Logger().Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Handle registers a route based on a http method, the route's path
|
||||
@@ -350,7 +355,7 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
||||
// handleHTTPError is called when a controller's method
|
||||
// with the "HandleHTTPError" is found. That method
|
||||
// can accept dependencies like the rest but if it's not called manually
|
||||
// then any dynamic dependencies depending on succesful requests
|
||||
// then any dynamic dependencies depending on successful requests
|
||||
// may fail - this is end-developer's job;
|
||||
// to register the correct dependencies or not do it all on that method.
|
||||
//
|
||||
@@ -364,20 +369,11 @@ func (c *ControllerActivator) handleHTTPError(funcName string) *router.Route {
|
||||
|
||||
routes := c.app.Router.OnAnyErrorCode(handler)
|
||||
if len(routes) == 0 {
|
||||
err := fmt.Errorf("MVC: unable to register an HTTP error code handler for '%s.%s'", c.fullName, funcName)
|
||||
c.addErr(err)
|
||||
c.logErrorf("MVC: unable to register an HTTP error code handler for '%s.%s'", c.fullName, funcName)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, r := range routes {
|
||||
r.Description = "controller"
|
||||
r.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName)
|
||||
if m, ok := c.Type.MethodByName(funcName); ok {
|
||||
r.SourceFileName, r.SourceLineNumber = context.HandlerFileLineRel(m.Func)
|
||||
}
|
||||
}
|
||||
|
||||
c.routes[funcName] = routes
|
||||
c.saveRoutes(funcName, routes, true)
|
||||
return routes[0]
|
||||
}
|
||||
|
||||
@@ -410,16 +406,19 @@ func (c *ControllerActivator) handleMany(method, path, funcName string, override
|
||||
// register the handler now.
|
||||
routes := c.app.Router.HandleMany(method, path, append(middleware, handler)...)
|
||||
if routes == nil {
|
||||
c.addErr(fmt.Errorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName))
|
||||
c.logErrorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.saveRoutes(funcName, routes, override)
|
||||
return routes
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) saveRoutes(funcName string, routes []*router.Route, override bool) {
|
||||
relName := c.RelName()
|
||||
for _, r := range routes {
|
||||
// change the main handler's name and file:line
|
||||
// in order to respect the controller's and give
|
||||
// a proper debug/log message.
|
||||
r.Description = "controller"
|
||||
r.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName)
|
||||
r.Description = relName
|
||||
r.MainHandlerName = fmt.Sprintf("%s.%s", relName, funcName)
|
||||
if m, ok := c.Type.MethodByName(funcName); ok {
|
||||
r.SourceFileName, r.SourceLineNumber = context.HandlerFileLineRel(m.Func)
|
||||
}
|
||||
@@ -428,15 +427,12 @@ func (c *ControllerActivator) handleMany(method, path, funcName string, override
|
||||
// add this as a reserved method name in order to
|
||||
// be sure that the same route
|
||||
// (method is allowed to be registered more than one on different routes - v11.2).
|
||||
|
||||
existingRoutes, exist := c.routes[funcName]
|
||||
if override || !exist {
|
||||
c.routes[funcName] = routes
|
||||
} else {
|
||||
c.routes[funcName] = append(existingRoutes, routes...)
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func (c *ControllerActivator) handlerOf(relPath, methodName string) context.Handler {
|
||||
|
||||
139
mvc/mvc.go
139
mvc/mvc.go
@@ -1,6 +1,7 @@
|
||||
package mvc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/kataras/iris/v12/websocket"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/kataras/pio"
|
||||
)
|
||||
|
||||
// Application is the high-level component of the "mvc" package.
|
||||
@@ -282,6 +284,10 @@ func (app *Application) handle(controller interface{}, options ...Option) *Contr
|
||||
}
|
||||
|
||||
app.Controllers = append(app.Controllers, c)
|
||||
|
||||
// Note: log on register-time, so they can catch any failures before build.
|
||||
logController(app.Router.Logger(), c)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -313,3 +319,136 @@ func (app *Application) Clone(party router.Party) *Application {
|
||||
func (app *Application) Party(relativePath string, middleware ...context.Handler) *Application {
|
||||
return app.Clone(app.Router.Party(relativePath, middleware...))
|
||||
}
|
||||
|
||||
var childNameReplacer = strings.NewReplacer("*", "", "(", "", ")", "")
|
||||
|
||||
// TODO: instead of this I want to get in touch with tools like "graphviz"
|
||||
// so we can put all that information (and the API) inside web graphs,
|
||||
// it will be easier for developers to see the flow of the whole application,
|
||||
// but probalby I will never find time for that as we have higher priorities...just a reminder though.
|
||||
func logController(logger *golog.Logger, c *ControllerActivator) {
|
||||
if logger.Level != golog.DebugLevel {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
[DBUG] controller.GreetController
|
||||
╺ Service → ./service/greet_service.go:16
|
||||
╺ Get
|
||||
GET /greet
|
||||
• iris.Context
|
||||
• service.Other → ./service/other_service.go:11
|
||||
*/
|
||||
|
||||
bckpNewLine := logger.NewLine
|
||||
bckpTimeFormat := logger.TimeFormat
|
||||
logger.NewLine = false
|
||||
logger.TimeFormat = ""
|
||||
|
||||
printer := logger.Printer
|
||||
|
||||
reports := c.injector.Container.Reports
|
||||
ctrlName := c.RelName()
|
||||
logger.Debugf("%s\n", ctrlName)
|
||||
|
||||
longestNameLen := 0
|
||||
for _, report := range reports {
|
||||
for _, entry := range report.Entries {
|
||||
if n := len(entry.InputFieldName); n > longestNameLen {
|
||||
if strings.HasSuffix(entry.InputFieldName, ctrlName) {
|
||||
continue
|
||||
}
|
||||
longestNameLen = n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
longestMethodName := 0
|
||||
for methodName := range c.routes {
|
||||
if n := len(methodName); n > longestMethodName {
|
||||
longestMethodName = n
|
||||
}
|
||||
}
|
||||
|
||||
lastColorCode := -1
|
||||
|
||||
for _, report := range reports {
|
||||
|
||||
childName := childNameReplacer.Replace(report.Name)
|
||||
if idx := strings.Index(childName, c.Name()); idx >= 0 {
|
||||
childName = childName[idx+len(c.Name()):] // it's always +1 otherwise should be reported as BUG.
|
||||
}
|
||||
|
||||
if childName != "" && childName[0] == '.' {
|
||||
// It's a struct's method.
|
||||
|
||||
childName = childName[1:]
|
||||
|
||||
for _, route := range c.routes[childName] {
|
||||
if route.NoLog {
|
||||
continue
|
||||
}
|
||||
|
||||
// Let them be logged again with the middlewares, e.g UseRouter or UseGlobal after this MVC app created.
|
||||
// route.NoLog = true
|
||||
|
||||
colorCode := router.TraceTitleColorCode(route.Method)
|
||||
|
||||
// group same methods (or errors).
|
||||
if lastColorCode == -1 {
|
||||
lastColorCode = colorCode
|
||||
} else if lastColorCode != colorCode {
|
||||
lastColorCode = colorCode
|
||||
fmt.Fprintln(printer)
|
||||
}
|
||||
|
||||
fmt.Fprint(printer, " ╺ ")
|
||||
pio.WriteRich(printer, childName, colorCode)
|
||||
|
||||
entries := report.Entries[1:] // the ctrl value is always the first input argument so 1:..
|
||||
if len(entries) == 0 {
|
||||
fmt.Print("()")
|
||||
}
|
||||
fmt.Fprintln(printer)
|
||||
|
||||
// pio.WriteRich(printer, " "+route.GetTitle(), colorCode)
|
||||
fmt.Fprintf(printer, " %s\n", route.String())
|
||||
|
||||
for _, entry := range entries {
|
||||
fileLine := ""
|
||||
if !strings.Contains(entry.DependencyFile, "kataras/iris/") {
|
||||
fileLine = fmt.Sprintf("→ %s:%d", entry.DependencyFile, entry.DependencyLine)
|
||||
}
|
||||
|
||||
fieldName := entry.InputFieldName
|
||||
|
||||
spaceRequired := longestNameLen - len(fieldName)
|
||||
if spaceRequired < 0 {
|
||||
spaceRequired = 0
|
||||
}
|
||||
// → ⊳ ↔
|
||||
fmt.Fprintf(printer, " • %s%s %s\n", fieldName, strings.Repeat(" ", spaceRequired), fileLine)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// It's a struct's field.
|
||||
for _, entry := range report.Entries {
|
||||
fileLine := ""
|
||||
if !strings.Contains(entry.DependencyFile, "kataras/iris/") {
|
||||
fileLine = fmt.Sprintf("→ %s:%d", entry.DependencyFile, entry.DependencyLine)
|
||||
}
|
||||
|
||||
fieldName := entry.InputFieldName
|
||||
spaceRequired := longestNameLen + 2 - len(fieldName) // plus the two spaces because it's not collapsed.
|
||||
if spaceRequired < 0 {
|
||||
spaceRequired = 0
|
||||
}
|
||||
fmt.Fprintf(printer, " ╺ %s%s %s\n", fieldName, strings.Repeat(" ", spaceRequired), fileLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
// fmt.Fprintln(printer)
|
||||
|
||||
logger.NewLine = bckpNewLine
|
||||
logger.TimeFormat = bckpTimeFormat
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user