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 }