From c88f73acbe22dab3f32fbedaa41b22af491cca57 Mon Sep 17 00:00:00 2001 From: Makis Maropoulos Date: Mon, 6 Jun 2016 21:04:38 +0300 Subject: [PATCH] Update to v3.0.0-beta.4 - Logger changes book, examples updated --- README.md | 4 +- config/iris.go | 27 ++++--- config/logger.go | 80 ++++++++++++++----- graceful/graceful.go | 4 +- iris.go | 76 ++++-------------- logger/logger.go | 154 +++++++++++++++++++++++++++--------- middleware/logger/README.md | 63 +-------------- middleware/logger/logger.go | 46 +++-------- 8 files changed, 223 insertions(+), 231 deletions(-) diff --git a/README.md b/README.md index 1e2e9b3f..e8d58815 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [Travis]: http://travis-ci.org/kataras/iris [License Widget]: https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square [License]: https://github.com/kataras/iris/blob/master/LICENSE -[Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta.3-blue.svg?style=flat-square +[Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta.4-blue.svg?style=flat-square [Release]: https://github.com/kataras/iris/releases [Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square [Gitter]: https://gitter.im/kataras/iris @@ -116,7 +116,7 @@ Iris suggests you to use [this](https://github.com/gavv/httpexpect) new suite t Versioning ------------ -Current: **v3.0.0-beta.3** +Current: **v3.0.0-beta.4** > Iris is an active project diff --git a/config/iris.go b/config/iris.go index 4056eda1..868f0783 100644 --- a/config/iris.go +++ b/config/iris.go @@ -21,12 +21,6 @@ type ( // using Config().Sessions... // and so on... Iris struct { - // MaxRequestBodySize Maximum request body size. - // - // The server rejects requests with bodies exceeding this limit. - // - // By default request body size is -1, unlimited. - MaxRequestBodySize int64 // DisablePathCorrection corrects and redirects the requested path to the registed path // for example, if /home/ path is requested but no handler for this Route found, @@ -50,15 +44,18 @@ type ( // Default is false DisablePathEscape bool - // DisableLog turn it to true if you want to disable logger, - // Iris prints/logs ONLY errors, so be careful when you enable it - DisableLog bool - // DisableBanner outputs the iris banner at startup // // Default is false DisableBanner bool + // MaxRequestBodySize Maximum request body size. + // + // The server rejects requests with bodies exceeding this limit. + // + // By default request body size is -1, unlimited. + MaxRequestBodySize int64 + // Profile set to true to enable web pprof (debug profiling) // Default is false, enabling makes available these 7 routes: // /debug/pprof/cmdline @@ -74,6 +71,10 @@ type ( // Default is /debug/pprof , which means yourhost.com/debug/pprof ProfilePath string + // Logger the configuration for the logger + // Iris logs ONLY errors and the banner if enabled + Logger Logger + // Sessions the config for sessions // contains 3(three) properties // Provider: (look /sessions/providers) @@ -86,6 +87,7 @@ type ( // Websocket contains the configs for Websocket's server integration Websocket Websocket + // Mail contains the config for the mail sender service Mail Mail } @@ -117,11 +119,10 @@ func Default() Iris { return Iris{ DisablePathCorrection: false, DisablePathEscape: false, - MaxRequestBodySize: -1, - DisableLog: false, DisableBanner: false, - Profile: false, + MaxRequestBodySize: -1, ProfilePath: DefaultProfilePath, + Logger: DefaultLogger(), Sessions: DefaultSessions(), Render: DefaultRender(), Websocket: DefaultWebsocket(), diff --git a/config/logger.go b/config/logger.go index 25e1f01f..cc00c77f 100644 --- a/config/logger.go +++ b/config/logger.go @@ -1,43 +1,85 @@ package config -import "github.com/imdario/mergo" +import ( + "github.com/fatih/color" + "github.com/imdario/mergo" +) import ( - "io" "os" ) +const DefaultLoggerPrefix = "[IRIS] " + var ( // TimeFormat default time format for any kind of datetime parsing TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" ) type ( - // Logger contains the configs for the Logger + // Logger contains the full configuration options fields for the Logger Logger struct { - Out io.Writer + // Out the (file) writer which the messages/logs will printed to + // Default is os.Stdout + Out *os.File + // Prefix the prefix for each message Prefix string - Flag int + // Disabled default is false + Disabled bool + + // foreground colors single SGR Code + + // ColorFgDefault the foreground color for the normal message bodies + ColorFgDefault int + // ColorFgInfo the foreground color for info messages + ColorFgInfo int + // ColorFgSuccess the foreground color for success messages + ColorFgSuccess int + // ColorFgWarning the foreground color for warning messages + ColorFgWarning int + // ColorFgDanger the foreground color for error messages + ColorFgDanger int + + // background colors single SGR Code + + // ColorBgDefault the background color for the normal message bodies + ColorBgDefault int + // ColorBgInfo the background color for info messages + ColorBgInfo int + // ColorBgSuccess the background color for success messages + ColorBgSuccess int + // ColorBgWarning the background color for warning messages + ColorBgWarning int + // ColorBgDanger the background color for error messages + ColorBgDanger int + + // banners are the force printed/written messages, doesn't care about Disabled field + // ColorFgBanner the foreground color for the banner + ColorFgBanner int } ) // DefaultLogger returns the default configs for the Logger func DefaultLogger() Logger { - return Logger{Out: os.Stdout, Prefix: "", Flag: 0} -} - -// Merge merges the default with the given config and returns the result -func (c Logger) Merge(cfg []Logger) (config Logger) { - - if len(cfg) > 0 { - config = cfg[0] - mergo.Merge(&config, c) - } else { - _default := c - config = _default + return Logger{ + Out: os.Stdout, + Prefix: DefaultLoggerPrefix, + Disabled: false, + // foreground colors + ColorFgDefault: int(color.FgHiWhite), + ColorFgInfo: int(color.FgCyan), + ColorFgSuccess: int(color.FgHiGreen), + ColorFgWarning: int(color.FgHiMagenta), + ColorFgDanger: int(color.FgHiRed), + // background colors + ColorBgDefault: int(color.BgHiBlack), + ColorBgInfo: int(color.BgHiBlack), + ColorBgSuccess: int(color.BgHiBlack), + ColorBgWarning: int(color.BgHiBlack), + ColorBgDanger: int(color.BgHiWhite), + // banner colors + ColorFgBanner: int(color.FgHiBlue), } - - return } // MergeSingle merges the default with the given config and returns the result diff --git a/graceful/graceful.go b/graceful/graceful.go index 19b671c3..60ca19a5 100644 --- a/graceful/graceful.go +++ b/graceful/graceful.go @@ -192,9 +192,9 @@ func (srv *Server) StopChan() <-chan struct{} { } // DefaultLogger returns the logger used by Run, RunWithErr, ListenAndServe, ListenAndServeTLS and Serve. -// The logger outputs to STDERR by default. +// The logger outputs to STDOUT by default. func DefaultLogger() *logger.Logger { - return logger.New() + return logger.New(config.DefaultLogger()) } func (srv *Server) manageConnections(add, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) { diff --git a/iris.go b/iris.go index 98fd7276..8cc0cc1f 100644 --- a/iris.go +++ b/iris.go @@ -5,12 +5,9 @@ package iris import ( "os" - "strconv" "sync" - "sync/atomic" "time" - "github.com/fatih/color" "github.com/kataras/iris/config" "github.com/kataras/iris/logger" "github.com/kataras/iris/mail" @@ -21,21 +18,22 @@ import ( // memory loads the memory session provider _ "github.com/kataras/iris/sessions/providers/memory" // _ redis loads the redis session provider + "fmt" + _ "github.com/kataras/iris/sessions/providers/redis" - "github.com/kataras/iris/utils" "github.com/kataras/iris/websocket" "github.com/klauspost/compress/gzip" ) const ( // Version of the iris - Version = "v3.0.0-beta.3" - banner = ` _____ _ - |_ _| (_) - | | ____ _ ___ - | | | __|| |/ __| - _| |_| | | |\__ \ - |_____|_| |_||___/ ` + Version + ` + Version = "v3.0.0-beta.4" + banner = ` _____ _ + |_ _| (_) + | | ____ _ ___ + | | | __|| |/ __| + _| |_| | | |\__ \ + |_____|_| |_||___/ ` + Version + ` ` ) @@ -64,8 +62,6 @@ var ( /* */ -var stationsRunning = 0 - type ( // Iris is the container of all, server, router, cache and the sync.Pool @@ -99,7 +95,7 @@ func New(cfg ...config.Iris) *Iris { s.router = newRouter(s) // set the Logger - s.logger = logger.New() + s.logger = logger.New(c.Logger) //set the plugin container s.plugins = &PluginContainer{logger: s.logger} @@ -152,60 +148,12 @@ func (s *Iris) initMailService() { } } -func (s *Iris) printBanner() { - c := color.New(color.FgHiBlue).Add(color.Bold) - printTicker := utils.NewTicker() - // for ANY case, we don't want to panic on print banner if anything goes bad - defer func() { - if r := recover(); r != nil { - printTicker.Stop() - } - }() - - var i uint64 - - printTicker.OnTick(func() { - if len(banner) <= int(atomic.LoadUint64(&i)) { - atomic.StoreUint64(&i, 0) - printTicker.Stop() - - c.Add(color.FgGreen) - stationsRunning++ - c.Println() - - if s.server != nil && s.server.IsListening() { - if stationsRunning > 1 { - c.Println("Server[" + strconv.Itoa(stationsRunning) + "]") - - } - c.Printf("%s: Running at %s\n", time.Now().Format(config.TimeFormat), s.server.Config.ListeningAddr) - - } - c.DisableColor() - return - } - c.Printf("%c", banner[i]) - atomic.AddUint64(&i, 1) - - }) - - printTicker.Start(time.Duration(433) * time.Nanosecond) - -} - // PreListen call router's optimize, sets the server's handler and notice the plugins // capital because we need it sometimes, for example inside the graceful // receives the config.Server // returns the station's Server (*server.Server) // it's a non-blocking func func (s *Iris) PreListen(opt config.Server) *server.Server { - // run the printBanner with nice animation until PreListen and PostListen finish - if !s.config.DisableBanner { - go s.printBanner() - } - - // set the logger's state - s.logger.SetEnable(!s.config.DisableLog) // router preparation, runs only once even if called more than one time. if !s.router.optimized { s.router.optimize() @@ -218,6 +166,10 @@ func (s *Iris) PreListen(opt config.Server) *server.Server { } } + if !s.config.DisableBanner { + s.logger.PrintBanner(banner, fmt.Sprintf("%s: Running at %s\n", time.Now().Format(config.TimeFormat), s.server.Config.ListeningAddr)) + } + s.plugins.DoPreListen(s) return s.server diff --git a/logger/logger.go b/logger/logger.go index d3a8cf5e..b189d9d1 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,111 +1,191 @@ package logger import ( - "log" "os" + "github.com/fatih/color" "github.com/kataras/iris/config" + "github.com/mattn/go-colorable" ) var ( // Prefix is the prefix for the logger, default is [IRIS] Prefix = "[IRIS] " + // bannersRan keeps track of the logger's print banner count + bannersRan = 0 ) -// Logger is just a log.Logger +// Logger the logger type Logger struct { - Logger *log.Logger - enabled bool + config config.Logger + underline *color.Color } -// New creates a new Logger. The out variable sets the -// destination to which log data will be written. -// The prefix appears at the beginning of each generated log line. -// The flag argument defines the logging properties. -func New(cfg ...config.Logger) *Logger { - c := config.DefaultLogger().Merge(cfg) - return &Logger{Logger: log.New(c.Out, Prefix+c.Prefix, c.Flag), enabled: true} +// attr takes a color integer and converts it to color.Attribute +func attr(sgr int) color.Attribute { + return color.Attribute(sgr) +} + +// New creates a new Logger from config.Logger configuration +func New(c config.Logger) *Logger { + color.Output = colorable.NewColorable(c.Out) + + l := &Logger{c, color.New(attr(c.ColorBgDefault), attr(c.ColorFgDefault), color.Bold)} + return l +} + +// PrintBanner prints a text (banner) with BannerFgColor, BannerBgColor and a success message at the end +// It doesn't cares if the logger is disabled or not, it will print this +func (l *Logger) PrintBanner(banner string, sucessMessage string) { + c := color.New(attr(l.config.ColorBgDefault), attr(l.config.ColorFgBanner), color.Bold) + c.Println(banner) + bannersRan++ + + if sucessMessage != "" { + c.Add(attr(l.config.ColorBgSuccess), attr(l.config.ColorFgSuccess), color.Bold) + + if bannersRan > 1 { + c.Printf("Server[%#v]\n", bannersRan) + + } + c.Println(sucessMessage) + } + + c.DisableColor() + c = nil +} + +// ResetColors sets the colors to the default +// this func is called every time a success, info, warning, or danger message is printed +func (l *Logger) ResetColors() { + l.underline.Add(attr(l.config.ColorBgDefault), attr(l.config.ColorFgBanner), color.Bold) } // SetEnable true enables, false disables the Logger func (l *Logger) SetEnable(enable bool) { - l.enabled = enable + l.config.Disabled = !enable } // IsEnabled returns true if Logger is enabled, otherwise false func (l *Logger) IsEnabled() bool { - return l.enabled + return !l.config.Disabled } // Print calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Print. func (l *Logger) Print(v ...interface{}) { - if l.enabled { - l.Logger.Print(v...) + if !l.config.Disabled { + l.underline.Print(v...) } } // Printf calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Printf. func (l *Logger) Printf(format string, a ...interface{}) { - if l.enabled { - l.Logger.Printf(format, a...) + if !l.config.Disabled { + l.underline.Printf(format, a...) } } // Println calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Println. func (l *Logger) Println(a ...interface{}) { - if l.enabled { - l.Logger.Println(a...) + if !l.config.Disabled { + l.underline.Println(a...) } } // Fatal is equivalent to l.Print() followed by a call to os.Exit(1). func (l *Logger) Fatal(a ...interface{}) { - if l.enabled { - l.Logger.Fatal(a...) - } else { - os.Exit(1) //we have to exit at any case because this is the Fatal + if !l.config.Disabled { + l.underline.Print(a...) + } + os.Exit(1) } // Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1). func (l *Logger) Fatalf(format string, a ...interface{}) { - if l.enabled { - l.Logger.Fatalf(format, a...) - } else { - os.Exit(1) + if !l.config.Disabled { + l.underline.Printf(format, a...) } + + os.Exit(1) + } // Fatalln is equivalent to l.Println() followed by a call to os.Exit(1). func (l *Logger) Fatalln(a ...interface{}) { - if l.enabled { - l.Logger.Fatalln(a...) - } else { - os.Exit(1) + if !l.config.Disabled { + l.underline.Println(a...) } + + os.Exit(1) + } // Panic is equivalent to l.Print() followed by a call to panic(). func (l *Logger) Panic(a ...interface{}) { - if l.enabled { - l.Logger.Panic(a...) + if !l.config.Disabled { + l.underline.Print(a...) } + + panic("") } // Panicf is equivalent to l.Printf() followed by a call to panic(). func (l *Logger) Panicf(format string, a ...interface{}) { - if l.enabled { - l.Logger.Panicf(format, a...) + if !l.config.Disabled { + l.underline.Printf(format, a...) } + panic("") } // Panicln is equivalent to l.Println() followed by a call to panic(). func (l *Logger) Panicln(a ...interface{}) { - if l.enabled { - l.Logger.Panicln(a...) + if !l.config.Disabled { + l.underline.Println(a...) + } + panic("") +} + +// Sucessf calls l.Output to print to the logger with the Success colors. +// Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Sucessf(format string, a ...interface{}) { + if !l.config.Disabled { + l.underline.Add(attr(l.config.ColorBgSuccess), attr(l.config.ColorFgSuccess)) + l.underline.Printf(format, a...) + l.ResetColors() + } +} + +// Infof calls l.Output to print to the logger with the Info colors. +// Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Infof(format string, a ...interface{}) { + if !l.config.Disabled { + l.underline.Add(attr(l.config.ColorBgInfo), attr(l.config.ColorFgInfo)) + l.underline.Printf(format, a...) + l.ResetColors() + } +} + +// Warningf calls l.Output to print to the logger with the Warning colors. +// Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Warningf(format string, a ...interface{}) { + if !l.config.Disabled { + l.underline.Add(attr(l.config.ColorBgWarning), attr(l.config.ColorFgWarning)) + l.underline.Printf(format, a...) + l.ResetColors() + } +} + +// Dangerf calls l.Output to print to the logger with the Danger colors. +// Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Dangerf(format string, a ...interface{}) { + if !l.config.Disabled { + l.underline.Add(attr(l.config.ColorBgDanger), attr(l.config.ColorFgDanger)) + l.underline.Printf(format, a...) + l.ResetColors() } } diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 66ed2f00..85f73e9c 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -1,64 +1,9 @@ ## Middleware information -This folder contains a middleware for the build'n Iris logger but for the requests. +This folder contains a middleware which is a bridge between Iris station's logger and http requests. + +**Logs the incoming requests** ## How to use -```go -package main - -import ( - "github.com/kataras/iris" - "github.com/kataras/iris/middleware/logger" -) - -func main() { - - iris.UseFunc(logger.Default()) - // or iris.Use(logger.DefaultHandler()) - // or iris.UseFunc(iris.HandlerFunc(logger.DefaultHandler()) - // or iris.Get("/", logger.Default(), func (ctx *iris.Context){}) - // or iris.Get("/", iris.HandlerFunc(logger.DefaultHandler()), func (ctx *iris.Context){}) - - // Custom settings: - // ... - // iris.UseFunc(logger.Custom(writer io.Writer, prefix string, flag int)) - // and so on... - - // Custom options: - // ... - // iris.UseFunc(logger.Default(logger.Options{IP:false})) // don't log the ip - // or iris.UseFunc(logger.Custom(writer io.Writer, prefix string, flag int, logger.Options{IP:false})) - // and so on... - - iris.Get("/", func(ctx *iris.Context) { - ctx.Write("hello") - }) - - iris.Get("/1", func(ctx *iris.Context) { - ctx.Write("hello") - }) - - iris.Get("/3", func(ctx *iris.Context) { - ctx.Write("hello") - }) - - // IF YOU WANT LOGGER TO LOGS THE HTTP ERRORS ALSO THEN: - // FUTURE: iris.OnError(404, logger.Default(logger.Options{Latency: false})) - - // NOW: - errorLogger := logger.Default(logger.Options{Latency: false}) //here we just disable to log the latency, no need for error pages I think - // yes we have options look at the logger.Options inside middleware/logger.go - iris.OnError(404, func(ctx *iris.Context) { - errorLogger.Serve(ctx) - ctx.Write("My Custom 404 error page ") - }) - // - - println("Server is running at :80") - iris.Listen(":80") - -} - - -``` +Read the logger section [here](https://kataras.gitbooks.io/iris/content/logger.html) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index be4f43dc..f772dc4f 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -75,17 +75,20 @@ func (l *loggerMiddleware) Serve(ctx *iris.Context) { //finally print the logs if l.options.Latency { - l.Printf("%s %v %4v %s %s %s", date, status, latency, ip, method, path) + l.Infof("%s %v %4v %s %s %s \n", date, status, latency, ip, method, path) } else { - l.Printf("%s %v %s %s %s", date, status, ip, method, path) + l.Infof("%s %v %s %s %s \n", date, status, ip, method, path) } } -func newLoggerMiddleware(loggerCfg config.Logger, options ...Options) *loggerMiddleware { - loggerCfg = config.DefaultLogger().MergeSingle(loggerCfg) +// Default returns the logger middleware as Handler with the default settings +func New(theLogger *logger.Logger, options ...Options) iris.HandlerFunc { + if theLogger == nil { + theLogger = logger.New(config.DefaultLogger()) + } - l := &loggerMiddleware{Logger: logger.New(loggerCfg)} + l := &loggerMiddleware{Logger: theLogger} if len(options) > 0 { l.options = options[0] @@ -93,36 +96,5 @@ func newLoggerMiddleware(loggerCfg config.Logger, options ...Options) *loggerMid l.options = DefaultOptions() } - return l -} - -//all bellow are just for flexibility - -// DefaultHandler returns the logger middleware with the default settings -func DefaultHandler(options ...Options) iris.Handler { - loggerCfg := config.DefaultLogger() - return newLoggerMiddleware(loggerCfg, options...) -} - -// Default returns the logger middleware as HandlerFunc with the default settings -func Default(options ...Options) iris.HandlerFunc { - return DefaultHandler(options...).Serve -} - -// CustomHandler returns the logger middleware with customized settings -// accepts 3 parameters -// first parameter is the writer (io.Writer) -// second parameter is the prefix of which the message will follow up -// third parameter is the logger.Options -func CustomHandler(loggerCfg config.Logger, options ...Options) iris.Handler { - return newLoggerMiddleware(loggerCfg, options...) -} - -// Custom returns the logger middleware as HandlerFunc with customized settings -// accepts 3 parameters -// first parameter is the writer (io.Writer) -// second parameter is the prefix of which the message will follow up -// third parameter is the logger.Options -func Custom(loggerCfg config.Logger, options ...Options) iris.HandlerFunc { - return CustomHandler(loggerCfg, options...).Serve + return l.Serve }