| package jsonlog // import "github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "time" | |
| "unicode/utf8" | |
| ) | |
| // JSONLogs marshals encoded JSONLog objects | |
| type JSONLogs struct { | |
| Log []byte `json:"log,omitempty"` | |
| Stream string `json:"stream,omitempty"` | |
| Created time.Time `json:"time"` | |
| // json-encoded bytes | |
| RawAttrs json.RawMessage `json:"attrs,omitempty"` | |
| } | |
| // MarshalJSONBuf is an optimized JSON marshaller that avoids reflection | |
| // and unnecessary allocation. | |
| func (mj *JSONLogs) MarshalJSONBuf(buf *bytes.Buffer) error { | |
| first := true | |
| buf.WriteString(`{`) | |
| if len(mj.Log) != 0 { | |
| first = false | |
| buf.WriteString(`"log":`) | |
| ffjsonWriteJSONBytesAsString(buf, mj.Log) | |
| } | |
| if len(mj.Stream) != 0 { | |
| if first { | |
| first = false | |
| } else { | |
| buf.WriteString(`,`) | |
| } | |
| buf.WriteString(`"stream":`) | |
| ffjsonWriteJSONBytesAsString(buf, []byte(mj.Stream)) | |
| } | |
| if len(mj.RawAttrs) > 0 { | |
| if first { | |
| first = false | |
| } else { | |
| buf.WriteString(`,`) | |
| } | |
| buf.WriteString(`"attrs":`) | |
| buf.Write(mj.RawAttrs) | |
| } | |
| if !first { | |
| buf.WriteString(`,`) | |
| } | |
| created, err := fastTimeMarshalJSON(mj.Created) | |
| if err != nil { | |
| return err | |
| } | |
| buf.WriteString(`"time":`) | |
| buf.WriteString(created) | |
| buf.WriteString(`}`) | |
| return nil | |
| } | |
| func ffjsonWriteJSONBytesAsString(buf *bytes.Buffer, s []byte) { | |
| const hex = "0123456789abcdef" | |
| buf.WriteByte('"') | |
| start := 0 | |
| for i := 0; i < len(s); { | |
| if b := s[i]; b < utf8.RuneSelf { | |
| if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { | |
| i++ | |
| continue | |
| } | |
| if start < i { | |
| buf.Write(s[start:i]) | |
| } | |
| switch b { | |
| case '\\', '"': | |
| buf.WriteByte('\\') | |
| buf.WriteByte(b) | |
| case '\n': | |
| buf.WriteByte('\\') | |
| buf.WriteByte('n') | |
| case '\r': | |
| buf.WriteByte('\\') | |
| buf.WriteByte('r') | |
| default: | |
| buf.WriteString(`\u00`) | |
| buf.WriteByte(hex[b>>4]) | |
| buf.WriteByte(hex[b&0xF]) | |
| } | |
| i++ | |
| start = i | |
| continue | |
| } | |
| c, size := utf8.DecodeRune(s[i:]) | |
| if c == utf8.RuneError && size == 1 { | |
| if start < i { | |
| buf.Write(s[start:i]) | |
| } | |
| buf.WriteString(`\ufffd`) | |
| i += size | |
| start = i | |
| continue | |
| } | |
| if c == '\u2028' || c == '\u2029' { | |
| if start < i { | |
| buf.Write(s[start:i]) | |
| } | |
| buf.WriteString(`\u202`) | |
| buf.WriteByte(hex[c&0xF]) | |
| i += size | |
| start = i | |
| continue | |
| } | |
| i += size | |
| } | |
| if start < len(s) { | |
| buf.Write(s[start:]) | |
| } | |
| buf.WriteByte('"') | |
| } |