2018-02-05 16:05:59 -05:00
|
|
|
package httputils // import "github.com/docker/docker/api/server/httputils"
|
2017-03-20 13:07:04 -04:00
|
|
|
|
|
|
|
import (
|
2018-04-19 18:30:59 -04:00
|
|
|
"context"
|
2017-03-20 13:07:04 -04:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-04-04 18:52:19 -04:00
|
|
|
"net/url"
|
2017-03-20 13:07:04 -04:00
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/api/types/backend"
|
|
|
|
"github.com/docker/docker/pkg/ioutils"
|
2017-09-22 14:40:10 -04:00
|
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
2017-03-20 13:07:04 -04:00
|
|
|
"github.com/docker/docker/pkg/stdcopy"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WriteLogStream writes an encoded byte stream of log messages from the
|
|
|
|
// messages channel, multiplexing them with a stdcopy.Writer if mux is true
|
2017-09-11 14:55:05 -04:00
|
|
|
func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *types.ContainerLogsOptions, mux bool) {
|
2017-03-20 13:07:04 -04:00
|
|
|
wf := ioutils.NewWriteFlusher(w)
|
|
|
|
defer wf.Close()
|
|
|
|
|
|
|
|
wf.Flush()
|
|
|
|
|
2017-09-11 14:55:05 -04:00
|
|
|
outStream := io.Writer(wf)
|
2017-03-20 13:07:04 -04:00
|
|
|
errStream := outStream
|
|
|
|
sysErrStream := errStream
|
|
|
|
if mux {
|
|
|
|
sysErrStream = stdcopy.NewStdWriter(outStream, stdcopy.Systemerr)
|
|
|
|
errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
|
|
|
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
msg, ok := <-msgs
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// check if the message contains an error. if so, write that error
|
|
|
|
// and exit
|
|
|
|
if msg.Err != nil {
|
|
|
|
fmt.Fprintf(sysErrStream, "Error grabbing logs: %v\n", msg.Err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logLine := msg.Line
|
|
|
|
if config.Details {
|
2017-07-18 22:01:20 -04:00
|
|
|
logLine = append(attrsByteSlice(msg.Attrs), ' ')
|
|
|
|
logLine = append(logLine, msg.Line...)
|
2017-03-20 13:07:04 -04:00
|
|
|
}
|
|
|
|
if config.Timestamps {
|
2017-09-22 14:40:10 -04:00
|
|
|
logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
|
2017-03-20 13:07:04 -04:00
|
|
|
}
|
|
|
|
if msg.Source == "stdout" && config.ShowStdout {
|
|
|
|
outStream.Write(logLine)
|
|
|
|
}
|
|
|
|
if msg.Source == "stderr" && config.ShowStderr {
|
|
|
|
errStream.Write(logLine)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-18 22:01:20 -04:00
|
|
|
type byKey []backend.LogAttr
|
2017-03-20 13:07:04 -04:00
|
|
|
|
2017-07-18 22:01:20 -04:00
|
|
|
func (b byKey) Len() int { return len(b) }
|
|
|
|
func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
|
|
|
|
func (b byKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
2017-03-20 13:07:04 -04:00
|
|
|
|
2017-07-18 22:01:20 -04:00
|
|
|
func attrsByteSlice(a []backend.LogAttr) []byte {
|
|
|
|
// Note this sorts "a" in-place. That is fine here - nothing else is
|
|
|
|
// going to use Attrs or care about the order.
|
|
|
|
sort.Sort(byKey(a))
|
|
|
|
|
|
|
|
var ret []byte
|
|
|
|
for i, pair := range a {
|
|
|
|
k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
|
|
|
|
ret = append(ret, []byte(k)...)
|
|
|
|
ret = append(ret, '=')
|
|
|
|
ret = append(ret, []byte(v)...)
|
|
|
|
if i != len(a)-1 {
|
|
|
|
ret = append(ret, ',')
|
|
|
|
}
|
2017-03-20 13:07:04 -04:00
|
|
|
}
|
2017-07-18 22:01:20 -04:00
|
|
|
return ret
|
2017-03-20 13:07:04 -04:00
|
|
|
}
|