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 }