// Logs obtains the stderr and stdout log messages from the container. func Logs(containerID string, stream StdStream) (string, error) { // Create the output buffer. buf := bytes.NewBuffer(nil) // Create the logs options. opts := docker.LogsOptions{ Container: containerID, Follow: false, OutputStream: buf, ErrorStream: buf, } if stream == StdStreamCombined { opts.Stdout = true opts.Stderr = true } else if stream == StdStreamOutput { opts.Stdout = true opts.Stderr = false } else if stream == StdStreamError { opts.Stdout = false opts.Stderr = true } else { return "", fmt.Errorf("invalid stream option!") } // Obtain the logs. err := Client.Logs(opts) if err != nil { return "", fmt.Errorf("failed to get container '%s' logs: %v", containerID, err) } return strings.TrimSpace(buf.String()), nil }
// GetKubeletDockerContainerLogs returns logs of specific container // By default the function will return snapshot of the container log // Log streaming is possible if 'follow' param is set to true // Log tailing is possible when number of tailed lines are set and only if 'follow' is false // TODO: Make 'RawTerminal' option flagable. func GetKubeletDockerContainerLogs(client DockerInterface, containerID, tail string, follow bool, stdout, stderr io.Writer) (err error) { opts := docker.LogsOptions{ Container: containerID, Stdout: true, Stderr: true, OutputStream: stdout, ErrorStream: stderr, Timestamps: true, RawTerminal: false, Follow: follow, } if !follow { opts.Tail = tail } err = client.Logs(opts) return }
// dockerRun mimics the 'docker run --rm' CLI command. It uses the Docker Remote // API to create and start a container and stream its logs. The container is // removed after it terminates. func dockerRun(client DockerClient, createOpts docker.CreateContainerOptions, logsOpts docker.LogsOptions) error { // Create a new container. glog.V(4).Infof("Creating container with options {Name:%q Config:%+v HostConfig:%+v} ...", createOpts.Name, createOpts.Config, createOpts.HostConfig) c, err := client.CreateContainer(createOpts) if err != nil { return fmt.Errorf("create container %q: %v", createOpts.Name, err) } containerName := containerNameOrID(c) removeContainer := func() { glog.V(4).Infof("Removing container %q ...", containerName) if err := client.RemoveContainer(docker.RemoveContainerOptions{ID: c.ID}); err != nil { glog.V(0).Infof("warning: Failed to remove container %q: %v", containerName, err) } else { glog.V(4).Infof("Removed container %q", containerName) } } startWaitContainer := func() error { // Start the container. glog.V(4).Infof("Starting container %q ...", containerName) if err := client.StartContainer(c.ID, nil); err != nil { return fmt.Errorf("start container %q: %v", containerName, err) } // Stream container logs. logsOpts.Container = c.ID glog.V(4).Infof("Streaming logs of container %q with options %+v ...", containerName, logsOpts) if err := client.Logs(logsOpts); err != nil { return fmt.Errorf("streaming logs of %q: %v", containerName, err) } // Return an error if the exit code of the container is non-zero. glog.V(4).Infof("Waiting for container %q to stop ...", containerName) exitCode, err := client.WaitContainer(c.ID) if err != nil { return fmt.Errorf("waiting for container %q to stop: %v", containerName, err) } if exitCode != 0 { return fmt.Errorf("container %q returned non-zero exit code: %d", containerName, exitCode) } return nil } // the interrupt handler acts as a super-defer which will guarantee removeContainer is executed // either when startWaitContainer finishes, or when a SIGQUIT/SIGINT/SIGTERM is received. return interrupt.New(nil, removeContainer).Run(startWaitContainer) }
// dockerRun mimics the 'docker run --rm' CLI command. It uses the Docker Remote // API to create and start a container and stream its logs. The container is // removed after it terminates. func dockerRun(client DockerClient, createOpts docker.CreateContainerOptions, logsOpts docker.LogsOptions) error { // Create a new container. glog.V(4).Infof("Creating container with options {Name:%q Config:%+v HostConfig:%+v} ...", createOpts.Name, createOpts.Config, createOpts.HostConfig) c, err := client.CreateContainer(createOpts) if err != nil { return fmt.Errorf("create container %q: %v", createOpts.Name, err) } containerName := containerNameOrID(c) // Container was created, so we defer its removal. defer func() { glog.V(4).Infof("Removing container %q ...", containerName) if err := client.RemoveContainer(docker.RemoveContainerOptions{ID: c.ID}); err != nil { glog.Warningf("Failed to remove container %q: %v", containerName, err) } else { glog.V(4).Infof("Removed container %q", containerName) } }() // Start the container. glog.V(4).Infof("Starting container %q ...", containerName) if err := client.StartContainer(c.ID, nil); err != nil { return fmt.Errorf("start container %q: %v", containerName, err) } // Stream container logs. logsOpts.Container = c.ID glog.V(4).Infof("Streaming logs of container %q with options %+v ...", containerName, logsOpts) if err := client.Logs(logsOpts); err != nil { return fmt.Errorf("streaming logs of %q: %v", containerName, err) } // Return an error if the exit code of the container is non-zero. glog.V(4).Infof("Waiting for container %q to stop ...", containerName) exitCode, err := client.WaitContainer(c.ID) if err != nil { return fmt.Errorf("waiting for container %q to stop: %v", containerName, err) } if exitCode != 0 { return fmt.Errorf("container %q returned non-zero exit code: %d", containerName, exitCode) } return nil }
func (l *LogsHandler) Handle(key string, initialMessage string, incomingMessages <-chan string, response chan<- common.Message) { defer backend.SignalHandlerClosed(key, response) requestUrl, err := url.Parse(initialMessage) if err != nil { log.WithFields(log.Fields{"error": err, "url": initialMessage}).Error("Couldn't parse url.") return } tokenString := requestUrl.Query().Get("token") token, valid := auth.GetAndCheckToken(tokenString) if !valid { return } logs := token.Claims["logs"].(map[string]interface{}) container := logs["Container"].(string) follow, found := logs["Follow"].(bool) if !found { follow = true } tailTemp, found := logs["Lines"].(int) var tail string if found { tail = strconv.Itoa(int(tailTemp)) } else { tail = "100" } client, err := events.NewDockerClient() if err != nil { log.WithFields(log.Fields{"error": err}).Error("Couldn't get docker client.") return } reader, writer := io.Pipe() containerRef, err := client.InspectContainer(container) if err != nil { return } logopts := dockerClient.LogsOptions{ Container: container, Follow: follow, Stdout: true, Stderr: true, Timestamps: true, Tail: tail, } if containerRef.Config.Tty { logopts.OutputStream = stdbothWriter{writer} logopts.RawTerminal = true } else { logopts.OutputStream = stdoutWriter{writer} logopts.ErrorStream = stderrorWriter{writer} logopts.RawTerminal = false } go func(w *io.PipeWriter) { for { _, ok := <-incomingMessages if !ok { w.Close() return } } }(writer) go func(r *io.PipeReader) { scanner := bufio.NewScanner(r) scanner.Split(customSplit) for scanner.Scan() { text := scanner.Text() message := common.Message{ Key: key, Type: common.Body, Body: text, } response <- message } if err := scanner.Err(); err != nil { log.WithFields(log.Fields{"error": err}).Error("Error with the container log scanner.") } }(reader) // Returns an error, but ignoring it because it will always return an error when a streaming call is made. client.Logs(logopts) }