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