github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/daemon/logs.go (about)

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