1package httputils
2
3import (
4	"fmt"
5	"io"
6	"net/url"
7	"sort"
8
9	"golang.org/x/net/context"
10
11	"github.com/docker/docker/api/types"
12	"github.com/docker/docker/api/types/backend"
13	"github.com/docker/docker/pkg/ioutils"
14	"github.com/docker/docker/pkg/jsonmessage"
15	"github.com/docker/docker/pkg/stdcopy"
16)
17
18// WriteLogStream writes an encoded byte stream of log messages from the
19// messages channel, multiplexing them with a stdcopy.Writer if mux is true
20func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *types.ContainerLogsOptions, mux bool) {
21	wf := ioutils.NewWriteFlusher(w)
22	defer wf.Close()
23
24	wf.Flush()
25
26	outStream := io.Writer(wf)
27	errStream := outStream
28	sysErrStream := errStream
29	if mux {
30		sysErrStream = stdcopy.NewStdWriter(outStream, stdcopy.Systemerr)
31		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
32		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
33	}
34
35	for {
36		msg, ok := <-msgs
37		if !ok {
38			return
39		}
40		// check if the message contains an error. if so, write that error
41		// and exit
42		if msg.Err != nil {
43			fmt.Fprintf(sysErrStream, "Error grabbing logs: %v\n", msg.Err)
44			continue
45		}
46		logLine := msg.Line
47		if config.Details {
48			logLine = append(attrsByteSlice(msg.Attrs), ' ')
49			logLine = append(logLine, msg.Line...)
50		}
51		if config.Timestamps {
52			logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
53		}
54		if msg.Source == "stdout" && config.ShowStdout {
55			outStream.Write(logLine)
56		}
57		if msg.Source == "stderr" && config.ShowStderr {
58			errStream.Write(logLine)
59		}
60	}
61}
62
63type byKey []backend.LogAttr
64
65func (b byKey) Len() int           { return len(b) }
66func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
67func (b byKey) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
68
69func attrsByteSlice(a []backend.LogAttr) []byte {
70	// Note this sorts "a" in-place. That is fine here - nothing else is
71	// going to use Attrs or care about the order.
72	sort.Sort(byKey(a))
73
74	var ret []byte
75	for i, pair := range a {
76		k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
77		ret = append(ret, []byte(k)...)
78		ret = append(ret, '=')
79		ret = append(ret, []byte(v)...)
80		if i != len(a)-1 {
81			ret = append(ret, ',')
82		}
83	}
84	return ret
85}
86