github.com/dougm/docker@v1.5.0/daemon/logs.go (about) 1 package daemon 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "os" 9 "strconv" 10 "sync" 11 12 log "github.com/Sirupsen/logrus" 13 "github.com/docker/docker/engine" 14 "github.com/docker/docker/pkg/jsonlog" 15 "github.com/docker/docker/pkg/tailfile" 16 "github.com/docker/docker/pkg/timeutils" 17 ) 18 19 func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status { 20 if len(job.Args) != 1 { 21 return job.Errorf("Usage: %s CONTAINER\n", job.Name) 22 } 23 24 var ( 25 name = job.Args[0] 26 stdout = job.GetenvBool("stdout") 27 stderr = job.GetenvBool("stderr") 28 tail = job.Getenv("tail") 29 follow = job.GetenvBool("follow") 30 times = job.GetenvBool("timestamps") 31 lines = -1 32 format string 33 ) 34 if !(stdout || stderr) { 35 return job.Errorf("You must choose at least one stream") 36 } 37 if times { 38 format = timeutils.RFC3339NanoFixed 39 } 40 if tail == "" { 41 tail = "all" 42 } 43 container := daemon.Get(name) 44 if container == nil { 45 return job.Errorf("No such container: %s", name) 46 } 47 cLog, err := container.ReadLog("json") 48 if err != nil && os.IsNotExist(err) { 49 // Legacy logs 50 log.Debugf("Old logs format") 51 if stdout { 52 cLog, err := container.ReadLog("stdout") 53 if err != nil { 54 log.Errorf("Error reading logs (stdout): %s", err) 55 } else if _, err := io.Copy(job.Stdout, cLog); err != nil { 56 log.Errorf("Error streaming logs (stdout): %s", err) 57 } 58 } 59 if stderr { 60 cLog, err := container.ReadLog("stderr") 61 if err != nil { 62 log.Errorf("Error reading logs (stderr): %s", err) 63 } else if _, err := io.Copy(job.Stderr, cLog); err != nil { 64 log.Errorf("Error streaming logs (stderr): %s", err) 65 } 66 } 67 } else if err != nil { 68 log.Errorf("Error reading logs (json): %s", err) 69 } else { 70 if tail != "all" { 71 var err error 72 lines, err = strconv.Atoi(tail) 73 if err != nil { 74 log.Errorf("Failed to parse tail %s, error: %v, show all logs", tail, err) 75 lines = -1 76 } 77 } 78 if lines != 0 { 79 if lines > 0 { 80 f := cLog.(*os.File) 81 ls, err := tailfile.TailFile(f, lines) 82 if err != nil { 83 return job.Error(err) 84 } 85 tmp := bytes.NewBuffer([]byte{}) 86 for _, l := range ls { 87 fmt.Fprintf(tmp, "%s\n", l) 88 } 89 cLog = tmp 90 } 91 dec := json.NewDecoder(cLog) 92 l := &jsonlog.JSONLog{} 93 for { 94 if err := dec.Decode(l); err == io.EOF { 95 break 96 } else if err != nil { 97 log.Errorf("Error streaming logs: %s", err) 98 break 99 } 100 logLine := l.Log 101 if times { 102 logLine = fmt.Sprintf("%s %s", l.Created.Format(format), logLine) 103 } 104 if l.Stream == "stdout" && stdout { 105 io.WriteString(job.Stdout, logLine) 106 } 107 if l.Stream == "stderr" && stderr { 108 io.WriteString(job.Stderr, logLine) 109 } 110 l.Reset() 111 } 112 } 113 } 114 if follow && container.IsRunning() { 115 errors := make(chan error, 2) 116 wg := sync.WaitGroup{} 117 118 if stdout { 119 wg.Add(1) 120 stdoutPipe := container.StdoutLogPipe() 121 defer stdoutPipe.Close() 122 go func() { 123 errors <- jsonlog.WriteLog(stdoutPipe, job.Stdout, format) 124 wg.Done() 125 }() 126 } 127 if stderr { 128 wg.Add(1) 129 stderrPipe := container.StderrLogPipe() 130 defer stderrPipe.Close() 131 go func() { 132 errors <- jsonlog.WriteLog(stderrPipe, job.Stderr, format) 133 wg.Done() 134 }() 135 } 136 137 wg.Wait() 138 close(errors) 139 140 for err := range errors { 141 if err != nil { 142 log.Errorf("%s", err) 143 } 144 } 145 146 } 147 return engine.StatusOK 148 }