github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/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 "github.com/Sirupsen/logrus" 13 "github.com/docker/docker/pkg/jsonlog" 14 "github.com/docker/docker/pkg/stdcopy" 15 "github.com/docker/docker/pkg/tailfile" 16 "github.com/docker/docker/pkg/timeutils" 17 ) 18 19 type ContainerLogsConfig struct { 20 Follow, Timestamps bool 21 Tail string 22 UseStdout, UseStderr bool 23 OutStream io.Writer 24 } 25 26 func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) error { 27 var ( 28 lines = -1 29 format string 30 ) 31 if !(config.UseStdout || config.UseStderr) { 32 return fmt.Errorf("You must choose at least one stream") 33 } 34 if config.Timestamps { 35 format = timeutils.RFC3339NanoFixed 36 } 37 if config.Tail == "" { 38 config.Tail = "all" 39 } 40 41 container, err := daemon.Get(name) 42 if err != nil { 43 return err 44 } 45 46 var ( 47 outStream = config.OutStream 48 errStream io.Writer 49 ) 50 if !container.Config.Tty { 51 errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) 52 outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) 53 } else { 54 errStream = outStream 55 } 56 57 if container.LogDriverType() != "json-file" { 58 return fmt.Errorf("\"logs\" endpoint is supported only for \"json-file\" logging driver") 59 } 60 cLog, err := container.ReadLog("json") 61 if err != nil && os.IsNotExist(err) { 62 // Legacy logs 63 logrus.Debugf("Old logs format") 64 if config.UseStdout { 65 cLog, err := container.ReadLog("stdout") 66 if err != nil { 67 logrus.Errorf("Error reading logs (stdout): %s", err) 68 } else if _, err := io.Copy(outStream, cLog); err != nil { 69 logrus.Errorf("Error streaming logs (stdout): %s", err) 70 } 71 } 72 if config.UseStderr { 73 cLog, err := container.ReadLog("stderr") 74 if err != nil { 75 logrus.Errorf("Error reading logs (stderr): %s", err) 76 } else if _, err := io.Copy(errStream, cLog); err != nil { 77 logrus.Errorf("Error streaming logs (stderr): %s", err) 78 } 79 } 80 } else if err != nil { 81 logrus.Errorf("Error reading logs (json): %s", err) 82 } else { 83 if config.Tail != "all" { 84 var err error 85 lines, err = strconv.Atoi(config.Tail) 86 if err != nil { 87 logrus.Errorf("Failed to parse tail %s, error: %v, show all logs", config.Tail, err) 88 lines = -1 89 } 90 } 91 if lines != 0 { 92 if lines > 0 { 93 f := cLog.(*os.File) 94 ls, err := tailfile.TailFile(f, lines) 95 if err != nil { 96 return err 97 } 98 tmp := bytes.NewBuffer([]byte{}) 99 for _, l := range ls { 100 fmt.Fprintf(tmp, "%s\n", l) 101 } 102 cLog = tmp 103 } 104 dec := json.NewDecoder(cLog) 105 l := &jsonlog.JSONLog{} 106 for { 107 if err := dec.Decode(l); err == io.EOF { 108 break 109 } else if err != nil { 110 logrus.Errorf("Error streaming logs: %s", err) 111 break 112 } 113 logLine := l.Log 114 if config.Timestamps { 115 // format can be "" or time format, so here can't be error 116 logLine, _ = l.Format(format) 117 } 118 if l.Stream == "stdout" && config.UseStdout { 119 io.WriteString(outStream, logLine) 120 } 121 if l.Stream == "stderr" && config.UseStderr { 122 io.WriteString(errStream, logLine) 123 } 124 l.Reset() 125 } 126 } 127 } 128 if config.Follow && container.IsRunning() { 129 errors := make(chan error, 2) 130 wg := sync.WaitGroup{} 131 132 if config.UseStdout { 133 wg.Add(1) 134 stdoutPipe := container.StdoutLogPipe() 135 defer stdoutPipe.Close() 136 go func() { 137 errors <- jsonlog.WriteLog(stdoutPipe, outStream, format) 138 wg.Done() 139 }() 140 } 141 if config.UseStderr { 142 wg.Add(1) 143 stderrPipe := container.StderrLogPipe() 144 defer stderrPipe.Close() 145 go func() { 146 errors <- jsonlog.WriteLog(stderrPipe, errStream, format) 147 wg.Done() 148 }() 149 } 150 151 wg.Wait() 152 close(errors) 153 154 for err := range errors { 155 if err != nil { 156 logrus.Errorf("%s", err) 157 } 158 } 159 160 } 161 return nil 162 }