github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/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  				if cLog != container.LogDriver {
    91  					// Since the logger isn't cached in the container, which occurs if it is running, it
    92  					// must get explicitly closed here to avoid leaking it and any file handles it has.
    93  					if err := cLog.Close(); err != nil {
    94  						logrus.Errorf("Error closing logger: %v", err)
    95  					}
    96  				}
    97  				return nil
    98  			}
    99  			logLine := msg.Line
   100  			if config.Details {
   101  				logLine = append([]byte(msg.Attrs.String()+" "), logLine...)
   102  			}
   103  			if config.Timestamps {
   104  				logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
   105  			}
   106  			if msg.Source == "stdout" && config.ShowStdout {
   107  				outStream.Write(logLine)
   108  			}
   109  			if msg.Source == "stderr" && config.ShowStderr {
   110  				errStream.Write(logLine)
   111  			}
   112  		}
   113  	}
   114  }
   115  
   116  func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
   117  	if container.LogDriver != nil && container.IsRunning() {
   118  		return container.LogDriver, nil
   119  	}
   120  	return container.StartLogger(container.HostConfig.LogConfig)
   121  }
   122  
   123  // StartLogging initializes and starts the container logging stream.
   124  func (daemon *Daemon) StartLogging(container *container.Container) error {
   125  	if container.HostConfig.LogConfig.Type == "none" {
   126  		return nil // do not start logging routines
   127  	}
   128  
   129  	l, err := container.StartLogger(container.HostConfig.LogConfig)
   130  	if err != nil {
   131  		return fmt.Errorf("Failed to initialize logging driver: %v", err)
   132  	}
   133  
   134  	copier := logger.NewCopier(map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
   135  	container.LogCopier = copier
   136  	copier.Run()
   137  	container.LogDriver = l
   138  
   139  	// set LogPath field only for json-file logdriver
   140  	if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
   141  		container.LogPath = jl.LogPath()
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // mergeLogConfig merges the daemon log config to the container's log config if the container's log driver is not specified.
   148  func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) error {
   149  	if cfg.Type == "" {
   150  		cfg.Type = daemon.defaultLogConfig.Type
   151  	}
   152  
   153  	if cfg.Type == daemon.defaultLogConfig.Type {
   154  		for k, v := range daemon.defaultLogConfig.Config {
   155  			if _, ok := cfg.Config[k]; !ok {
   156  				cfg.Config[k] = v
   157  			}
   158  		}
   159  	}
   160  
   161  	return logger.ValidateLogOpts(cfg.Type, cfg.Config)
   162  }