github.com/mssola/docker@v1.8.1/daemon/logger/copier.go (about) 1 package logger 2 3 import ( 4 "bufio" 5 "bytes" 6 "io" 7 "sync" 8 "time" 9 10 "github.com/Sirupsen/logrus" 11 ) 12 13 // Copier can copy logs from specified sources to Logger and attach 14 // ContainerID and Timestamp. 15 // Writes are concurrent, so you need implement some sync in your logger 16 type Copier struct { 17 // cid is container id for which we copying logs 18 cid string 19 // srcs is map of name -> reader pairs, for example "stdout", "stderr" 20 srcs map[string]io.Reader 21 dst Logger 22 copyJobs sync.WaitGroup 23 } 24 25 // NewCopier creates new Copier 26 func NewCopier(cid string, srcs map[string]io.Reader, dst Logger) (*Copier, error) { 27 return &Copier{ 28 cid: cid, 29 srcs: srcs, 30 dst: dst, 31 }, nil 32 } 33 34 // Run starts logs copying 35 func (c *Copier) Run() { 36 for src, w := range c.srcs { 37 c.copyJobs.Add(1) 38 go c.copySrc(src, w) 39 } 40 } 41 42 func (c *Copier) copySrc(name string, src io.Reader) { 43 defer c.copyJobs.Done() 44 reader := bufio.NewReader(src) 45 46 for { 47 line, err := reader.ReadBytes('\n') 48 line = bytes.TrimSuffix(line, []byte{'\n'}) 49 50 // ReadBytes can return full or partial output even when it failed. 51 // e.g. it can return a full entry and EOF. 52 if err == nil || len(line) > 0 { 53 if logErr := c.dst.Log(&Message{ContainerID: c.cid, Line: line, Source: name, Timestamp: time.Now().UTC()}); logErr != nil { 54 logrus.Errorf("Failed to log msg %q for logger %s: %s", line, c.dst.Name(), logErr) 55 } 56 } 57 58 if err != nil { 59 if err != io.EOF { 60 logrus.Errorf("Error scanning log stream: %s", err) 61 } 62 return 63 } 64 65 } 66 } 67 68 // Wait waits until all copying is done 69 func (c *Copier) Wait() { 70 c.copyJobs.Wait() 71 }