diff --git a/_examples/http_request/request-logger/main.go b/_examples/http_request/request-logger/main.go index 3eb64b43..327b3e69 100644 --- a/_examples/http_request/request-logger/main.go +++ b/_examples/http_request/request-logger/main.go @@ -22,7 +22,10 @@ func main() { // if !empty then its contents derives from `ctx.Values().Get("logger_message") // will be added to the logs. - MessageContextKey: "logger_message", + MessageContextKeys: []string{"logger_message"}, + + // if !empty then its contents derives from `ctx.GetHeader("User-Agent") + MessageHeaderKeys: []string{"User-Agent"}, }) app.Use(customLogger) diff --git a/_examples/websocket/README.md b/_examples/websocket/README.md index c7d9c68f..6d7f41cf 100644 --- a/_examples/websocket/README.md +++ b/_examples/websocket/README.md @@ -116,7 +116,7 @@ func main() { app.Get("/echo", ws.Handler()) // serve the javascript built'n client-side library, - // see weboskcets.html script tags, this path is used. + // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) }) diff --git a/_examples/websocket/chat/main.go b/_examples/websocket/chat/main.go index d1adeacb..fce0a664 100644 --- a/_examples/websocket/chat/main.go +++ b/_examples/websocket/chat/main.go @@ -36,7 +36,7 @@ func setupWebsocket(app *iris.Application) { app.Get("/echo", ws.Handler()) // serve the javascript built'n client-side library, - // see weboskcets.html script tags, this path is used. + // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) }) diff --git a/_examples/websocket/connectionlist/main.go b/_examples/websocket/connectionlist/main.go index f736bb21..490f85c6 100644 --- a/_examples/websocket/connectionlist/main.go +++ b/_examples/websocket/connectionlist/main.go @@ -26,7 +26,7 @@ func main() { app.Get("/my_endpoint", ws.Handler()) // serve the javascript built'n client-side library, - // see weboskcets.html script tags, this path is used. + // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) }) diff --git a/_examples/websocket/secure/main.go b/_examples/websocket/secure/main.go index 83f3d6ee..3d289114 100644 --- a/_examples/websocket/secure/main.go +++ b/_examples/websocket/secure/main.go @@ -27,7 +27,7 @@ func main() { app.Get("/my_endpoint", ws.Handler()) // serve the javascript built'n client-side library, - // see weboskcets.html script tags, this path is used. + // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) }) diff --git a/configuration.go b/configuration.go index 2ab09e3b..0d08fc3e 100644 --- a/configuration.go +++ b/configuration.go @@ -8,7 +8,6 @@ import ( "runtime" "github.com/BurntSushi/toml" - "github.com/kataras/golog" "gopkg.in/yaml.v2" "github.com/kataras/iris/context" @@ -17,8 +16,6 @@ import ( const globalConfigurationKeyword = "~" -var globalConfigurationExisted = false - // homeConfigurationFilename returns the physical location of the global configuration(yaml or toml) file. // This is useful when we run multiple iris servers that share the same // configuration, even with custom values at its "Other" field. @@ -28,28 +25,6 @@ func homeConfigurationFilename(ext string) string { return filepath.Join(homeDir(), "iris"+ext) } -func init() { - filename := homeConfigurationFilename(".yml") - c, err := parseYAML(filename) - if err != nil { - // this error will be occurred the first time that the configuration - // file doesn't exist. - // Create the YAML-ONLY global configuration file now using the default configuration 'c'. - // This is useful when we run multiple iris servers that share the same - // configuration, even with custom values at its "Other" field. - out, err := yaml.Marshal(&c) - - if err == nil { - err = ioutil.WriteFile(filename, out, os.FileMode(0666)) - } - if err != nil { - golog.Debugf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err) - } - } else { - globalConfigurationExisted = true - } -} - func homeDir() (home string) { u, err := user.Current() if u != nil && err == nil { diff --git a/configuration_test.go b/configuration_test.go index 61915011..ab770fbe 100644 --- a/configuration_test.go +++ b/configuration_test.go @@ -6,6 +6,8 @@ import ( "reflect" "testing" "time" + + "gopkg.in/yaml.v2" ) // $ go test -v -run TestConfiguration* @@ -89,7 +91,30 @@ func TestConfigurationOptionsDeep(t *testing.T) { t.Fatalf("DEEP configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has) } } + +func createGlobalConfiguration(t *testing.T) { + filename := homeConfigurationFilename(".yml") + c, err := parseYAML(filename) + if err != nil { + // this error will be occurred the first time that the configuration + // file doesn't exist. + // Create the YAML-ONLY global configuration file now using the default configuration 'c'. + // This is useful when we run multiple iris servers that share the same + // configuration, even with custom values at its "Other" field. + out, err := yaml.Marshal(&c) + + if err == nil { + err = ioutil.WriteFile(filename, out, os.FileMode(0666)) + } + if err != nil { + t.Fatalf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err) + } + } +} + func TestConfigurationGlobal(t *testing.T) { + createGlobalConfiguration(t) + testConfigurationGlobal(t, WithGlobalConfiguration) // globalConfigurationKeyword = "~"" testConfigurationGlobal(t, WithConfiguration(YAML(globalConfigurationKeyword))) diff --git a/core/maintenance/maintenance.go b/core/maintenance/maintenance.go index 4d098ab0..613b67fd 100644 --- a/core/maintenance/maintenance.go +++ b/core/maintenance/maintenance.go @@ -1,6 +1,6 @@ package maintenance // Start starts the maintenance process. -func Start(globalConfigurationExisted bool) { - CheckForUpdates(!globalConfigurationExisted) +func Start() { + CheckForUpdates() } diff --git a/core/maintenance/version.go b/core/maintenance/version.go index 34498252..f663cb60 100644 --- a/core/maintenance/version.go +++ b/core/maintenance/version.go @@ -18,7 +18,7 @@ const ( // CheckForUpdates checks for any available updates // and asks for the user if want to update now or not. -func CheckForUpdates(ft bool) { +func CheckForUpdates() { v := version.Acquire() updateAvailale := v.Compare(Version) == version.Smaller diff --git a/doc.go b/doc.go index babd25ec..d87a09cc 100644 --- a/doc.go +++ b/doc.go @@ -1416,7 +1416,7 @@ Example Server Code: app.Get("/echo", ws.Handler()) // serve the javascript built'n client-side library, - // see weboskcets.html script tags, this path is used. + // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) }) diff --git a/iris.go b/iris.go index c2efb75a..5bbcafff 100644 --- a/iris.go +++ b/iris.go @@ -771,7 +771,7 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error { app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1) if !app.config.DisableVersionChecker { - go maintenance.Start(globalConfigurationExisted) + go maintenance.Start() } // this will block until an error(unless supervisor's DeferFlow called from a Task). diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 2b324024..eb3e2143 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -37,7 +37,7 @@ type Config struct { // Defaults to false. Columns bool - // MessageContextKey if not empty, + // MessageContextKeys if not empty, // the middleware will try to fetch // the contents with `ctx.Values().Get(MessageContextKey)` // and if available then these contents will be @@ -46,12 +46,23 @@ type Config struct { // a new column will be added named 'Message'. // // Defaults to empty. - MessageContextKey string + MessageContextKeys []string + + // MessageHeaderKeys if not empty, + // the middleware will try to fetch + // the contents with `ctx.Values().Get(MessageHeaderKey)` + // and if available then these contents will be + // appended as part of the logs (with `%v`, in order to be able to set a struct too), + // if Columns field was setted to true then + // a new column will be added named 'HeaderMessage'. + // + // Defaults to empty. + MessageHeaderKeys []string // LogFunc is the writer which logs are written to, // if missing the logger middleware uses the app.Logger().Infof instead. // Note that message argument can be empty. - LogFunc func(now time.Time, latency time.Duration, status, ip, method, path string, message interface{}) + LogFunc func(now time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{}) // Skippers used to skip the logging i.e by `ctx.Path()` and serve // the next/main handler immediately. Skippers []SkipperFunc @@ -66,15 +77,14 @@ type Config struct { // LogFunc and Skippers to nil as well. func DefaultConfig() Config { return Config{ - Status: true, - IP: true, - Method: true, - Path: true, - Columns: false, - MessageContextKey: "", - LogFunc: nil, - Skippers: nil, - skip: nil, + Status: true, + IP: true, + Method: true, + Path: true, + Columns: false, + LogFunc: nil, + Skippers: nil, + skip: nil, } } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index b4796484..e9abeb36 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -70,19 +70,37 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) { } var message interface{} - if ctxKey := l.config.MessageContextKey; ctxKey != "" { - message = ctx.Values().Get(ctxKey) + if ctxKeys := l.config.MessageContextKeys; len(ctxKeys) > 0 { + for _, key := range ctxKeys { + msg := ctx.Values().Get(key) + if message == nil { + message = msg + } else { + message = fmt.Sprintf(" %v %v", message, msg) + } + } + } + var headerMessage interface{} + if headerKeys := l.config.MessageHeaderKeys; len(headerKeys) > 0 { + for _, key := range headerKeys { + msg := ctx.GetHeader(key) + if headerMessage == nil { + headerMessage = msg + } else { + headerMessage = fmt.Sprintf(" %v %v", headerMessage, msg) + } + } } // print the logs if logFunc := l.config.LogFunc; logFunc != nil { - logFunc(endTime, latency, status, ip, method, path, message) + logFunc(endTime, latency, status, ip, method, path, message, headerMessage) return } if l.config.Columns { endTimeFormatted := endTime.Format("2006/01/02 - 15:04:05") - output := Columnize(endTimeFormatted, latency, status, ip, method, path, message) + output := Columnize(endTimeFormatted, latency, status, ip, method, path, message, headerMessage) ctx.Application().Logger().Printer.Output.Write([]byte(output)) return } @@ -91,12 +109,16 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) { if message != nil { line += fmt.Sprintf(" %v", message) } + + if headerMessage != nil { + line += fmt.Sprintf(" %v", headerMessage) + } ctx.Application().Logger().Info(line) } // Columnize formats the given arguments as columns and returns the formatted output, // note that it appends a new line to the end. -func Columnize(nowFormatted string, latency time.Duration, status, ip, method, path string, message interface{}) string { +func Columnize(nowFormatted string, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{}) string { titles := "Time | Status | Latency | IP | Method | Path" line := fmt.Sprintf("%s | %v | %4v | %s | %s | %s", nowFormatted, status, latency, ip, method, path) @@ -105,6 +127,11 @@ func Columnize(nowFormatted string, latency time.Duration, status, ip, method, p line += fmt.Sprintf(" | %v", message) } + if headerMessage != nil { + titles += " | HeaderMessage" + line += fmt.Sprintf(" | %v", headerMessage) + } + outputC := []string{ titles, line, diff --git a/websocket/websocket.go b/websocket/websocket.go index 20f4b58a..9cf4fdf0 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -53,7 +53,7 @@ Example code: app.Get("/echo", ws.Handler()) // serve the javascript built'n client-side library, - // see weboskcets.html script tags, this path is used. + // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx context.Context) { ctx.Write(websocket.ClientSource) })