github.com/chenchun/docker@v1.3.2-0.20150629222414-20467faf132b/daemon/logs.go (about) 1 package daemon 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net" 9 "os" 10 "strconv" 11 "syscall" 12 "time" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/docker/daemon/logger/jsonfilelog" 16 "github.com/docker/docker/pkg/jsonlog" 17 "github.com/docker/docker/pkg/stdcopy" 18 "github.com/docker/docker/pkg/tailfile" 19 "github.com/docker/docker/pkg/timeutils" 20 ) 21 22 type ContainerLogsConfig struct { 23 Follow, Timestamps bool 24 Tail string 25 Since time.Time 26 UseStdout, UseStderr bool 27 OutStream io.Writer 28 Stop <-chan bool 29 } 30 31 func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) error { 32 var ( 33 lines = -1 34 format string 35 ) 36 if !(config.UseStdout || config.UseStderr) { 37 return fmt.Errorf("You must choose at least one stream") 38 } 39 if config.Timestamps { 40 format = timeutils.RFC3339NanoFixed 41 } 42 if config.Tail == "" { 43 config.Tail = "all" 44 } 45 46 container, err := daemon.Get(name) 47 if err != nil { 48 return err 49 } 50 51 var ( 52 outStream = config.OutStream 53 errStream io.Writer 54 ) 55 if !container.Config.Tty { 56 errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) 57 outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) 58 } else { 59 errStream = outStream 60 } 61 62 if container.LogDriverType() != jsonfilelog.Name { 63 return fmt.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver") 64 } 65 logDriver, err := container.getLogger() 66 cLog, err := logDriver.GetReader() 67 if err != nil { 68 logrus.Errorf("Error reading logs: %s", err) 69 } else { 70 // json-file driver 71 if config.Tail != "all" { 72 var err error 73 lines, err = strconv.Atoi(config.Tail) 74 if err != nil { 75 logrus.Errorf("Failed to parse tail %s, error: %v, show all logs", config.Tail, err) 76 lines = -1 77 } 78 } 79 80 if lines != 0 { 81 if lines > 0 { 82 f := cLog.(*os.File) 83 ls, err := tailfile.TailFile(f, lines) 84 if err != nil { 85 return err 86 } 87 tmp := bytes.NewBuffer([]byte{}) 88 for _, l := range ls { 89 fmt.Fprintf(tmp, "%s\n", l) 90 } 91 cLog = tmp 92 } 93 94 dec := json.NewDecoder(cLog) 95 l := &jsonlog.JSONLog{} 96 for { 97 l.Reset() 98 if err := dec.Decode(l); err == io.EOF { 99 break 100 } else if err != nil { 101 logrus.Errorf("Error streaming logs: %s", err) 102 break 103 } 104 logLine := l.Log 105 if !config.Since.IsZero() && l.Created.Before(config.Since) { 106 continue 107 } 108 if config.Timestamps { 109 // format can be "" or time format, so here can't be error 110 logLine, _ = l.Format(format) 111 } 112 if l.Stream == "stdout" && config.UseStdout { 113 io.WriteString(outStream, logLine) 114 } 115 if l.Stream == "stderr" && config.UseStderr { 116 io.WriteString(errStream, logLine) 117 } 118 } 119 } 120 } 121 122 if config.Follow && container.IsRunning() { 123 chErrStderr := make(chan error) 124 chErrStdout := make(chan error) 125 var stdoutPipe, stderrPipe io.ReadCloser 126 127 // write an empty chunk of data (this is to ensure that the 128 // HTTP Response is sent immediatly, even if the container has 129 // not yet produced any data) 130 outStream.Write(nil) 131 132 if config.UseStdout { 133 stdoutPipe = container.StdoutLogPipe() 134 go func() { 135 logrus.Debug("logs: stdout stream begin") 136 chErrStdout <- jsonlog.WriteLog(stdoutPipe, outStream, format, config.Since) 137 logrus.Debug("logs: stdout stream end") 138 }() 139 } 140 if config.UseStderr { 141 stderrPipe = container.StderrLogPipe() 142 go func() { 143 logrus.Debug("logs: stderr stream begin") 144 chErrStderr <- jsonlog.WriteLog(stderrPipe, errStream, format, config.Since) 145 logrus.Debug("logs: stderr stream end") 146 }() 147 } 148 149 select { 150 case err = <-chErrStderr: 151 if stdoutPipe != nil { 152 stdoutPipe.Close() 153 <-chErrStdout 154 } 155 case err = <-chErrStdout: 156 if stderrPipe != nil { 157 stderrPipe.Close() 158 <-chErrStderr 159 } 160 case <-config.Stop: 161 if stdoutPipe != nil { 162 stdoutPipe.Close() 163 <-chErrStdout 164 } 165 if stderrPipe != nil { 166 stderrPipe.Close() 167 <-chErrStderr 168 } 169 return nil 170 } 171 172 if err != nil && err != io.EOF && err != io.ErrClosedPipe { 173 if e, ok := err.(*net.OpError); ok && e.Err != syscall.EPIPE { 174 logrus.Errorf("error streaming logs: %v", err) 175 } 176 } 177 } 178 return nil 179 }