diff --git a/HISTORY.md b/HISTORY.md index dd9095945..405f3e654 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -364,6 +364,9 @@ Response: Other Improvements: +- Add `Party.SetRoutesNoLog(disable bool) Party` to disable (the new) verbose logging of next routes. +- Add `mvc.Application.SetControllersNoLog(disable bool) *mvc.Application` to disable (the new) verbose logging of next controllers. As requested at [#1630](https://github.com/kataras/iris/issues/1630). + - Fix [#1621](https://github.com/kataras/iris/issues/1621) and add a new `cache.WithKey` to customize the cached entry key. - Add a `Response() *http.Response` to the Response Recorder. diff --git a/_examples/mvc/basic/main.go b/_examples/mvc/basic/main.go index ebdfcde99..e7f14c1eb 100644 --- a/_examples/mvc/basic/main.go +++ b/_examples/mvc/basic/main.go @@ -14,12 +14,17 @@ func main() { app := iris.New() app.Use(recover.New()) app.Logger().SetLevel("debug") + // Disable verbose logging of routes for this and its children parties + // when the log level is "debug": app.SetRoutesNoLog(true) mvc.Configure(app.Party("/basic"), basicMVC) app.Listen(":8080") } func basicMVC(app *mvc.Application) { + // Disable verbose logging of controllers for this and its children mvc apps + // when the log level is "debug": app.SetControllersNoLog(true) + // You can use normal middlewares at MVC apps of course. app.Router.Use(func(ctx iris.Context) { ctx.Application().Logger().Infof("Path: %s", ctx.Path()) diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 46ee5526c..28dcba0e7 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -198,6 +198,8 @@ type APIBuilder struct { macros *macro.Macros // the api builder global routes repository routes *repository + // disables the debug logging of routes under a per-party and its children. + routesNoLog bool // the per-party handlers, order // of handlers registration matters, @@ -643,6 +645,7 @@ func (api *APIBuilder) createRoutes(errorCode int, methods []string, relativePat // route.Use(api.beginGlobalHandlers...) // route.Done(api.doneGlobalHandlers...) + route.NoLog = api.routesNoLog routes[i] = route } @@ -714,6 +717,7 @@ func (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) P logger: api.logger, macros: api.macros, routes: api.routes, + routesNoLog: api.routesNoLog, beginGlobalHandlers: api.beginGlobalHandlers, doneGlobalHandlers: api.doneGlobalHandlers, @@ -866,6 +870,18 @@ func (api *APIBuilder) GetRouteReadOnlyByPath(tmplPath string) context.RouteRead return r.ReadOnly } +// SetRoutesNoLog disables (true) the verbose logging for the next registered +// routes under this Party and its children. +// +// To disable logging for controllers under MVC Application, +// see `mvc/Application.SetControllersNoLog` instead. +// +// Defaults to false when log level is "debug". +func (api *APIBuilder) SetRoutesNoLog(disable bool) Party { + api.routesNoLog = disable + return api +} + type ( // PartyMatcherFunc used to build a filter which decides // if the given Party is responsible to fire its `UseRouter` handlers or not. diff --git a/core/router/party.go b/core/router/party.go index 98110f88e..85d7ec8ec 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -39,6 +39,15 @@ type Party interface { // Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path Macros() *macro.Macros + // SetRoutesNoLog disables (true) the verbose logging for the next registered + // routes under this Party and its children. + // + // To disable logging for controllers under MVC Application, + // see `mvc/Application.SetControllersNoLog` instead. + // + // Defaults to false when log level is "debug". + SetRoutesNoLog(disable bool) Party + // OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code. // Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml // Look `UseError` and `OnAnyErrorCode` too. diff --git a/i18n/loader.go b/i18n/loader.go index 42c76438f..34d88a0ce 100644 --- a/i18n/loader.go +++ b/i18n/loader.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "path/filepath" "strings" + "sync" "text/template" "github.com/kataras/iris/v12/context" @@ -144,16 +145,15 @@ func load(assetNames []string, asset func(string) ([]byte, error), options ...Lo t := m.Languages[langIndex] locale := &defaultLocale{ - index: langIndex, - id: t.String(), - tag: &t, - templateKeys: templateKeys, - lineKeys: lineKeys, - other: other, - + index: langIndex, + id: t.String(), + tag: &t, + templateKeys: templateKeys, + lineKeys: lineKeys, + other: other, defaultMessageFunc: m.defaultMessageFunc, } - + var longestValueLength int for k, v := range keyValues { // fmt.Printf("[%d] %s = %v of type: [%T]\n", langIndex, k, v, v) @@ -176,24 +176,34 @@ func load(assetNames []string, asset func(string) ([]byte, error), options ...Lo } } - if t, err := template.New(k). - Delims(c.Left, c.Right). - Funcs(funcs). - Parse(value); err == nil { + t, err := template.New(k).Delims(c.Left, c.Right).Funcs(funcs).Parse(value) + if err == nil { templateKeys[k] = t - continue } else if c.Strict { return nil, err } + + if valueLength := len(value); valueLength > longestValueLength { + longestValueLength = valueLength + } } lineKeys[k] = value default: other[k] = v } + } - locales[langIndex] = locale + // pre-allocate the initial internal buffer. + // Note that Reset should be called immediately. + initBuf := []byte(strings.Repeat("x", longestValueLength)) + locale.tmplBufPool = &sync.Pool{ + New: func() interface{} { + // try to eliminate the internal "grow" method as much as possible. + return bytes.NewBuffer(initBuf) + }, } + locales[langIndex] = locale } if n := len(locales); n == 0 { @@ -263,6 +273,8 @@ type defaultLocale struct { other map[string]interface{} defaultMessageFunc MessageFunc + + tmplBufPool *sync.Pool } func (l *defaultLocale) Index() int { @@ -289,18 +301,26 @@ func (l *defaultLocale) GetMessageContext(ctx *context.Context, key string, args func (l *defaultLocale) getMessage(langInput, key string, args ...interface{}) string { // search on templates. if tmpl, ok := l.templateKeys[key]; ok { - var data interface{} + var ( + data interface{} + text string + ) if len(args) > 0 { data = args[0] } - buf := new(bytes.Buffer) + buf := l.tmplBufPool.Get().(*bytes.Buffer) + buf.Reset() + err := tmpl.Execute(buf, data) if err != nil { - return err.Error() + text = err.Error() + } else { + text = buf.String() } - return buf.String() + l.tmplBufPool.Put(buf) + return text } if text, ok := l.lineKeys[key]; ok { diff --git a/mvc/mvc.go b/mvc/mvc.go index 0a3199760..57d8a749a 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -33,6 +33,10 @@ type Application struct { Router router.Party Controllers []*ControllerActivator websocketControllers []websocket.ConnHandler + + // Disables verbose logging for controllers under this and its children mvc apps. + // Defaults to false. + controllersNoLog bool } func newApp(subRouter router.Party, container *hero.Container) *Application { @@ -115,6 +119,18 @@ func (app *Application) SetName(appName string) *Application { return app } +// SetControllersNoLog disables verbose logging for next registered controllers +// under this App and its children of `Application.Party` or `Application.Clone`. +// +// To disable logging for routes under a Party, +// see `Party.SetRoutesNoLog` instead. +// +// Defaults to false when log level is "debug". +func (app *Application) SetControllersNoLog(disable bool) *Application { + app.controllersNoLog = disable + return app +} + // Register appends one or more values as dependencies. // The value can be a single struct value-instance or a function // which has one input and one output, the input should be @@ -286,7 +302,9 @@ 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) + if !app.controllersNoLog { + logController(app.Router.Logger(), c) + } return c } @@ -308,6 +326,7 @@ func (app *Application) HandleError(handler func(ctx *context.Context, err error // Example: `.Clone(app.Party("/path")).Handle(new(TodoSubController))`. func (app *Application) Clone(party router.Party) *Application { cloned := newApp(party, app.container.Clone()) + cloned.controllersNoLog = app.controllersNoLog return cloned }