mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Use sync.Pool for logger Messages
This reduces allocs and bytes used per log entry significantly as well as some improvement to time per log operation. Each log driver, however, must put messages back in the pool once they are finished with the message. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
054abff3b6
commit
3f4fccb65f
14 changed files with 74 additions and 25 deletions
|
@ -203,7 +203,6 @@ func (l *logStream) Log(msg *logger.Message) error {
|
|||
l.lock.RLock()
|
||||
defer l.lock.RUnlock()
|
||||
if !l.closed {
|
||||
// buffer up the data, making sure to copy the Line data
|
||||
l.messages <- msg
|
||||
}
|
||||
return nil
|
||||
|
@ -347,6 +346,7 @@ func (l *logStream) collectBatch() {
|
|||
})
|
||||
bytes += (lineBytes + perEventBytes)
|
||||
}
|
||||
logger.PutMessage(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,15 +76,14 @@ func (c *Copier) copySrc(name string, src io.Reader) {
|
|||
}
|
||||
// Break up the data that we've buffered up into lines, and log each in turn.
|
||||
p := 0
|
||||
for q := bytes.Index(buf[p:n], []byte{'\n'}); q >= 0; q = bytes.Index(buf[p:n], []byte{'\n'}) {
|
||||
for q := bytes.IndexByte(buf[p:n], '\n'); q >= 0; q = bytes.IndexByte(buf[p:n], '\n') {
|
||||
select {
|
||||
case <-c.closed:
|
||||
return
|
||||
default:
|
||||
msg := &Message{
|
||||
Source: name,
|
||||
Timestamp: time.Now().UTC(),
|
||||
}
|
||||
msg := NewMessage()
|
||||
msg.Source = name
|
||||
msg.Timestamp = time.Now().UTC()
|
||||
msg.Line = append(msg.Line, buf[p:p+q]...)
|
||||
|
||||
if logErr := c.dst.Log(msg); logErr != nil {
|
||||
|
@ -98,11 +97,9 @@ func (c *Copier) copySrc(name string, src io.Reader) {
|
|||
// noting that it's a partial log line.
|
||||
if eof || (p == 0 && n == len(buf)) {
|
||||
if p < n {
|
||||
msg := &Message{
|
||||
Source: name,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Partial: true,
|
||||
}
|
||||
msg := NewMessage()
|
||||
msg.Source = name
|
||||
msg.Timestamp = time.Now().UTC()
|
||||
msg.Line = append(msg.Line, buf[p:n]...)
|
||||
msg.Partial = true
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ func TestCopierSlow(t *testing.T) {
|
|||
type BenchmarkLoggerDummy struct {
|
||||
}
|
||||
|
||||
func (l *BenchmarkLoggerDummy) Log(m *Message) error { return nil }
|
||||
func (l *BenchmarkLoggerDummy) Log(m *Message) error { PutMessage(m); return nil }
|
||||
|
||||
func (l *BenchmarkLoggerDummy) Close() error { return nil }
|
||||
|
||||
|
|
|
@ -76,7 +76,9 @@ func (etwLogger *etwLogs) Log(msg *logger.Message) error {
|
|||
logrus.Error(errorMessage)
|
||||
return errors.New(errorMessage)
|
||||
}
|
||||
return callEventWriteString(createLogMessage(etwLogger, msg))
|
||||
m := createLogMessage(etwLogger, msg)
|
||||
logger.PutMessage(msg)
|
||||
return callEventWriteString(m)
|
||||
}
|
||||
|
||||
// Close closes the logger by unregistering the ETW provider.
|
||||
|
|
|
@ -151,9 +151,12 @@ func (f *fluentd) Log(msg *logger.Message) error {
|
|||
for k, v := range f.extra {
|
||||
data[k] = v
|
||||
}
|
||||
|
||||
ts := msg.Timestamp
|
||||
logger.PutMessage(msg)
|
||||
// fluent-logger-golang buffers logs from failures and disconnections,
|
||||
// and these are transferred again automatically.
|
||||
return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
|
||||
return f.writer.PostWithTime(f.tag, ts, data)
|
||||
}
|
||||
|
||||
func (f *fluentd) Close() error {
|
||||
|
|
|
@ -194,12 +194,16 @@ func ValidateLogOpts(cfg map[string]string) error {
|
|||
}
|
||||
|
||||
func (l *gcplogs) Log(m *logger.Message) error {
|
||||
data := string(m.Line)
|
||||
ts := m.Timestamp
|
||||
logger.PutMessage(m)
|
||||
|
||||
l.logger.Log(logging.Entry{
|
||||
Timestamp: m.Timestamp,
|
||||
Timestamp: ts,
|
||||
Payload: &dockerLogEntry{
|
||||
Instance: l.instance,
|
||||
Container: l.container,
|
||||
Data: string(m.Line),
|
||||
Data: data,
|
||||
},
|
||||
})
|
||||
return nil
|
||||
|
|
|
@ -133,6 +133,7 @@ func (s *gelfLogger) Log(msg *logger.Message) error {
|
|||
Level: level,
|
||||
RawExtra: s.rawExtra,
|
||||
}
|
||||
logger.PutMessage(msg)
|
||||
|
||||
if err := s.writer.WriteMessage(&m); err != nil {
|
||||
return fmt.Errorf("gelf: cannot send GELF message: %v", err)
|
||||
|
|
|
@ -105,10 +105,14 @@ func (s *journald) Log(msg *logger.Message) error {
|
|||
if msg.Partial {
|
||||
vars["CONTAINER_PARTIAL_MESSAGE"] = "true"
|
||||
}
|
||||
|
||||
line := string(msg.Line)
|
||||
logger.PutMessage(msg)
|
||||
|
||||
if msg.Source == "stderr" {
|
||||
return journal.Send(string(msg.Line), journal.PriErr, vars)
|
||||
return journal.Send(line, journal.PriErr, vars)
|
||||
}
|
||||
return journal.Send(string(msg.Line), journal.PriInfo, vars)
|
||||
return journal.Send(line, journal.PriInfo, vars)
|
||||
}
|
||||
|
||||
func (s *journald) Name() string {
|
||||
|
|
|
@ -100,6 +100,7 @@ func (l *JSONFileLogger) Log(msg *logger.Message) error {
|
|||
Created: timestamp,
|
||||
RawAttrs: l.extra,
|
||||
}).MarshalJSONBuf(l.buf)
|
||||
logger.PutMessage(msg)
|
||||
if err != nil {
|
||||
l.mu.Unlock()
|
||||
return err
|
||||
|
|
|
@ -61,7 +61,9 @@ func (f *logentries) Log(msg *logger.Message) error {
|
|||
for k, v := range f.extra {
|
||||
data[k] = v
|
||||
}
|
||||
f.writer.Println(f.tag, msg.Timestamp, data)
|
||||
ts := msg.Timestamp
|
||||
logger.PutMessage(msg)
|
||||
f.writer.Println(f.tag, ts, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,24 @@ const (
|
|||
logWatcherBufferSize = 4096
|
||||
)
|
||||
|
||||
var messagePool = &sync.Pool{New: func() interface{} { return &Message{Line: make([]byte, 0, 256)} }}
|
||||
|
||||
// NewMessage returns a new message from the message sync.Pool
|
||||
func NewMessage() *Message {
|
||||
return messagePool.Get().(*Message)
|
||||
}
|
||||
|
||||
// PutMessage puts the specified message back n the message pool.
|
||||
// The message fields are reset before putting into the pool.
|
||||
func PutMessage(msg *Message) {
|
||||
msg.reset()
|
||||
messagePool.Put(msg)
|
||||
}
|
||||
|
||||
// Message is datastructure that represents piece of output produced by some
|
||||
// container. The Line member is a slice of an array whose contents can be
|
||||
// changed after a log driver's Log() method returns.
|
||||
// Any changes made to this struct must also be updated in the `reset` function
|
||||
type Message struct {
|
||||
Line []byte
|
||||
Source string
|
||||
|
@ -37,6 +52,16 @@ type Message struct {
|
|||
Partial bool
|
||||
}
|
||||
|
||||
// reset sets the message back to default values
|
||||
// This is used when putting a message back into the message pool.
|
||||
// Any changes to the `Message` struct should be reflected here.
|
||||
func (m *Message) reset() {
|
||||
m.Line = m.Line[:0]
|
||||
m.Source = ""
|
||||
m.Attrs = nil
|
||||
m.Partial = false
|
||||
}
|
||||
|
||||
// LogAttributes is used to hold the extra attributes available in the log message
|
||||
// Primarily used for converting the map type to string and sorting.
|
||||
type LogAttributes map[string]string
|
||||
|
|
|
@ -83,10 +83,18 @@ func (r *RingLogger) Close() error {
|
|||
r.setClosed()
|
||||
r.buffer.Close()
|
||||
// empty out the queue
|
||||
var logErr bool
|
||||
for _, msg := range r.buffer.Drain() {
|
||||
if logErr {
|
||||
// some error logging a previous message, so re-insert to message pool
|
||||
// and assume log driver is hosed
|
||||
PutMessage(msg)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := r.l.Log(msg); err != nil {
|
||||
logrus.WithField("driver", r.l.Name()).WithField("container", r.logInfo.ContainerID).Errorf("Error writing log message: %v", r.l)
|
||||
break
|
||||
logErr = true
|
||||
}
|
||||
}
|
||||
return r.l.Close()
|
||||
|
|
|
@ -336,7 +336,7 @@ func (l *splunkLoggerInline) Log(msg *logger.Message) error {
|
|||
event.Source = msg.Source
|
||||
|
||||
message.Event = &event
|
||||
|
||||
logger.PutMessage(msg)
|
||||
return l.queueMessageAsync(message)
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ func (l *splunkLoggerJSON) Log(msg *logger.Message) error {
|
|||
event.Source = msg.Source
|
||||
|
||||
message.Event = &event
|
||||
|
||||
logger.PutMessage(msg)
|
||||
return l.queueMessageAsync(message)
|
||||
}
|
||||
|
||||
|
@ -362,7 +362,7 @@ func (l *splunkLoggerRaw) Log(msg *logger.Message) error {
|
|||
message := l.createSplunkMessage(msg)
|
||||
|
||||
message.Event = string(append(l.prefix, msg.Line...))
|
||||
|
||||
logger.PutMessage(msg)
|
||||
return l.queueMessageAsync(message)
|
||||
}
|
||||
|
||||
|
|
|
@ -132,10 +132,12 @@ func New(info logger.Info) (logger.Logger, error) {
|
|||
}
|
||||
|
||||
func (s *syslogger) Log(msg *logger.Message) error {
|
||||
line := string(msg.Line)
|
||||
logger.PutMessage(msg)
|
||||
if msg.Source == "stderr" {
|
||||
return s.writer.Err(string(msg.Line))
|
||||
return s.writer.Err(line)
|
||||
}
|
||||
return s.writer.Info(string(msg.Line))
|
||||
return s.writer.Info(line)
|
||||
}
|
||||
|
||||
func (s *syslogger) Close() error {
|
||||
|
|
Loading…
Reference in a new issue