소스 검색

use custom marshalling for JSONLog

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
unclejack 10 년 전
부모
커밋
9851e8c4c1
2개의 변경된 파일189개의 추가작업 그리고 5개의 파일을 삭제
  1. 13 5
      pkg/broadcastwriter/broadcastwriter.go
  2. 176 0
      pkg/jsonlog/jsonlog_marshalling.go

+ 13 - 5
pkg/broadcastwriter/broadcastwriter.go

@@ -2,7 +2,6 @@ package broadcastwriter
 
 import (
 	"bytes"
-	"encoding/json"
 	"io"
 	"sync"
 	"time"
@@ -14,8 +13,9 @@ import (
 // BroadcastWriter accumulate multiple io.WriteCloser by stream.
 type BroadcastWriter struct {
 	sync.Mutex
-	buf     *bytes.Buffer
-	streams map[string](map[io.WriteCloser]struct{})
+	buf      *bytes.Buffer
+	jsLogBuf *bytes.Buffer
+	streams  map[string](map[io.WriteCloser]struct{})
 }
 
 // AddWriter adds new io.WriteCloser for stream.
@@ -43,6 +43,10 @@ func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
 			}
 		}
 	}
+	if w.jsLogBuf == nil {
+		w.jsLogBuf = new(bytes.Buffer)
+		w.jsLogBuf.Grow(1024)
+	}
 	w.buf.Write(p)
 	for {
 		line, err := w.buf.ReadString('\n')
@@ -54,19 +58,23 @@ func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
 			if stream == "" {
 				continue
 			}
-			b, err := json.Marshal(jsonlog.JSONLog{Log: line, Stream: stream, Created: created})
+			jsonLog := jsonlog.JSONLog{Log: line, Stream: stream, Created: created}
+			err = jsonLog.MarshalJSONBuf(w.jsLogBuf)
 			if err != nil {
 				log.Errorf("Error making JSON log line: %s", err)
 				continue
 			}
-			b = append(b, '\n')
+			w.jsLogBuf.WriteByte('\n')
+			b := w.jsLogBuf.Bytes()
 			for sw := range writers {
 				if _, err := sw.Write(b); err != nil {
 					delete(writers, sw)
 				}
 			}
 		}
+		w.jsLogBuf.Reset()
 	}
+	w.jsLogBuf.Reset()
 	w.Unlock()
 	return len(p), nil
 }

+ 176 - 0
pkg/jsonlog/jsonlog_marshalling.go

@@ -0,0 +1,176 @@
+// This code was initially generated by ffjson <https://github.com/pquerna/ffjson>
+// This code was generated via the following steps:
+// $ go get -u github.com/pquerna/ffjson
+// $ make shell BINDDIR=.
+// $ ffjson pkg/jsonlog/jsonlog.go
+// $ mv pkg/jsonglog/jsonlog_ffjson.go pkg/jsonlog/jsonlog_marshalling.go
+//
+// It has been modified to improve the performance of time marshalling to JSON
+// and to clean it up.
+// Should this code need to be regenerated when the JSONLog struct is changed,
+// the relevant changes which have been made are:
+// import (
+//        "bytes"
+//-
+//        "unicode/utf8"
+//+
+//+       "github.com/docker/docker/pkg/timeutils"
+// )
+//
+// func (mj *JSONLog) MarshalJSON() ([]byte, error) {
+//@@ -20,13 +16,13 @@ func (mj *JSONLog) MarshalJSON() ([]byte, error) {
+//        }
+//        return buf.Bytes(), nil
+// }
+//+
+// func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
+//-       var err error
+//-       var obj []byte
+//-       var first bool = true
+//-       _ = obj
+//-       _ = err
+//-       _ = first
+//+       var (
+//+               err       error
+//+               timestamp string
+//+               first     bool = true
+//+       )
+//        buf.WriteString(`{`)
+//        if len(mj.Log) != 0 {
+//                if first == true {
+//@@ -52,11 +48,11 @@ func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
+//                buf.WriteString(`,`)
+//        }
+//        buf.WriteString(`"time":`)
+//-       obj, err = mj.Created.MarshalJSON()
+//+       timestamp, err = timeutils.FastMarshalJSON(mj.Created)
+//        if err != nil {
+//                return err
+//        }
+//-       buf.Write(obj)
+//+       buf.WriteString(timestamp)
+//        buf.WriteString(`}`)
+//        return nil
+// }
+
+package jsonlog
+
+import (
+	"bytes"
+	"unicode/utf8"
+
+	"github.com/docker/docker/pkg/timeutils"
+)
+
+func (mj *JSONLog) MarshalJSON() ([]byte, error) {
+	var buf bytes.Buffer
+	buf.Grow(1024)
+	err := mj.MarshalJSONBuf(&buf)
+	if err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), nil
+}
+
+func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
+	var (
+		err       error
+		timestamp string
+		first     bool = true
+	)
+	buf.WriteString(`{`)
+	if len(mj.Log) != 0 {
+		if first == true {
+			first = false
+		} else {
+			buf.WriteString(`,`)
+		}
+		buf.WriteString(`"log":`)
+		ffjson_WriteJsonString(buf, mj.Log)
+	}
+	if len(mj.Stream) != 0 {
+		if first == true {
+			first = false
+		} else {
+			buf.WriteString(`,`)
+		}
+		buf.WriteString(`"stream":`)
+		ffjson_WriteJsonString(buf, mj.Stream)
+	}
+	if first == true {
+		first = false
+	} else {
+		buf.WriteString(`,`)
+	}
+	buf.WriteString(`"time":`)
+	timestamp, err = timeutils.FastMarshalJSON(mj.Created)
+	if err != nil {
+		return err
+	}
+	buf.WriteString(timestamp)
+	buf.WriteString(`}`)
+	return nil
+}
+
+func ffjson_WriteJsonString(buf *bytes.Buffer, s string) {
+	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.WriteString(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.DecodeRuneInString(s[i:])
+		if c == utf8.RuneError && size == 1 {
+			if start < i {
+				buf.WriteString(s[start:i])
+			}
+			buf.WriteString(`\ufffd`)
+			i += size
+			start = i
+			continue
+		}
+
+		if c == '\u2028' || c == '\u2029' {
+			if start < i {
+				buf.WriteString(s[start:i])
+			}
+			buf.WriteString(`\u202`)
+			buf.WriteByte(hex[c&0xF])
+			i += size
+			start = i
+			continue
+		}
+		i += size
+	}
+	if start < len(s) {
+		buf.WriteString(s[start:])
+	}
+	buf.WriteByte('"')
+}