|
@@ -1,7 +1,6 @@
|
|
package logger
|
|
package logger
|
|
|
|
|
|
import (
|
|
import (
|
|
- "bufio"
|
|
|
|
"bytes"
|
|
"bytes"
|
|
"io"
|
|
"io"
|
|
"sync"
|
|
"sync"
|
|
@@ -10,8 +9,13 @@ import (
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/Sirupsen/logrus"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+const (
|
|
|
|
+ bufSize = 16 * 1024
|
|
|
|
+ readSize = 2 * 1024
|
|
|
|
+)
|
|
|
|
+
|
|
// Copier can copy logs from specified sources to Logger and attach Timestamp.
|
|
// Copier can copy logs from specified sources to Logger and attach Timestamp.
|
|
-// Writes are concurrent, so you need implement some sync in your logger
|
|
|
|
|
|
+// Writes are concurrent, so you need implement some sync in your logger.
|
|
type Copier struct {
|
|
type Copier struct {
|
|
// srcs is map of name -> reader pairs, for example "stdout", "stderr"
|
|
// srcs is map of name -> reader pairs, for example "stdout", "stderr"
|
|
srcs map[string]io.Reader
|
|
srcs map[string]io.Reader
|
|
@@ -39,30 +43,76 @@ func (c *Copier) Run() {
|
|
|
|
|
|
func (c *Copier) copySrc(name string, src io.Reader) {
|
|
func (c *Copier) copySrc(name string, src io.Reader) {
|
|
defer c.copyJobs.Done()
|
|
defer c.copyJobs.Done()
|
|
- reader := bufio.NewReader(src)
|
|
|
|
|
|
+ buf := make([]byte, bufSize)
|
|
|
|
+ n := 0
|
|
|
|
+ eof := false
|
|
|
|
+ msg := &Message{Source: name}
|
|
|
|
|
|
for {
|
|
for {
|
|
select {
|
|
select {
|
|
case <-c.closed:
|
|
case <-c.closed:
|
|
return
|
|
return
|
|
default:
|
|
default:
|
|
- line, err := reader.ReadBytes('\n')
|
|
|
|
- line = bytes.TrimSuffix(line, []byte{'\n'})
|
|
|
|
-
|
|
|
|
- // ReadBytes can return full or partial output even when it failed.
|
|
|
|
- // e.g. it can return a full entry and EOF.
|
|
|
|
- if err == nil || len(line) > 0 {
|
|
|
|
- if logErr := c.dst.Log(&Message{Line: line, Source: name, Timestamp: time.Now().UTC()}); logErr != nil {
|
|
|
|
- logrus.Errorf("Failed to log msg %q for logger %s: %s", line, c.dst.Name(), logErr)
|
|
|
|
- }
|
|
|
|
|
|
+ // Work out how much more data we are okay with reading this time.
|
|
|
|
+ upto := n + readSize
|
|
|
|
+ if upto > cap(buf) {
|
|
|
|
+ upto = cap(buf)
|
|
}
|
|
}
|
|
-
|
|
|
|
- if err != nil {
|
|
|
|
- if err != io.EOF {
|
|
|
|
- logrus.Errorf("Error scanning log stream: %s", err)
|
|
|
|
|
|
+ // Try to read that data.
|
|
|
|
+ if upto > n {
|
|
|
|
+ read, err := src.Read(buf[n:upto])
|
|
|
|
+ if err != nil {
|
|
|
|
+ if err != io.EOF {
|
|
|
|
+ logrus.Errorf("Error scanning log stream: %s", err)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ eof = true
|
|
}
|
|
}
|
|
|
|
+ n += read
|
|
|
|
+ }
|
|
|
|
+ // If we have no data to log, and there's no more coming, we're done.
|
|
|
|
+ if n == 0 && eof {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+ // 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'}) {
|
|
|
|
+ msg.Line = buf[p : p+q]
|
|
|
|
+ msg.Timestamp = time.Now().UTC()
|
|
|
|
+ msg.Partial = false
|
|
|
|
+ select {
|
|
|
|
+ case <-c.closed:
|
|
|
|
+ return
|
|
|
|
+ default:
|
|
|
|
+ if logErr := c.dst.Log(msg); logErr != nil {
|
|
|
|
+ logrus.Errorf("Failed to log msg %q for logger %s: %s", msg.Line, c.dst.Name(), logErr)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ p += q + 1
|
|
|
|
+ }
|
|
|
|
+ // If there's no more coming, or the buffer is full but
|
|
|
|
+ // has no newlines, log whatever we haven't logged yet,
|
|
|
|
+ // noting that it's a partial log line.
|
|
|
|
+ if eof || (p == 0 && n == len(buf)) {
|
|
|
|
+ if p < n {
|
|
|
|
+ msg.Line = buf[p:n]
|
|
|
|
+ msg.Timestamp = time.Now().UTC()
|
|
|
|
+ msg.Partial = true
|
|
|
|
+ if logErr := c.dst.Log(msg); logErr != nil {
|
|
|
|
+ logrus.Errorf("Failed to log msg %q for logger %s: %s", msg.Line, c.dst.Name(), logErr)
|
|
|
|
+ }
|
|
|
|
+ p = 0
|
|
|
|
+ n = 0
|
|
|
|
+ }
|
|
|
|
+ if eof {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // Move any unlogged data to the front of the buffer in preparation for another read.
|
|
|
|
+ if p > 0 {
|
|
|
|
+ copy(buf[0:], buf[p:n])
|
|
|
|
+ n -= p
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|