mirror of https://github.com/docker/docs.git
Merge pull request #11930 from unclejack/broadcastwriter_refactor
pkg/broadcastwriter: use []byte to lower alloc
This commit is contained in:
commit
e736f16bbd
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/pkg/jsonlog"
|
"github.com/docker/docker/pkg/jsonlog"
|
||||||
|
"github.com/docker/docker/pkg/timeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BroadcastWriter accumulate multiple io.WriteCloser by stream.
|
// BroadcastWriter accumulate multiple io.WriteCloser by stream.
|
||||||
|
@ -33,7 +34,6 @@ func (w *BroadcastWriter) AddWriter(writer io.WriteCloser, stream string) {
|
||||||
// Write writes bytes to all writers. Failed writers will be evicted during
|
// Write writes bytes to all writers. Failed writers will be evicted during
|
||||||
// this call.
|
// this call.
|
||||||
func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
|
func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
|
||||||
created := time.Now().UTC()
|
|
||||||
w.Lock()
|
w.Lock()
|
||||||
if writers, ok := w.streams[""]; ok {
|
if writers, ok := w.streams[""]; ok {
|
||||||
for sw := range writers {
|
for sw := range writers {
|
||||||
|
@ -42,23 +42,44 @@ func (w *BroadcastWriter) Write(p []byte) (n int, err error) {
|
||||||
delete(writers, sw)
|
delete(writers, sw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(w.streams) == 1 {
|
||||||
|
if w.buf.Len() >= 4096 {
|
||||||
|
w.buf.Reset()
|
||||||
|
} else {
|
||||||
|
w.buf.Write(p)
|
||||||
|
}
|
||||||
|
w.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if w.jsLogBuf == nil {
|
if w.jsLogBuf == nil {
|
||||||
w.jsLogBuf = new(bytes.Buffer)
|
w.jsLogBuf = new(bytes.Buffer)
|
||||||
w.jsLogBuf.Grow(1024)
|
w.jsLogBuf.Grow(1024)
|
||||||
}
|
}
|
||||||
|
var timestamp string
|
||||||
|
created := time.Now().UTC()
|
||||||
w.buf.Write(p)
|
w.buf.Write(p)
|
||||||
for {
|
for {
|
||||||
line, err := w.buf.ReadString('\n')
|
if n := w.buf.Len(); n == 0 {
|
||||||
if err != nil {
|
|
||||||
w.buf.WriteString(line)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
i := bytes.IndexByte(w.buf.Bytes(), '\n')
|
||||||
|
if i < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lineBytes := w.buf.Next(i + 1)
|
||||||
|
if timestamp == "" {
|
||||||
|
timestamp, err = timeutils.FastMarshalJSON(created)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for stream, writers := range w.streams {
|
for stream, writers := range w.streams {
|
||||||
if stream == "" {
|
if stream == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
jsonLog := jsonlog.JSONLog{Log: line, Stream: stream, Created: created}
|
jsonLog := jsonlog.JSONLogBytes{Log: lineBytes, Stream: stream, Created: timestamp}
|
||||||
err = jsonLog.MarshalJSONBuf(w.jsLogBuf)
|
err = jsonLog.MarshalJSONBuf(w.jsLogBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Error making JSON log line: %s", err)
|
logrus.Errorf("Error making JSON log line: %s", err)
|
||||||
|
|
|
@ -142,3 +142,33 @@ func BenchmarkBroadcastWriter(b *testing.B) {
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkBroadcastWriterWithoutStdoutStderr(b *testing.B) {
|
||||||
|
writer := New()
|
||||||
|
setUpWriter := func() {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
writer.AddWriter(devNullCloser(0), "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testLine := "Line that thinks that it is log line from docker"
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
buf.Write([]byte(testLine + "\n"))
|
||||||
|
}
|
||||||
|
// line without eol
|
||||||
|
buf.Write([]byte(testLine))
|
||||||
|
testText := buf.Bytes()
|
||||||
|
b.SetBytes(int64(5 * len(testText)))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
setUpWriter()
|
||||||
|
|
||||||
|
for j := 0; j < 5; j++ {
|
||||||
|
if _, err := writer.Write(testText); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Clean()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
package jsonlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONLogBytes is based on JSONLog.
|
||||||
|
// It allows marshalling JSONLog from Log as []byte
|
||||||
|
// and an already marshalled Created timestamp.
|
||||||
|
type JSONLogBytes struct {
|
||||||
|
Log []byte `json:"log,omitempty"`
|
||||||
|
Stream string `json:"stream,omitempty"`
|
||||||
|
Created string `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSONBuf is based on the same method from JSONLog
|
||||||
|
// It has been modified to take into account the necessary changes.
|
||||||
|
func (mj *JSONLogBytes) MarshalJSONBuf(buf *bytes.Buffer) error {
|
||||||
|
var first = true
|
||||||
|
|
||||||
|
buf.WriteString(`{`)
|
||||||
|
if len(mj.Log) != 0 {
|
||||||
|
if first == true {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
buf.WriteString(`,`)
|
||||||
|
}
|
||||||
|
buf.WriteString(`"log":`)
|
||||||
|
ffjson_WriteJsonBytesAsString(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":`)
|
||||||
|
buf.WriteString(mj.Created)
|
||||||
|
buf.WriteString(`}`)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is based on ffjson_WriteJsonString. It has been changed
|
||||||
|
// to accept a string passed as a slice of bytes.
|
||||||
|
func ffjson_WriteJsonBytesAsString(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('"')
|
||||||
|
}
|
Loading…
Reference in New Issue