github.com/dpiddy/docker@v1.12.2-rc1/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 Timestamp. 14 // Writes are concurrent, so you need implement some sync in your logger 15 type Copier struct { 16 // srcs is map of name -> reader pairs, for example "stdout", "stderr" 17 srcs map[string]io.Reader 18 dst Logger 19 copyJobs sync.WaitGroup 20 closeOnce sync.Once 21 closed chan struct{} 22 } 23 24 // NewCopier creates a new Copier 25 func NewCopier(srcs map[string]io.Reader, dst Logger) *Copier { 26 return &Copier{ 27 srcs: srcs, 28 dst: dst, 29 closed: make(chan struct{}), 30 } 31 } 32 33 // Run starts logs copying 34 func (c *Copier) Run() { 35 for src, w := range c.srcs { 36 c.copyJobs.Add(1) 37 go c.copySrc(src, w) 38 } 39 } 40 41 func (c *Copier) copySrc(name string, src io.Reader) { 42 defer c.copyJobs.Done() 43 reader := bufio.NewReader(src) 44 45 for { 46 select { 47 case <-c.closed: 48 return 49 default: 50 line, err := reader.ReadBytes('\n') 51 line = bytes.TrimSuffix(line, []byte{'\n'}) 52 53 // ReadBytes can return full or partial output even when it failed. 54 // e.g. it can return a full entry and EOF. 55 if err == nil || len(line) > 0 { 56 if logErr := c.dst.Log(&Message{Line: line, Source: name, Timestamp: time.Now().UTC()}); logErr != nil { 57 logrus.Errorf("Failed to log msg %q for logger %s: %s", line, c.dst.Name(), logErr) 58 } 59 } 60 61 if err != nil { 62 if err != io.EOF { 63 logrus.Errorf("Error scanning log stream: %s", err) 64 } 65 return 66 } 67 } 68 } 69 } 70 71 // Wait waits until all copying is done 72 func (c *Copier) Wait() { 73 c.copyJobs.Wait() 74 } 75 76 // Close closes the copier 77 func (c *Copier) Close() { 78 c.closeOnce.Do(func() { 79 close(c.closed) 80 }) 81 }