diff --git a/_examples/file-server/basic/main.go b/_examples/file-server/basic/main.go index e261239e..add94614 100644 --- a/_examples/file-server/basic/main.go +++ b/_examples/file-server/basic/main.go @@ -48,5 +48,7 @@ func newApp() *iris.Application { func main() { app := newApp() + app.Logger().SetLevel("debug") + app.Listen(":8080") } diff --git a/core/router/api_builder_benchmark_test.go b/core/router/api_builder_benchmark_test.go index 1ff511f9..afb111d2 100644 --- a/core/router/api_builder_benchmark_test.go +++ b/core/router/api_builder_benchmark_test.go @@ -84,7 +84,7 @@ func BenchmarkAPIBuilder(b *testing.B) { paths := genPaths(routesLength, 15, 42) api := NewAPIBuilder() - requestHandler := NewDefaultHandler(nil) + requestHandler := NewDefaultHandler(nil, nil) b.ReportAllocs() b.ResetTimer() diff --git a/core/router/handler.go b/core/router/handler.go index ec07ac25..5ebc2a9e 100644 --- a/core/router/handler.go +++ b/core/router/handler.go @@ -1,6 +1,7 @@ package router import ( + "fmt" "net/http" "sort" "strings" @@ -11,6 +12,7 @@ import ( macroHandler "github.com/kataras/iris/v12/macro/handler" "github.com/kataras/golog" + "github.com/kataras/pio" ) // RequestHandler the middle man between acquiring a context and releasing it. @@ -25,13 +27,24 @@ type RequestHandler interface { } type routerHandler struct { - trees []*trie - hosts bool // true if at least one route contains a Subdomain. config context.ConfigurationReadOnly + logger *golog.Logger + + trees []*trie + hosts bool // true if at least one route contains a Subdomain. } var _ RequestHandler = &routerHandler{} +// NewDefaultHandler returns the handler which is responsible +// to map the request with a route (aka mux implementation). +func NewDefaultHandler(config context.ConfigurationReadOnly, logger *golog.Logger) RequestHandler { + return &routerHandler{ + config: config, + logger: logger, + } +} + func (h *routerHandler) getTree(method, subdomain string) *trie { for i := range h.trees { t := h.trees[i] @@ -66,14 +79,6 @@ func (h *routerHandler) AddRoute(r *Route) error { return nil } -// NewDefaultHandler returns the handler which is responsible -// to map the request with a route (aka mux implementation). -func NewDefaultHandler(config context.ConfigurationReadOnly) RequestHandler { - return &routerHandler{ - config: config, - } -} - // RoutesProvider should be implemented by // iteral which contains the registered routes. type RoutesProvider interface { // api builder @@ -155,13 +160,13 @@ func (h *routerHandler) Build(provider RoutesProvider) error { } } - if golog.Default.Level == golog.DebugLevel { - tr := "routes" + if logger := h.logger; logger != nil && logger.Level == golog.DebugLevel { + tr := "Routes" if len(registeredRoutes) == 1 { tr = tr[0 : len(tr)-1] } - golog.Debugf("API: %d registered %s", len(registeredRoutes), tr) + // logger.Debugf("%s: %d", tr, len(registeredRoutes)) // group routes by method and print them without the [DBUG] and time info, // the route logs are colorful. @@ -177,24 +182,57 @@ func (h *routerHandler) Build(provider RoutesProvider) error { return } - bckpTimeFormat := golog.Default.TimeFormat - defer golog.SetTimeFormat(bckpTimeFormat) - golog.SetTimeFormat("") + // bckpTimeFormat := logger.TimeFormat + // defer logger.SetTimeFormat(bckpTimeFormat) + // logger.SetTimeFormat("") - newLine := []byte("\n") + type methodCount struct { + method string + count int + } - for _, method := range append(AllMethods, MethodNone) { + allMethods := append(AllMethods, MethodNone) + routeMethodCounts := make([]methodCount, 0, len(allMethods)) + + for i, method := range allMethods { methodRoutes := collect(method) if len(methodRoutes) == 0 { continue } + routeMethodCounts = append(routeMethodCounts, methodCount{method, len(methodRoutes)}) + for _, r := range methodRoutes { - r.Trace(golog.Default.Printer) + r.Trace(logger.Printer) } - golog.Default.Printer.Write(newLine) + if i != len(allMethods)-1 { + logger.Printer.Write(pio.NewLine) + } } + + if n := len(routeMethodCounts); n > 0 { + tr := "routes" + if len(registeredRoutes) == 1 { + tr = tr[0 : len(tr)-1] + } + fmt.Fprintf(logger.Printer, "%s API: %d registered %s (", golog.GetTextForLevel(golog.DebugLevel, true), len(registeredRoutes), tr) + for i, mc := range routeMethodCounts { + // @method: @count + if i > 0 { + if i == n-1 { + fmt.Fprint(logger.Printer, " and ") + } else { + fmt.Fprint(logger.Printer, ", ") + } + } + fmt.Fprintf(logger.Printer, "%d ", mc.count) + pio.WriteRich(logger.Printer, mc.method, traceMethodColor(mc.method)) + } + + fmt.Fprint(logger.Printer, ")\n") + } + } return errgroup.Check(rp) diff --git a/core/router/route.go b/core/router/route.go index c23f99e9..31b51d91 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -349,7 +349,28 @@ func traceHandlerFile(method, name, line string, number int) string { } space := strings.Repeat(" ", len(method)+1) - return fmt.Sprintf("\n%s ⬝ %s %s", space, name, file) + return fmt.Sprintf("\n%s • %s %s", space, name, file) +} + +var methodColors = map[string]int{ + http.MethodGet: pio.Green, + http.MethodPost: pio.Magenta, + http.MethodPut: pio.Blue, + http.MethodDelete: pio.Red, + http.MethodConnect: pio.Green, + http.MethodHead: 23, + http.MethodPatch: pio.Blue, + http.MethodOptions: pio.Gray, + http.MethodTrace: pio.Yellow, + MethodNone: 203, // orange-red. +} + +func traceMethodColor(method string) int { + if color, ok := methodColors[method]; ok { + return color + } + + return pio.Black } // Trace prints some debug info about the Route to the "w". @@ -361,39 +382,18 @@ func traceHandlerFile(method, name, line string, number int) string { // If route and handler line:number locations are equal then the second is ignored. func (r *Route) Trace(w io.Writer) { // 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 = 23 - case http.MethodPatch: - color = pio.Blue - case http.MethodOptions: - color = pio.Gray - case http.MethodTrace: - color = pio.Yellow - case MethodNone: - color = 203 // orange-red. - } + color := traceMethodColor(r.Method) + + // @method: @path + // space := strings.Repeat(" ", len(http.MethodConnect)-len(r.Method)) + // s := fmt.Sprintf("%s: %s", pio.Rich(r.Method, color), path) + pio.WriteRich(w, r.Method, color) path := r.Tmpl().Src if path == "" { path = "/" } - // @method: @path - // space := strings.Repeat(" ", len(http.MethodConnect)-len(r.Method)) - // s := fmt.Sprintf("%s: %s", pio.Rich(r.Method, color), path) - pio.WriteRich(w, r.Method, color) fmt.Fprintf(w, ": %s", path) // (@description) diff --git a/i18n/i18n.go b/i18n/i18n.go index ee019692..fceec970 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -248,6 +248,13 @@ func parsePath(m *Matcher, path string) int { return -1 } +func reverseStrings(s []string) []string { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + func parseLanguage(path string) (language.Tag, bool) { if idx := strings.LastIndexByte(path, '.'); idx > 0 { path = path[0:idx] @@ -259,6 +266,8 @@ func parseLanguage(path string) (language.Tag, bool) { return r == '_' || r == os.PathSeparator || r == '/' || r == '.' }) + names = reverseStrings(names) // see https://github.com/kataras/i18n/issues/1 + for _, s := range names { t, err := language.Parse(s) if err != nil { @@ -303,7 +312,7 @@ func (i *I18n) Tr(lang, format string, args ...interface{}) string { return msg } - return fmt.Sprintf(format, args...) + return "" } const acceptLanguageHeaderKey = "Accept-Language" @@ -372,6 +381,7 @@ func (i *I18n) GetLocale(ctx context.Context) context.Locale { } // GetMessage returns the localized text message for this "r" request based on the key "format". +// It returns an empty string if locale or format not found. func (i *I18n) GetMessage(ctx context.Context, format string, args ...interface{}) string { loc := i.GetLocale(ctx) if loc != nil { @@ -382,7 +392,7 @@ func (i *I18n) GetMessage(ctx context.Context, format string, args ...interface{ } } - return fmt.Sprintf(format, args...) + return "" } // Wrapper returns a new router wrapper. diff --git a/iris.go b/iris.go index 2c78c51c..a4fa5fda 100644 --- a/iris.go +++ b/iris.go @@ -731,7 +731,7 @@ func (app *Application) Build() error { if app.builded { return nil } - start := time.Now() + // start := time.Now() app.builded = true // even if fails. rp := errgroup.New("Application Builder") @@ -803,7 +803,7 @@ func (app *Application) Build() error { } // create the request handler, the default routing handler - routerHandler := router.NewDefaultHandler(app.config) + routerHandler := router.NewDefaultHandler(app.config, app.logger) err := app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false) if err != nil { rp.Err(err) @@ -813,7 +813,7 @@ func (app *Application) Build() error { } // if end := time.Since(start); end.Seconds() > 5 { - app.logger.Debugf("Application: build took %s", time.Since(start)) + // app.logger.Debugf("Application: build took %s", time.Since(start)) return errgroup.Check(rp) } @@ -1172,7 +1172,7 @@ func (app *Application) tryStartTunneling() { // to make subdomains resolution still based on this new remote, public addresses. app.config.vhost = publicAddr[strings.Index(publicAddr, "://")+3:] - directLog := []byte(fmt.Sprintf("⬝ Public Address: %s\n", publicAddr)) + directLog := []byte(fmt.Sprintf("• Public Address: %s\n", publicAddr)) app.Logger().Printer.Output.Write(directLog) } })