diff --git a/_examples/README.md b/_examples/README.md index a3f53a14..63d8aafb 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -71,16 +71,17 @@ * [API Versioning](routing/versioning/main.go) * [Sitemap](routing/sitemap/main.go) * Logging - * [Request Logger](logging/request-logger/main.go) - * [AccessLog: simple example](logging/request-logger/accesslog-simple/main.go) - * [AccessLog: log request & response and more](logging/request-logger/accesslog) - * [AccessLog: custom fields and template](logging/request-logger/accesslog-template/main.go) - * [AccessLog: CSV Format](logging/request-logger/accesslog-csv/main.go) - * [AccessLog: listen to logs and render them](logging/request-logger/accesslog-broker/main.go) - * [Log Requests to a JSON File](logging/request-logger/request-logger-file-json/main.go) * [Application File Logger](logging/file-logger/main.go) * [Application JSON Logger](logging/json-logger/main.go) * [Rollbar](logging/rollbar/main.go) + * AccessLog + * [Log Requests to a JSON File](logging/request-logger/accesslog-simple/main.go) + * [Using Log Rotation and more](logging/request-logger/accesslog) + * [Custom Fields and Template](logging/request-logger/accesslog-template/main.go) + * [Listen and render Logs to a Client](logging/request-logger/accesslog-broker/main.go) + * [The CSV Formatter](logging/request-logger/accesslog-csv/main.go) + * [Create your own Formatter](logging/request-logger/accesslog-formatter/main.go) + * API Documentation * [Yaag](apidoc/yaag/main.go) * [Swagger](https://github.com/iris-contrib/swagger/tree/master/example) diff --git a/_examples/logging/request-logger/accesslog-formatter/main.go b/_examples/logging/request-logger/accesslog-formatter/main.go new file mode 100644 index 00000000..851cee19 --- /dev/null +++ b/_examples/logging/request-logger/accesslog-formatter/main.go @@ -0,0 +1,129 @@ +// Package main shows to create a quite fast custom Log Formatter. +// Note that, this example requires a little more knowledge about Go. +package main + +import ( + "bytes" + "fmt" + "io" + "strconv" + "sync" + "time" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/middleware/accesslog" + "github.com/kataras/iris/v12/middleware/requestid" +) + +func logFields(ctx iris.Context, fields *accesslog.Fields) { + fields.Set("reqid", ctx.GetID()) +} + +func main() { + app := iris.New() + + ac := accesslog.File("./access.log"). + AddFields(logFields). + SetFormatter(newCustomFormatter(' ', "-\t\t\t\t\t")) + ac.RequestBody = false + ac.BytesReceivedBody = false + ac.BytesSentBody = false + defer ac.Close() + + app.UseRouter(ac.Handler) + app.UseRouter(requestid.New()) + + app.OnErrorCode(iris.StatusNotFound, notFound) + app.Get("/", index) + + app.Listen(":8080") +} + +func notFound(ctx iris.Context) { + ctx.WriteString("The page you're looking for does not exist!") +} + +func index(ctx iris.Context) { + ctx.WriteString("OK Index") +} + +type customFormatter struct { + w io.Writer + bufPool *sync.Pool + + delim byte + blank string +} + +var _ accesslog.Formatter = (*customFormatter)(nil) + +func newCustomFormatter(delim byte, blank string) *customFormatter { + return &customFormatter{delim: delim, blank: blank} +} + +func (f *customFormatter) SetOutput(dest io.Writer) { + f.w = dest + f.bufPool = &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + } + + if f.delim == 0 { + f.delim = ' ' + } +} + +const newLine = '\n' + +func (f *customFormatter) Format(log *accesslog.Log) (bool, error) { + buf := f.bufPool.Get().(*bytes.Buffer) + + buf.WriteString(log.Now.Format(log.TimeFormat)) + buf.WriteByte(f.delim) + + reqid := log.Fields.GetString("reqid") + f.writeTextOrBlank(buf, reqid) + + buf.WriteString(uniformDuration(log.Latency)) + buf.WriteByte(f.delim) + + buf.WriteString(log.IP) + buf.WriteByte(f.delim) + + buf.WriteString(strconv.Itoa(log.Code)) + buf.WriteByte(f.delim) + + buf.WriteString(log.Method) + buf.WriteByte(f.delim) + + buf.WriteString(log.Path) + + buf.WriteByte(newLine) + + // _, err := buf.WriteTo(f.w) + // or (to make sure that it resets on errors too): + _, err := f.w.Write(buf.Bytes()) + buf.Reset() + f.bufPool.Put(buf) + + return true, err +} + +func (f *customFormatter) writeTextOrBlank(buf *bytes.Buffer, s string) { + if len(s) == 0 { + if len(f.blank) == 0 { + return + } + + buf.WriteString(f.blank) + } else { + buf.WriteString(s) + } + + buf.WriteByte(f.delim) +} + +func uniformDuration(t time.Duration) string { + return fmt.Sprintf("%*s", 12, t.String()) +} diff --git a/middleware/accesslog/accesslog.go b/middleware/accesslog/accesslog.go index b1ffd51a..31dbcb1d 100644 --- a/middleware/accesslog/accesslog.go +++ b/middleware/accesslog/accesslog.go @@ -631,7 +631,7 @@ func (ac *AccessLog) after(ctx *context.Context, lat time.Duration, method, path requestData, err := ctx.GetBody() requestBodyLength := len(requestData) if ac.BytesReceivedBody { - bytesReceived = requestBodyLength // store it, if the total is enabled then this will be overriden. + bytesReceived = requestBodyLength // store it, if the total is enabled then this will be overridden. } if err != nil && ac.RequestBody { requestBody = ac.getErrorText(err)