github.com/erriapo/docker@v1.6.0-rc2/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, err := daemon.Get(name) 44 if err != nil { 45 return job.Error(err) 46 } 47 if container.LogDriverType() != "json-file" { 48 return job.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver") 49 } 50 cLog, err := container.ReadLog("json") 51 if err != nil && os.IsNotExist(err) { 52 // Legacy logs 53 log.Debugf("Old logs format") 54 if stdout { 55 cLog, err := container.ReadLog("stdout") 56 if err != nil { 57 log.Errorf("Error reading logs (stdout): %s", err) 58 } else if _, err := io.Copy(job.Stdout, cLog); err != nil { 59 log.Errorf("Error streaming logs (stdout): %s", err) 60 } 61 } 62 if stderr { 63 cLog, err := container.ReadLog("stderr") 64 if err != nil { 65 log.Errorf("Error reading logs (stderr): %s", err) 66 } else if _, err := io.Copy(job.Stderr, cLog); err != nil { 67 log.Errorf("Error streaming logs (stderr): %s", err) 68 } 69 } 70 } else if err != nil { 71 log.Errorf("Error reading logs (json): %s", err) 72 } else { 73 if tail != "all" { 74 var err error 75 lines, err = strconv.Atoi(tail) 76 if err != nil { 77 log.Errorf("Failed to parse tail %s, error: %v, show all logs", tail, err) 78 lines = -1 79 } 80 } 81 if lines != 0 { 82 if lines > 0 { 83 f := cLog.(*os.File) 84 ls, err := tailfile.TailFile(f, lines) 85 if err != nil { 86 return job.Error(err) 87 } 88 tmp := bytes.NewBuffer([]byte{}) 89 for _, l := range ls { 90 fmt.Fprintf(tmp, "%s\n", l) 91 } 92 cLog = tmp 93 } 94 dec := json.NewDecoder(cLog) 95 l := &jsonlog.JSONLog{} 96 for { 97 if err := dec.Decode(l); err == io.EOF { 98 break 99 } else if err != nil { 100 log.Errorf("Error streaming logs: %s", err) 101 break 102 } 103 logLine := l.Log 104 if times { 105 // format can be "" or time format, so here can't be error 106 logLine, _ = l.Format(format) 107 } 108 if l.Stream == "stdout" && stdout { 109 io.WriteString(job.Stdout, logLine) 110 } 111 if l.Stream == "stderr" && stderr { 112 io.WriteString(job.Stderr, logLine) 113 } 114 l.Reset() 115 } 116 } 117 } 118 if follow && container.IsRunning() { 119 errors := make(chan error, 2) 120 wg := sync.WaitGroup{} 121 122 if stdout { 123 wg.Add(1) 124 stdoutPipe := container.StdoutLogPipe() 125 defer stdoutPipe.Close() 126 go func() { 127 errors <- jsonlog.WriteLog(stdoutPipe, job.Stdout, format) 128 wg.Done() 129 }() 130 } 131 if stderr { 132 wg.Add(1) 133 stderrPipe := container.StderrLogPipe() 134 defer stderrPipe.Close() 135 go func() { 136 errors <- jsonlog.WriteLog(stderrPipe, job.Stderr, format) 137 wg.Done() 138 }() 139 } 140 141 wg.Wait() 142 close(errors) 143 144 for err := range errors { 145 if err != nil { 146 log.Errorf("%s", err) 147 } 148 } 149 150 } 151 return engine.StatusOK 152 }