func New(prefix string) (l *Logger) { l = &Logger{ level: INFO, prefix: prefix, template: l.newTemplate(defaultFormat), color: color.New(), bufferPool: sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 256)) }, }, } l.initLevels() l.SetOutput(colorable.NewColorableStdout()) return }
// LoggerWithConfig returns a Logger middleware with config. // See: `Logger()`. func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { // Defaults if config.Skipper == nil { config.Skipper = DefaultLoggerConfig.Skipper } if config.Format == "" { config.Format = DefaultLoggerConfig.Format } if config.Output == nil { config.Output = DefaultLoggerConfig.Output } config.template = fasttemplate.New(config.Format, "${", "}") config.color = color.New() if w, ok := config.Output.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) { config.color.Disable() } config.pool = sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 256)) }, } return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) (err error) { if config.Skipper(c) { return next(c) } req := c.Request() res := c.Response() start := time.Now() if err = next(c); err != nil { c.Error(err) } stop := time.Now() buf := config.pool.Get().(*bytes.Buffer) buf.Reset() defer config.pool.Put(buf) _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { switch tag { case "time_rfc3339": return w.Write([]byte(time.Now().Format(time.RFC3339))) case "remote_ip": ra := c.RealIP() return w.Write([]byte(ra)) case "host": return w.Write([]byte(req.Host)) case "uri": return w.Write([]byte(req.RequestURI)) case "method": return w.Write([]byte(req.Method)) case "path": p := req.URL.Path if p == "" { p = "/" } return w.Write([]byte(p)) case "referer": return w.Write([]byte(req.Referer())) case "user_agent": return w.Write([]byte(req.UserAgent())) case "status": n := res.Status s := config.color.Green(n) switch { case n >= 500: s = config.color.Red(n) case n >= 400: s = config.color.Yellow(n) case n >= 300: s = config.color.Cyan(n) } return w.Write([]byte(s)) case "latency": l := stop.Sub(start).Nanoseconds() / 1000 return w.Write([]byte(strconv.FormatInt(l, 10))) case "latency_human": return w.Write([]byte(stop.Sub(start).String())) case "bytes_in": b := req.Header.Get(echo.HeaderContentLength) if b == "" { b = "0" } return w.Write([]byte(b)) case "bytes_out": return w.Write([]byte(strconv.FormatInt(res.Size, 10))) default: switch { case strings.HasPrefix(tag, "header:"): return buf.Write([]byte(c.Request().Header.Get(tag[7:]))) case strings.HasPrefix(tag, "query:"): return buf.Write([]byte(c.QueryParam(tag[6:]))) case strings.HasPrefix(tag, "form:"): return buf.Write([]byte(c.FormValue(tag[5:]))) } } return 0, nil }) if err == nil { config.Output.Write(buf.Bytes()) } return } } }
// LoggerFromConfig returns a logger middleware from config. // See `Logger()`. func LoggerFromConfig(config LoggerConfig) echo.MiddlewareFunc { config.template = fasttemplate.New(config.Format, "${", "}") config.color = color.New() if w, ok := config.Output.(*os.File); ok && !isatty.IsTerminal(w.Fd()) { config.color.Disable() } return func(next echo.Handler) echo.Handler { return echo.HandlerFunc(func(c echo.Context) (err error) { rq := c.Request() rs := c.Response() start := time.Now() if err = next.Handle(c); err != nil { c.Error(err) } stop := time.Now() _, err = config.template.ExecuteFunc(config.Output, func(w io.Writer, tag string) (int, error) { switch tag { case "time_rfc3339": return w.Write([]byte(time.Now().Format(time.RFC3339))) case "remote_ip": ra := rq.RemoteAddress() if ip := rq.Header().Get(echo.XRealIP); ip != "" { ra = ip } else if ip = rq.Header().Get(echo.XForwardedFor); ip != "" { ra = ip } else { ra, _, _ = net.SplitHostPort(ra) } return w.Write([]byte(ra)) case "uri": return w.Write([]byte(rq.URI())) case "method": return w.Write([]byte(rq.Method())) case "path": p := rq.URL().Path() if p == "" { p = "/" } return w.Write([]byte(p)) case "status": n := rs.Status() s := color.Green(n) switch { case n >= 500: s = color.Red(n) case n >= 400: s = color.Yellow(n) case n >= 300: s = color.Cyan(n) } return w.Write([]byte(s)) case "response_time": return w.Write([]byte(stop.Sub(start).String())) case "response_size": return w.Write([]byte(strconv.FormatInt(rs.Size(), 10))) default: return w.Write([]byte(fmt.Sprintf("[unknown tag %s]", tag))) } }) return }) } }
template *fasttemplate.Template color *color.Color pool sync.Pool } ) var ( // DefaultLoggerConfig is the default Logger middleware config. DefaultLoggerConfig = LoggerConfig{ Skipper: defaultSkipper, Format: `{"time":"${time_rfc3339}","remote_ip":"${remote_ip}","host":"${host}",` + `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + `"latency_human":"${latency_human}","bytes_in":${bytes_in},` + `"bytes_out":${bytes_out}}` + "\n", Output: os.Stdout, color: color.New(), } ) // Logger returns a middleware that logs HTTP requests. func Logger() echo.MiddlewareFunc { return LoggerWithConfig(DefaultLoggerConfig) } // LoggerWithConfig returns a Logger middleware with config. // See: `Logger()`. func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { // Defaults if config.Skipper == nil { config.Skipper = DefaultLoggerConfig.Skipper }
// LoggerWithConfig returns a logger middleware from config. // See: `Logger()`. func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { // Defaults if config.Format == "" { config.Format = DefaultLoggerConfig.Format } if config.Output == nil { config.Output = DefaultLoggerConfig.Output } config.template = fasttemplate.New(config.Format, "${", "}") config.color = color.New() if w, ok := config.Output.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) { config.color.Disable() } config.bufferPool = sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 256)) }, } return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) (err error) { req := c.Request() res := c.Response() start := time.Now() if err = next(c); err != nil { c.Error(err) } stop := time.Now() buf := config.bufferPool.Get().(*bytes.Buffer) buf.Reset() defer config.bufferPool.Put(buf) _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { switch tag { case "time_rfc3339": return w.Write([]byte(time.Now().Format(time.RFC3339))) case "remote_ip": ra := req.RemoteAddress() if ip := req.Header().Get(echo.HeaderXRealIP); ip != "" { ra = ip } else if ip = req.Header().Get(echo.HeaderXForwardedFor); ip != "" { ra = ip } else { ra, _, _ = net.SplitHostPort(ra) } return w.Write([]byte(ra)) case "host": return w.Write([]byte(req.Host())) case "uri": return w.Write([]byte(req.URI())) case "method": return w.Write([]byte(req.Method())) case "path": p := req.URL().Path() if p == "" { p = "/" } return w.Write([]byte(p)) case "referer": return w.Write([]byte(req.Referer())) case "user_agent": return w.Write([]byte(req.UserAgent())) case "status": n := res.Status() s := config.color.Green(n) switch { case n >= 500: s = config.color.Red(n) case n >= 400: s = config.color.Yellow(n) case n >= 300: s = config.color.Cyan(n) } return w.Write([]byte(s)) case "latency": l := stop.Sub(start).Nanoseconds() / 1000 return w.Write([]byte(strconv.FormatInt(l, 10))) case "latency_human": return w.Write([]byte(stop.Sub(start).String())) case "rx_bytes": b := req.Header().Get(echo.HeaderContentLength) if b == "" { b = "0" } return w.Write([]byte(b)) case "tx_bytes": return w.Write([]byte(strconv.FormatInt(res.Size(), 10))) default: return w.Write([]byte(fmt.Sprintf("[unknown tag %s]", tag))) } }) if err == nil { config.Output.Write(buf.Bytes()) } return } } }