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