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 }