github.com/akerouanton/docker@v1.11.0-rc3/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  				logs.Close()
    88  				return nil
    89  			}
    90  			logLine := msg.Line
    91  			if config.Timestamps {
    92  				logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
    93  			}
    94  			if msg.Source == "stdout" && config.ShowStdout {
    95  				outStream.Write(logLine)
    96  			}
    97  			if msg.Source == "stderr" && config.ShowStderr {
    98  				errStream.Write(logLine)
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
   105  	if container.LogDriver != nil && container.IsRunning() {
   106  		return container.LogDriver, nil
   107  	}
   108  	cfg := daemon.getLogConfig(container.HostConfig.LogConfig)
   109  	if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
   110  		return nil, err
   111  	}
   112  	return container.StartLogger(cfg)
   113  }
   114  
   115  // StartLogging initializes and starts the container logging stream.
   116  func (daemon *Daemon) StartLogging(container *container.Container) error {
   117  	cfg := daemon.getLogConfig(container.HostConfig.LogConfig)
   118  	if cfg.Type == "none" {
   119  		return nil // do not start logging routines
   120  	}
   121  
   122  	if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
   123  		return err
   124  	}
   125  	l, err := container.StartLogger(cfg)
   126  	if err != nil {
   127  		return fmt.Errorf("Failed to initialize logging driver: %v", err)
   128  	}
   129  
   130  	copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
   131  	container.LogCopier = copier
   132  	copier.Run()
   133  	container.LogDriver = l
   134  
   135  	// set LogPath field only for json-file logdriver
   136  	if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
   137  		container.LogPath = jl.LogPath()
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  // getLogConfig returns the log configuration for the container.
   144  func (daemon *Daemon) getLogConfig(cfg containertypes.LogConfig) containertypes.LogConfig {
   145  	if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
   146  		if cfg.Type == "" {
   147  			cfg.Type = jsonfilelog.Name
   148  		}
   149  		return cfg
   150  	}
   151  
   152  	// Use daemon's default log config for containers
   153  	return daemon.defaultLogConfig
   154  }