moby/daemon/logs.go
Morgan Bauer 1eecc1e7e5
refactor logs to not use internal data structures
- refactor to make it easier to split the api in the future
 - additional tests for non existent container case

Signed-off-by: Morgan Bauer <mbauer@us.ibm.com>
2015-10-07 15:44:16 -07:00

99 lines
2.5 KiB
Go

package daemon
import (
"io"
"strconv"
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/logger"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/stdcopy"
)
// ContainerLogsConfig holds configs for logging operations. Exists
// for users of the daemon to to pass it a logging configuration.
type ContainerLogsConfig struct {
// if true stream log output
Follow bool
// if true include timestamps for each line of log output
Timestamps bool
// return that many lines of log output from the end
Tail string
// filter logs by returning on those entries after this time
Since time.Time
// whether or not to show stdout and stderr as well as log entries.
UseStdout, UseStderr bool
OutStream io.Writer
Stop <-chan bool
}
// ContainerLogs hooks up a container's stdout and stderr streams
// configured with the given struct.
func (daemon *Daemon) ContainerLogs(containerName string, config *ContainerLogsConfig) error {
container, err := daemon.Get(containerName)
if err != nil {
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
}
if !(config.UseStdout || config.UseStderr) {
return derr.ErrorCodeNeedStream
}
outStream := config.OutStream
errStream := outStream
if !container.Config.Tty {
errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
}
config.OutStream = outStream
cLog, err := container.getLogger()
if err != nil {
return err
}
logReader, ok := cLog.(logger.LogReader)
if !ok {
return logger.ErrReadLogsNotSupported
}
follow := config.Follow && container.IsRunning()
tailLines, err := strconv.Atoi(config.Tail)
if err != nil {
tailLines = -1
}
logrus.Debug("logs: begin stream")
readConfig := logger.ReadConfig{
Since: config.Since,
Tail: tailLines,
Follow: follow,
}
logs := logReader.ReadLogs(readConfig)
for {
select {
case err := <-logs.Err:
logrus.Errorf("Error streaming logs: %v", err)
return nil
case <-config.Stop:
logs.Close()
return nil
case msg, ok := <-logs.Msg:
if !ok {
logrus.Debugf("logs: end stream")
return nil
}
logLine := msg.Line
if config.Timestamps {
logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
}
if msg.Source == "stdout" && config.UseStdout {
outStream.Write(logLine)
}
if msg.Source == "stderr" && config.UseStderr {
errStream.Write(logLine)
}
}
}
}