github.com/kobeld/docker@v1.12.0-rc1/daemon/logs.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strconv"
     7  	"time"
     8  
     9  	"golang.org/x/net/context"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/api/types/backend"
    13  	"github.com/docker/docker/container"
    14  	"github.com/docker/docker/daemon/logger"
    15  	"github.com/docker/docker/daemon/logger/jsonfilelog"
    16  	"github.com/docker/docker/pkg/ioutils"
    17  	"github.com/docker/docker/pkg/stdcopy"
    18  	containertypes "github.com/docker/engine-api/types/container"
    19  	timetypes "github.com/docker/engine-api/types/time"
    20  )
    21  
    22  // ContainerLogs hooks up a container's stdout and stderr streams
    23  // configured with the given struct.
    24  func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, config *backend.ContainerLogsConfig, started chan struct{}) error {
    25  	container, err := daemon.GetContainer(containerName)
    26  	if err != nil {
    27  		return err
    28  	}
    29  
    30  	if !(config.ShowStdout || config.ShowStderr) {
    31  		return fmt.Errorf("You must choose at least one stream")
    32  	}
    33  
    34  	cLog, err := daemon.getLogger(container)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	logReader, ok := cLog.(logger.LogReader)
    39  	if !ok {
    40  		return logger.ErrReadLogsNotSupported
    41  	}
    42  
    43  	follow := config.Follow && container.IsRunning()
    44  	tailLines, err := strconv.Atoi(config.Tail)
    45  	if err != nil {
    46  		tailLines = -1
    47  	}
    48  
    49  	logrus.Debug("logs: begin stream")
    50  
    51  	var since time.Time
    52  	if config.Since != "" {
    53  		s, n, err := timetypes.ParseTimestamps(config.Since, 0)
    54  		if err != nil {
    55  			return err
    56  		}
    57  		since = time.Unix(s, n)
    58  	}
    59  	readConfig := logger.ReadConfig{
    60  		Since:  since,
    61  		Tail:   tailLines,
    62  		Follow: follow,
    63  	}
    64  	logs := logReader.ReadLogs(readConfig)
    65  
    66  	wf := ioutils.NewWriteFlusher(config.OutStream)
    67  	defer wf.Close()
    68  	close(started)
    69  	wf.Flush()
    70  
    71  	var outStream io.Writer = wf
    72  	errStream := outStream
    73  	if !container.Config.Tty {
    74  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
    75  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
    76  	}
    77  
    78  	for {
    79  		select {
    80  		case err := <-logs.Err:
    81  			logrus.Errorf("Error streaming logs: %v", err)
    82  			return nil
    83  		case <-ctx.Done():
    84  			logs.Close()
    85  			return nil
    86  		case msg, ok := <-logs.Msg:
    87  			if !ok {
    88  				logrus.Debug("logs: end stream")
    89  				logs.Close()
    90  				return nil
    91  			}
    92  			logLine := msg.Line
    93  			if config.Details {
    94  				logLine = append([]byte(msg.Attrs.String()+" "), logLine...)
    95  			}
    96  			if config.Timestamps {
    97  				logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
    98  			}
    99  			if msg.Source == "stdout" && config.ShowStdout {
   100  				outStream.Write(logLine)
   101  			}
   102  			if msg.Source == "stderr" && config.ShowStderr {
   103  				errStream.Write(logLine)
   104  			}
   105  		}
   106  	}
   107  }
   108  
   109  func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
   110  	if container.LogDriver != nil && container.IsRunning() {
   111  		return container.LogDriver, nil
   112  	}
   113  	return container.StartLogger(container.HostConfig.LogConfig)
   114  }
   115  
   116  // StartLogging initializes and starts the container logging stream.
   117  func (daemon *Daemon) StartLogging(container *container.Container) error {
   118  	if container.HostConfig.LogConfig.Type == "none" {
   119  		return nil // do not start logging routines
   120  	}
   121  
   122  	l, err := container.StartLogger(container.HostConfig.LogConfig)
   123  	if err != nil {
   124  		return fmt.Errorf("Failed to initialize logging driver: %v", err)
   125  	}
   126  
   127  	copier := logger.NewCopier(map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
   128  	container.LogCopier = copier
   129  	copier.Run()
   130  	container.LogDriver = l
   131  
   132  	// set LogPath field only for json-file logdriver
   133  	if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
   134  		container.LogPath = jl.LogPath()
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  // mergeLogConfig merges the daemon log config to the container's log config if the container's log driver is not specified.
   141  func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) error {
   142  	if cfg.Type == "" {
   143  		cfg.Type = daemon.defaultLogConfig.Type
   144  	}
   145  
   146  	if cfg.Type == daemon.defaultLogConfig.Type {
   147  		for k, v := range daemon.defaultLogConfig.Config {
   148  			if _, ok := cfg.Config[k]; !ok {
   149  				cfg.Config[k] = v
   150  			}
   151  		}
   152  	}
   153  
   154  	return logger.ValidateLogOpts(cfg.Type, cfg.Config)
   155  }