package containers import ( "bytes" "context" "fmt" "io" "net/http" "net/url" "strconv" "github.com/containers/libpod/pkg/bindings" "github.com/pkg/errors" ) // Logs obtains a container's logs given the options provided. The logs are then sent to the // stdout|stderr channels as strings. func Logs(ctx context.Context, nameOrID string, opts LogOptions, stdoutChan, stderrChan chan string) error { conn, err := bindings.GetClient(ctx) if err != nil { return err } params := url.Values{} if opts.Follow != nil { params.Set("follow", strconv.FormatBool(*opts.Follow)) } if opts.Since != nil { params.Set("since", *opts.Since) } if opts.Stderr != nil { params.Set("stderr", strconv.FormatBool(*opts.Stderr)) } if opts.Stdout != nil { params.Set("stdout", strconv.FormatBool(*opts.Stdout)) } if opts.Tail != nil { params.Set("tail", *opts.Tail) } if opts.Timestamps != nil { params.Set("timestamps", strconv.FormatBool(*opts.Timestamps)) } if opts.Until != nil { params.Set("until", *opts.Until) } // The API requires either stdout|stderr be used. If neither are specified, we specify stdout if opts.Stdout == nil && opts.Stderr == nil { params.Set("stdout", strconv.FormatBool(true)) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID) if err != nil { return err } buffer := make([]byte, 1024) for { fd, l, err := DemuxHeader(response.Body, buffer) if err != nil { if errors.Is(err, io.EOF) { return nil } return err } frame, err := DemuxFrame(response.Body, buffer, l) if err != nil { return err } frame = bytes.Replace(frame[0:l], []byte{13}, []byte{10}, -1) switch fd { case 0: stdoutChan <- string(frame) case 1: stdoutChan <- string(frame) case 2: stderrChan <- string(frame) case 3: return errors.New("error from service in stream: " + string(frame)) default: return fmt.Errorf("unrecognized input header: %d", fd) } } }