Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
| ... | ... |
@@ -2,7 +2,6 @@ package broadcastwriter |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
- "encoding/json" |
|
| 6 | 5 |
"io" |
| 7 | 6 |
"sync" |
| 8 | 7 |
"time" |
| ... | ... |
@@ -14,8 +13,9 @@ import ( |
| 14 | 14 |
// BroadcastWriter accumulate multiple io.WriteCloser by stream. |
| 15 | 15 |
type BroadcastWriter struct {
|
| 16 | 16 |
sync.Mutex |
| 17 |
- buf *bytes.Buffer |
|
| 18 |
- streams map[string](map[io.WriteCloser]struct{})
|
|
| 17 |
+ buf *bytes.Buffer |
|
| 18 |
+ jsLogBuf *bytes.Buffer |
|
| 19 |
+ streams map[string](map[io.WriteCloser]struct{})
|
|
| 19 | 20 |
} |
| 20 | 21 |
|
| 21 | 22 |
// AddWriter adds new io.WriteCloser for stream. |
| ... | ... |
@@ -43,6 +43,10 @@ func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
|
| 43 | 43 |
} |
| 44 | 44 |
} |
| 45 | 45 |
} |
| 46 |
+ if w.jsLogBuf == nil {
|
|
| 47 |
+ w.jsLogBuf = new(bytes.Buffer) |
|
| 48 |
+ w.jsLogBuf.Grow(1024) |
|
| 49 |
+ } |
|
| 46 | 50 |
w.buf.Write(p) |
| 47 | 51 |
for {
|
| 48 | 52 |
line, err := w.buf.ReadString('\n')
|
| ... | ... |
@@ -54,19 +58,23 @@ func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
|
| 54 | 54 |
if stream == "" {
|
| 55 | 55 |
continue |
| 56 | 56 |
} |
| 57 |
- b, err := json.Marshal(jsonlog.JSONLog{Log: line, Stream: stream, Created: created})
|
|
| 57 |
+ jsonLog := jsonlog.JSONLog{Log: line, Stream: stream, Created: created}
|
|
| 58 |
+ err = jsonLog.MarshalJSONBuf(w.jsLogBuf) |
|
| 58 | 59 |
if err != nil {
|
| 59 | 60 |
log.Errorf("Error making JSON log line: %s", err)
|
| 60 | 61 |
continue |
| 61 | 62 |
} |
| 62 |
- b = append(b, '\n') |
|
| 63 |
+ w.jsLogBuf.WriteByte('\n')
|
|
| 64 |
+ b := w.jsLogBuf.Bytes() |
|
| 63 | 65 |
for sw := range writers {
|
| 64 | 66 |
if _, err := sw.Write(b); err != nil {
|
| 65 | 67 |
delete(writers, sw) |
| 66 | 68 |
} |
| 67 | 69 |
} |
| 68 | 70 |
} |
| 71 |
+ w.jsLogBuf.Reset() |
|
| 69 | 72 |
} |
| 73 |
+ w.jsLogBuf.Reset() |
|
| 70 | 74 |
w.Unlock() |
| 71 | 75 |
return len(p), nil |
| 72 | 76 |
} |
| 73 | 77 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,176 @@ |
| 0 |
+// This code was initially generated by ffjson <https://github.com/pquerna/ffjson> |
|
| 1 |
+// This code was generated via the following steps: |
|
| 2 |
+// $ go get -u github.com/pquerna/ffjson |
|
| 3 |
+// $ make shell BINDDIR=. |
|
| 4 |
+// $ ffjson pkg/jsonlog/jsonlog.go |
|
| 5 |
+// $ mv pkg/jsonglog/jsonlog_ffjson.go pkg/jsonlog/jsonlog_marshalling.go |
|
| 6 |
+// |
|
| 7 |
+// It has been modified to improve the performance of time marshalling to JSON |
|
| 8 |
+// and to clean it up. |
|
| 9 |
+// Should this code need to be regenerated when the JSONLog struct is changed, |
|
| 10 |
+// the relevant changes which have been made are: |
|
| 11 |
+// import ( |
|
| 12 |
+// "bytes" |
|
| 13 |
+//- |
|
| 14 |
+// "unicode/utf8" |
|
| 15 |
+//+ |
|
| 16 |
+//+ "github.com/docker/docker/pkg/timeutils" |
|
| 17 |
+// ) |
|
| 18 |
+// |
|
| 19 |
+// func (mj *JSONLog) MarshalJSON() ([]byte, error) {
|
|
| 20 |
+//@@ -20,13 +16,13 @@ func (mj *JSONLog) MarshalJSON() ([]byte, error) {
|
|
| 21 |
+// } |
|
| 22 |
+// return buf.Bytes(), nil |
|
| 23 |
+// } |
|
| 24 |
+//+ |
|
| 25 |
+// func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
|
|
| 26 |
+//- var err error |
|
| 27 |
+//- var obj []byte |
|
| 28 |
+//- var first bool = true |
|
| 29 |
+//- _ = obj |
|
| 30 |
+//- _ = err |
|
| 31 |
+//- _ = first |
|
| 32 |
+//+ var ( |
|
| 33 |
+//+ err error |
|
| 34 |
+//+ timestamp string |
|
| 35 |
+//+ first bool = true |
|
| 36 |
+//+ ) |
|
| 37 |
+// buf.WriteString(`{`)
|
|
| 38 |
+// if len(mj.Log) != 0 {
|
|
| 39 |
+// if first == true {
|
|
| 40 |
+//@@ -52,11 +48,11 @@ func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
|
|
| 41 |
+// buf.WriteString(`,`) |
|
| 42 |
+// } |
|
| 43 |
+// buf.WriteString(`"time":`) |
|
| 44 |
+//- obj, err = mj.Created.MarshalJSON() |
|
| 45 |
+//+ timestamp, err = timeutils.FastMarshalJSON(mj.Created) |
|
| 46 |
+// if err != nil {
|
|
| 47 |
+// return err |
|
| 48 |
+// } |
|
| 49 |
+//- buf.Write(obj) |
|
| 50 |
+//+ buf.WriteString(timestamp) |
|
| 51 |
+// buf.WriteString(`}`) |
|
| 52 |
+// return nil |
|
| 53 |
+// } |
|
| 54 |
+ |
|
| 55 |
+package jsonlog |
|
| 56 |
+ |
|
| 57 |
+import ( |
|
| 58 |
+ "bytes" |
|
| 59 |
+ "unicode/utf8" |
|
| 60 |
+ |
|
| 61 |
+ "github.com/docker/docker/pkg/timeutils" |
|
| 62 |
+) |
|
| 63 |
+ |
|
| 64 |
+func (mj *JSONLog) MarshalJSON() ([]byte, error) {
|
|
| 65 |
+ var buf bytes.Buffer |
|
| 66 |
+ buf.Grow(1024) |
|
| 67 |
+ err := mj.MarshalJSONBuf(&buf) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ return nil, err |
|
| 70 |
+ } |
|
| 71 |
+ return buf.Bytes(), nil |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
|
|
| 75 |
+ var ( |
|
| 76 |
+ err error |
|
| 77 |
+ timestamp string |
|
| 78 |
+ first bool = true |
|
| 79 |
+ ) |
|
| 80 |
+ buf.WriteString(`{`)
|
|
| 81 |
+ if len(mj.Log) != 0 {
|
|
| 82 |
+ if first == true {
|
|
| 83 |
+ first = false |
|
| 84 |
+ } else {
|
|
| 85 |
+ buf.WriteString(`,`) |
|
| 86 |
+ } |
|
| 87 |
+ buf.WriteString(`"log":`) |
|
| 88 |
+ ffjson_WriteJsonString(buf, mj.Log) |
|
| 89 |
+ } |
|
| 90 |
+ if len(mj.Stream) != 0 {
|
|
| 91 |
+ if first == true {
|
|
| 92 |
+ first = false |
|
| 93 |
+ } else {
|
|
| 94 |
+ buf.WriteString(`,`) |
|
| 95 |
+ } |
|
| 96 |
+ buf.WriteString(`"stream":`) |
|
| 97 |
+ ffjson_WriteJsonString(buf, mj.Stream) |
|
| 98 |
+ } |
|
| 99 |
+ if first == true {
|
|
| 100 |
+ first = false |
|
| 101 |
+ } else {
|
|
| 102 |
+ buf.WriteString(`,`) |
|
| 103 |
+ } |
|
| 104 |
+ buf.WriteString(`"time":`) |
|
| 105 |
+ timestamp, err = timeutils.FastMarshalJSON(mj.Created) |
|
| 106 |
+ if err != nil {
|
|
| 107 |
+ return err |
|
| 108 |
+ } |
|
| 109 |
+ buf.WriteString(timestamp) |
|
| 110 |
+ buf.WriteString(`}`) |
|
| 111 |
+ return nil |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func ffjson_WriteJsonString(buf *bytes.Buffer, s string) {
|
|
| 115 |
+ const hex = "0123456789abcdef" |
|
| 116 |
+ |
|
| 117 |
+ buf.WriteByte('"')
|
|
| 118 |
+ start := 0 |
|
| 119 |
+ for i := 0; i < len(s); {
|
|
| 120 |
+ if b := s[i]; b < utf8.RuneSelf {
|
|
| 121 |
+ if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
|
|
| 122 |
+ i++ |
|
| 123 |
+ continue |
|
| 124 |
+ } |
|
| 125 |
+ if start < i {
|
|
| 126 |
+ buf.WriteString(s[start:i]) |
|
| 127 |
+ } |
|
| 128 |
+ switch b {
|
|
| 129 |
+ case '\\', '"': |
|
| 130 |
+ buf.WriteByte('\\')
|
|
| 131 |
+ buf.WriteByte(b) |
|
| 132 |
+ case '\n': |
|
| 133 |
+ buf.WriteByte('\\')
|
|
| 134 |
+ buf.WriteByte('n')
|
|
| 135 |
+ case '\r': |
|
| 136 |
+ buf.WriteByte('\\')
|
|
| 137 |
+ buf.WriteByte('r')
|
|
| 138 |
+ default: |
|
| 139 |
+ |
|
| 140 |
+ buf.WriteString(`\u00`) |
|
| 141 |
+ buf.WriteByte(hex[b>>4]) |
|
| 142 |
+ buf.WriteByte(hex[b&0xF]) |
|
| 143 |
+ } |
|
| 144 |
+ i++ |
|
| 145 |
+ start = i |
|
| 146 |
+ continue |
|
| 147 |
+ } |
|
| 148 |
+ c, size := utf8.DecodeRuneInString(s[i:]) |
|
| 149 |
+ if c == utf8.RuneError && size == 1 {
|
|
| 150 |
+ if start < i {
|
|
| 151 |
+ buf.WriteString(s[start:i]) |
|
| 152 |
+ } |
|
| 153 |
+ buf.WriteString(`\ufffd`) |
|
| 154 |
+ i += size |
|
| 155 |
+ start = i |
|
| 156 |
+ continue |
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ if c == '\u2028' || c == '\u2029' {
|
|
| 160 |
+ if start < i {
|
|
| 161 |
+ buf.WriteString(s[start:i]) |
|
| 162 |
+ } |
|
| 163 |
+ buf.WriteString(`\u202`) |
|
| 164 |
+ buf.WriteByte(hex[c&0xF]) |
|
| 165 |
+ i += size |
|
| 166 |
+ start = i |
|
| 167 |
+ continue |
|
| 168 |
+ } |
|
| 169 |
+ i += size |
|
| 170 |
+ } |
|
| 171 |
+ if start < len(s) {
|
|
| 172 |
+ buf.WriteString(s[start:]) |
|
| 173 |
+ } |
|
| 174 |
+ buf.WriteByte('"')
|
|
| 175 |
+} |