Ver código fonte

store both logs in a same file, as JSON

Victor Vieux 12 anos atrás
pai
commit
599f85d4e4
4 arquivos alterados com 40 adições e 44 exclusões
  1. 6 6
      commands_test.go
  2. 1 9
      container.go
  3. 15 13
      server.go
  4. 18 16
      utils/utils.go

+ 6 - 6
commands_test.go

@@ -59,7 +59,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
 	return nil
 }
 
-
 // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
 func TestRunHostname(t *testing.T) {
 	stdout, stdoutPipe := io.Pipe()
@@ -91,7 +90,6 @@ func TestRunHostname(t *testing.T) {
 
 }
 
-
 // TestAttachStdin checks attaching to stdin without stdout and stderr.
 // 'docker run -i -a stdin' should sends the client's stdin to the command,
 // then detach from it and print the container id.
@@ -144,15 +142,17 @@ func TestRunAttachStdin(t *testing.T) {
 	})
 
 	// Check logs
-	if cmdLogs, err := container.ReadLog("stdout"); err != nil {
+	if cmdLogs, err := container.ReadLog("json"); err != nil {
 		t.Fatal(err)
 	} else {
 		if output, err := ioutil.ReadAll(cmdLogs); err != nil {
 			t.Fatal(err)
 		} else {
-			expectedLog := "hello\nhi there\n"
-			if string(output) != expectedLog {
-				t.Fatalf("Unexpected logs: should be '%s', not '%s'\n", expectedLog, output)
+			expectedLogs := []string{"{\"log\":\"hello\\n\",\"stream\":\"stdout\"", "{\"log\":\"hi there\\n\",\"stream\":\"stdout\""}
+			for _, expectedLog := range expectedLogs {
+				if !strings.Contains(string(output), expectedLog) {
+					t.Fatalf("Unexpected logs: should contains '%s', it is not '%s'\n", expectedLog, output)
+				}
 			}
 		}
 	}

+ 1 - 9
container.go

@@ -640,21 +640,13 @@ func (container *Container) Start(hostConfig *HostConfig) error {
 	container.cmd = exec.Command("lxc-start", params...)
 
 	// Setup logging of stdout and stderr to disk
-	/*
-	if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout"), ""); err != nil {
-		return err
-	}
-	if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr"), ""); err != nil {
-		return err
-	}
-	*/
 	if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
 		return err
 	}
 	if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
 		return err
 	}
-	
+
 	var err error
 	if container.Config.Tty {
 		err = container.startPty()

+ 15 - 13
server.go

@@ -2,6 +2,7 @@ package docker
 
 import (
 	"bufio"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"github.com/dotcloud/docker/auth"
@@ -1042,20 +1043,21 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
 	}
 	//logs
 	if logs {
-		if stdout {
-			cLog, err := container.ReadLog("stdout")
-			if err != nil {
-				utils.Debugf("Error reading logs (stdout): %s", err)
-			} else if _, err := io.Copy(out, cLog); err != nil {
-				utils.Debugf("Error streaming logs (stdout): %s", err)
+		cLog, err := container.ReadLog("json")
+		if err != nil {
+			utils.Debugf("Error reading logs (json): %s", err)
+		}
+		dec := json.NewDecoder(cLog)
+		for {
+			var l utils.JSONLog
+			if err := dec.Decode(&l); err == io.EOF {
+				break
+			} else if err != nil {
+				utils.Debugf("Error streaming logs: %s", err)
+				break
 			}
-		}
-		if stderr {
-			cLog, err := container.ReadLog("stderr")
-			if err != nil {
-				utils.Debugf("Error reading logs (stderr): %s", err)
-			} else if _, err := io.Copy(out, cLog); err != nil {
-				utils.Debugf("Error streaming logs (stderr): %s", err)
+			if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) {
+				fmt.Fprintf(out, "%s", l.Log)
 			}
 		}
 	}

+ 18 - 16
utils/utils.go

@@ -247,47 +247,49 @@ func (r *bufReader) Close() error {
 
 type WriteBroadcaster struct {
 	sync.Mutex
-	writers map[StreamWriter][]byte
+	buf     *bytes.Buffer
+	writers map[StreamWriter]bool
 }
 
 type StreamWriter struct {
-	wc io.WriteCloser
+	wc     io.WriteCloser
 	stream string
 }
 
 func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser, stream string) {
 	w.Lock()
 	sw := StreamWriter{wc: writer, stream: stream}
-	w.writers[sw] = []byte{}
+	w.writers[sw] = true
 	w.Unlock()
 }
 
 type JSONLog struct {
-	Log   string `json:"log,omitempty"`
-	Stream string `json:"stream,omitempty"`
+	Log     string    `json:"log,omitempty"`
+	Stream  string    `json:"stream,omitempty"`
 	Created time.Time `json:"time"`
 }
 
 func (w *WriteBroadcaster) Write(p []byte) (n int, err error) {
 	w.Lock()
 	defer w.Unlock()
+	w.buf.Write(p)
 	for sw := range w.writers {
 		lp := p
 		if sw.stream != "" {
-			w.writers[sw] = append(w.writers[sw], p...)
-			s := string(p)
-			if s[len(s)-1] == '\n' {
-			/*	lp, err = json.Marshal(&JSONLog{Log: s, Stream: sw.stream, Created: time.Now()})
+			lp = nil
+			for {
+				line, err := w.buf.ReadString('\n')
+				if err != nil {
+					w.buf.Write([]byte(line))
+					break
+				}
+				b, err := json.Marshal(&JSONLog{Log: line, Stream: sw.stream, Created: time.Now()})
 				if err != nil {
 					// On error, evict the writer
 					delete(w.writers, sw)
 					continue
 				}
-			*/
-				lp = []byte("[" + time.Now().String() + "] [" + sw.stream + "] " + s)
-				w.writers[sw] = []byte{}
-			} else {
-				continue
+				lp = append(lp, b...)
 			}
 		}
 		if n, err := sw.wc.Write(lp); err != nil || n != len(lp) {
@@ -304,12 +306,12 @@ func (w *WriteBroadcaster) CloseWriters() error {
 	for sw := range w.writers {
 		sw.wc.Close()
 	}
-	w.writers = make(map[StreamWriter][]byte)
+	w.writers = make(map[StreamWriter]bool)
 	return nil
 }
 
 func NewWriteBroadcaster() *WriteBroadcaster {
-	return &WriteBroadcaster{writers: make(map[StreamWriter][]byte)}
+	return &WriteBroadcaster{writers: make(map[StreamWriter]bool), buf: bytes.NewBuffer(nil)}
 }
 
 func GetTotalUsedFds() int {