iris/middleware/accesslog/json.go

93 lines
2.1 KiB
Go
Raw Permalink Normal View History

package accesslog
import (
"bytes"
"io"
"strconv"
"strings"
jsoniter "github.com/json-iterator/go"
)
// JSON is a Formatter type for JSON logs.
type JSON struct {
// Indent in spaces.
// Note that, if set to > 0 then jsoniter is used instead of easyjson.
Indent string
EscapeHTML bool
HumanTime bool
jsoniter jsoniter.API
ac *AccessLog
}
// SetOutput creates the json encoder writes to the "dest".
// It's called automatically by the middleware when this Formatter is used.
func (f *JSON) SetOutput(dest io.Writer) {
f.ac, _ = dest.(*AccessLog)
if indentStep := strings.Count(f.Indent, " "); indentStep > 0 {
// Note that: indent setting should always be spaces
// as the jsoniter does not support other chars.
f.jsoniter = jsoniter.Config{
TagKey: "json",
IndentionStep: indentStep,
EscapeHTML: f.EscapeHTML,
SortMapKeys: true,
}.Froze()
}
}
var (
timestampKeyB = []byte(`"timestamp":`)
timestampKeyIndentB = append(timestampKeyB, ' ')
timestampKeyVB = append(timestampKeyB, '0')
timestampIndentKeyVB = append(timestampKeyIndentB, '0')
)
// Format prints the logs in JSON format.
// Writes to the destination directly,
// locks on each Format call.
func (f *JSON) Format(log *Log) (bool, error) {
if f.jsoniter != nil {
if f.HumanTime {
// 1. Don't write the unix timestamp,
// key will be visible though as we don't omit the field.
log.Timestamp = 0
}
b, err := f.jsoniter.Marshal(log)
if err != nil {
return true, err
}
if f.HumanTime {
// 2. Get the time text based on the configuration.
t := log.Now.Format(log.TimeFormat)
// 3. Find the "timestamp:$indent"
// and set it to the text one.
var (
oldT []byte
tsKey []byte
)
if f.Indent != "" {
oldT = timestampIndentKeyVB
tsKey = timestampKeyIndentB
} else {
oldT = timestampKeyVB
tsKey = timestampKeyB
}
newT := append(tsKey, strconv.Quote(t)...)
b = bytes.Replace(b, oldT, newT, 1)
}
f.ac.Write(append(b, newLine))
return true, nil
}
err := f.writeEasyJSON(log)
return true, err
}