Browse code

use custom marshalling for JSONLog

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)

unclejack authored on 2014/09/16 01:35:51
Showing 2 changed files
... ...
@@ -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
+}