github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/api/server/httputils/write_log_stream.go (about)

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