-
Notifications
You must be signed in to change notification settings - Fork 8
/
log_relay.go
125 lines (102 loc) · 3.61 KB
/
log_relay.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
package main
import (
"bufio"
"io"
"strings"
"time"
"github.com/Nitro/sidecar-executor/container"
"github.com/Nitro/sidecar-executor/loghooks"
log "github.com/sirupsen/logrus"
)
func (exec *sidecarExecutor) configureLogRelay(containerId string,
labels map[string]string, output io.Writer) *log.Entry {
syslogger := log.New()
// We relay UDP syslog because we don't plan to ship it off the box
// and because it's simplest since there is no backpressure issue to
// deal with.
hook, err := loghooks.NewUDPHook(exec.config.SyslogAddr)
if err != nil {
log.Fatalf("Error adding hook: %s", err)
}
syslogger.Hooks.Add(hook)
syslogger.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyTime: "Timestamp",
log.FieldKeyLevel: "Level",
log.FieldKeyMsg: "Payload",
log.FieldKeyFunc: "Func",
},
})
syslogger.SetOutput(output)
// Add one to the labels length to account for hostname
fields := make(log.Fields, len(exec.config.SendDockerLabels)+1)
// Loop through the fields we're supposed to pass, and add them from the
// Docker labels on this container
for _, field := range exec.config.SendDockerLabels {
if val, ok := labels[field]; ok {
fields[field] = val
}
}
fields["Hostname"] = exec.config.LogHostname
return syslogger.WithFields(fields)
}
// relayLogs will watch a container and send the logs to Syslog
func (exec *sidecarExecutor) relayLogs(quitChan chan struct{},
containerId string, labels map[string]string, output io.Writer) {
logger := exec.configureLogRelay(containerId, labels, output)
logger.Infof("sidecar-executor starting log pump for '%s'", containerId[:12])
log.Info("Started syslog log pump") // Send to local log output
outrd, outwr := io.Pipe()
errrd, errwr := io.Pipe()
// Tell Docker client to start pumping logs into our pipes
container.FollowLogs(exec.client, containerId, 0, outwr, errwr)
go exec.handleOneStream(quitChan, "stdout", logger, outrd)
go exec.handleOneStream(quitChan, "stderr", logger, errrd)
if exec.config.RelaySyslogStartupOnly {
go cancelAfterStartup(quitChan, exec.config.RelaySyslogStartupTime)
}
<-quitChan
}
// cancelAfterStartup will stop the log pump after RelaySyslogStartupTime. This
// is used for apps that do their lown logging when started, but might fail
// during startup and need us to pump startup logs.
func cancelAfterStartup(quitChan chan struct{}, startupTime time.Duration) {
<-time.After(startupTime)
close(quitChan)
}
// handleOneStream will process one data stream into logs
func (exec *sidecarExecutor) handleOneStream(quitChan chan struct{}, name string,
logger *log.Entry, in io.Reader) {
scanner := bufio.NewScanner(in) // Defaults to splitting as lines
for scanner.Scan() {
// Before processing anything, see if we should be exiting. Note that
// this still doesn't exit until the _next_ log is processed after the
// channel was closed.
select {
case <-quitChan:
return
default:
// nothing
}
text := scanner.Text()
log.Debugf("docker: %s", text)
switch name {
case "stdout":
logger.Info(text) // Send to syslog "info"
case "stderr":
// Pretty basic attempt to scrape only errors from the logs
if strings.Contains(strings.ToLower(text), "error") {
logger.Error(text) // Send to syslog "error"
} else {
logger.Info(text) // Send to syslog "info"
}
default:
log.Errorf("handleOneStream(): Unknown stream type '%s'. Exiting log pump.", name)
return
}
}
if err := scanner.Err(); err != nil {
log.Errorf("handleOneStream() error reading Docker log input: '%s'. Exiting log pump '%s'.", err, name)
}
log.Warnf("Log pump exited for '%s'", name)
}