diff --git a/api/client/lib/container_inspect.go b/api/client/lib/container_inspect.go new file mode 100644 index 0000000000..73863aafee --- /dev/null +++ b/api/client/lib/container_inspect.go @@ -0,0 +1,20 @@ +package lib + +import ( + "encoding/json" + + "github.com/docker/docker/api/types" +) + +// ContainerInspect returns the all the container information. +func (cli *Client) ContainerInspect(containerID string) (types.ContainerJSON, error) { + serverResp, err := cli.GET("/containers/"+containerID+"/json", nil, nil) + if err != nil { + return types.ContainerJSON{}, err + } + defer serverResp.body.Close() + + var response types.ContainerJSON + json.NewDecoder(serverResp.body).Decode(&response) + return response, err +} diff --git a/api/client/lib/logs.go b/api/client/lib/logs.go new file mode 100644 index 0000000000..a464de7c0b --- /dev/null +++ b/api/client/lib/logs.go @@ -0,0 +1,56 @@ +package lib + +import ( + "io" + "net/url" + "time" + + "github.com/docker/docker/pkg/timeutils" +) + +// ContainerLogsOptions holds parameters to filter logs with. +type ContainerLogsOptions struct { + ContainerID string + ShowStdout bool + ShowStderr bool + Since string + Timestamps bool + Follow bool + Tail string +} + +// ContainerLogs returns the logs generated by a container in an io.ReadCloser. +// It's up to the caller to close the stream. +func (cli *Client) ContainerLogs(options ContainerLogsOptions) (io.ReadCloser, error) { + var query url.Values + if options.ShowStdout { + query.Set("stdout", "1") + } + + if options.ShowStderr { + query.Set("stderr", "1") + } + + if options.Since != "" { + ts, err := timeutils.GetTimestamp(options.Since, time.Now()) + if err != nil { + return nil, err + } + query.Set("since", ts) + } + + if options.Timestamps { + query.Set("timestamps", "1") + } + + if options.Follow { + query.Set("follow", "1") + } + query.Set("tail", options.Tail) + + resp, err := cli.GET("/containers/"+options.ContainerID+"/logs", query, nil) + if err != nil { + return nil, err + } + return resp.body, nil +} diff --git a/api/client/logs.go b/api/client/logs.go index 09c3d9398f..52465683a0 100644 --- a/api/client/logs.go +++ b/api/client/logs.go @@ -1,15 +1,13 @@ package client import ( - "encoding/json" "fmt" - "net/url" - "time" + "io" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/client/lib" Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/pkg/timeutils" + "github.com/docker/docker/pkg/stdcopy" ) var validDrivers = map[string]bool{ @@ -32,47 +30,34 @@ func (cli *DockerCli) CmdLogs(args ...string) error { name := cmd.Arg(0) - serverResp, err := cli.call("GET", "/containers/"+name+"/json", nil, nil) + c, err := cli.client.ContainerInspect(name) if err != nil { return err } - var c types.ContainerJSON - if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil { - return err - } - if !validDrivers[c.HostConfig.LogConfig.Type] { return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type) } - v := url.Values{} - v.Set("stdout", "1") - v.Set("stderr", "1") - - if *since != "" { - ts, err := timeutils.GetTimestamp(*since, time.Now()) - if err != nil { - return err - } - v.Set("since", ts) + options := lib.ContainerLogsOptions{ + ContainerID: name, + ShowStdout: true, + ShowStderr: true, + Since: *since, + Timestamps: *times, + Follow: *follow, + Tail: *tail, } - - if *times { - v.Set("timestamps", "1") + responseBody, err := cli.client.ContainerLogs(options) + if err != nil { + return err } + defer responseBody.Close() - if *follow { - v.Set("follow", "1") + if c.Config.Tty { + _, err = io.Copy(cli.out, responseBody) + } else { + _, err = stdcopy.StdCopy(cli.out, cli.err, responseBody) } - v.Set("tail", *tail) - - sopts := &streamOpts{ - rawTerminal: c.Config.Tty, - out: cli.out, - err: cli.err, - } - - _, err = cli.stream("GET", "/containers/"+name+"/logs?"+v.Encode(), sopts) return err } diff --git a/integration-cli/docker_cli_logs_test.go b/integration-cli/docker_cli_logs_test.go index b572475c73..94ca1d3de8 100644 --- a/integration-cli/docker_cli_logs_test.go +++ b/integration-cli/docker_cli_logs_test.go @@ -349,6 +349,6 @@ func (s *DockerSuite) TestLogsFollowGoroutinesNoOutput(c *check.C) { func (s *DockerSuite) TestLogsCLIContainerNotFound(c *check.C) { name := "testlogsnocontainer" out, _, _ := dockerCmdWithError("logs", name) - message := fmt.Sprintf(".*No such container: %s.*\n", name) - c.Assert(out, checker.Matches, message) + message := fmt.Sprintf("Error: No such container: %s\n", name) + c.Assert(out, checker.Equals, message) }