forked from moby/moby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logs.go
140 lines (124 loc) · 3.73 KB
/
logs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package daemon
import (
"io"
"strconv"
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/logger"
"github.com/docker/docker/daemon/logger/jsonfilelog"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/stdcopy"
)
// ContainerLogsConfig holds configs for logging operations. Exists
// for users of the daemon to to pass it a logging configuration.
type ContainerLogsConfig struct {
// if true stream log output
Follow bool
// if true include timestamps for each line of log output
Timestamps bool
// return that many lines of log output from the end
Tail string
// filter logs by returning on those entries after this time
Since time.Time
// whether or not to show stdout and stderr as well as log entries.
UseStdout, UseStderr bool
OutStream io.Writer
Stop <-chan bool
}
// ContainerLogs hooks up a container's stdout and stderr streams
// configured with the given struct.
func (daemon *Daemon) ContainerLogs(containerName string, config *ContainerLogsConfig) error {
container, err := daemon.GetContainer(containerName)
if err != nil {
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
}
if !(config.UseStdout || config.UseStderr) {
return derr.ErrorCodeNeedStream
}
outStream := config.OutStream
errStream := outStream
if !container.Config.Tty {
errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
}
config.OutStream = outStream
cLog, err := daemon.getLogger(container)
if err != nil {
return err
}
logReader, ok := cLog.(logger.LogReader)
if !ok {
return logger.ErrReadLogsNotSupported
}
follow := config.Follow && container.IsRunning()
tailLines, err := strconv.Atoi(config.Tail)
if err != nil {
tailLines = -1
}
logrus.Debug("logs: begin stream")
readConfig := logger.ReadConfig{
Since: config.Since,
Tail: tailLines,
Follow: follow,
}
logs := logReader.ReadLogs(readConfig)
for {
select {
case err := <-logs.Err:
logrus.Errorf("Error streaming logs: %v", err)
return nil
case <-config.Stop:
logs.Close()
return nil
case msg, ok := <-logs.Msg:
if !ok {
logrus.Debugf("logs: end stream")
return nil
}
logLine := msg.Line
if config.Timestamps {
logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
}
if msg.Source == "stdout" && config.UseStdout {
outStream.Write(logLine)
}
if msg.Source == "stderr" && config.UseStderr {
errStream.Write(logLine)
}
}
}
}
func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
if container.LogDriver != nil && container.IsRunning() {
return container.LogDriver, nil
}
cfg := container.GetLogConfig(daemon.defaultLogConfig)
if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
return nil, err
}
return container.StartLogger(cfg)
}
// StartLogging initializes and starts the container logging stream.
func (daemon *Daemon) StartLogging(container *container.Container) error {
cfg := container.GetLogConfig(daemon.defaultLogConfig)
if cfg.Type == "none" {
return nil // do not start logging routines
}
if err := logger.ValidateLogOpts(cfg.Type, cfg.Config); err != nil {
return err
}
l, err := container.StartLogger(cfg)
if err != nil {
return derr.ErrorCodeInitLogger.WithArgs(err)
}
copier := logger.NewCopier(container.ID, map[string]io.Reader{"stdout": container.StdoutPipe(), "stderr": container.StderrPipe()}, l)
container.LogCopier = copier
copier.Run()
container.LogDriver = l
// set LogPath field only for json-file logdriver
if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok {
container.LogPath = jl.LogPath()
}
return nil
}