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  }