forked from projectcalico/felix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logutils.go
172 lines (150 loc) · 5.94 KB
/
logutils.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright (c) 2016-2018 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logutils
import (
"os"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/felix/config"
"github.com/projectcalico/libcalico-go/lib/logutils"
)
var (
counterDroppedLogs = prometheus.NewCounter(prometheus.CounterOpts{
Name: "felix_logs_dropped",
Help: "Number of logs dropped because the output stream was blocked.",
})
counterLogErrors = prometheus.NewCounter(prometheus.CounterOpts{
Name: "felix_log_errors",
Help: "Number of errors encountered while logging.",
})
)
func init() {
prometheus.MustRegister(
counterDroppedLogs,
counterLogErrors,
)
}
const logQueueSize = 100
// ConfigureEarlyLogging installs our logging adapters, and enables early logging to screen
// if it is enabled by either the FELIX_EARLYLOGSEVERITYSCREEN or FELIX_LOGSEVERITYSCREEN
// environment variable.
func ConfigureEarlyLogging() {
// Log to stdout. This prevents fluentd, for example, from interpreting all our logs as errors by default.
log.SetOutput(os.Stdout)
// Replace logrus' formatter with a custom one using our time format,
// shared with the Python code.
log.SetFormatter(&logutils.Formatter{})
// Install a hook that adds file/line no information.
log.AddHook(&logutils.ContextHook{})
// First try the early-only environment variable. Since the normal
// config processing doesn't know about that variable, normal config
// will override it once it's loaded.
rawLogLevel := os.Getenv("FELIX_EARLYLOGSEVERITYSCREEN")
if rawLogLevel == "" {
// Early-only flag not set, look for the normal config-owned
// variable.
rawLogLevel = os.Getenv("FELIX_LOGSEVERITYSCREEN")
}
// Default to logging errors.
logLevelScreen := log.ErrorLevel
if rawLogLevel != "" {
parsedLevel, err := log.ParseLevel(rawLogLevel)
if err == nil {
logLevelScreen = parsedLevel
} else {
log.WithError(err).Error("Failed to parse early log level, defaulting to error.")
}
}
log.SetLevel(logLevelScreen)
log.Infof("Early screen log level set to %v", logLevelScreen)
}
// ConfigureLogging uses the resolved configuration to complete the logging
// configuration. It creates hooks for the relevant logging targets and
// attaches them to logrus.
func ConfigureLogging(configParams *config.Config) {
// Parse the log levels, defaulting to panic if in doubt.
logLevelScreen := logutils.SafeParseLogLevel(configParams.LogSeverityScreen)
logLevelFile := logutils.SafeParseLogLevel(configParams.LogSeverityFile)
logLevelSyslog := logutils.SafeParseLogLevel(configParams.LogSeveritySys)
// Work out the most verbose level that is being logged.
mostVerboseLevel := logLevelScreen
if logLevelFile > mostVerboseLevel {
mostVerboseLevel = logLevelFile
}
if logLevelSyslog > mostVerboseLevel {
mostVerboseLevel = logLevelScreen
}
// Disable all more-verbose levels using the global setting, this ensures that debug logs
// are filtered out as early as possible.
log.SetLevel(mostVerboseLevel)
// Screen target.
var dests []*logutils.Destination
if configParams.LogSeverityScreen != "" {
dests = append(dests, getScreenDestination(configParams, logLevelScreen))
}
// File target. We record any errors so we can log them out below after finishing set-up
// of the logger.
var fileDirErr, fileOpenErr error
if configParams.LogSeverityFile != "" && configParams.LogFilePath != "" {
var destination *logutils.Destination
destination, fileDirErr, fileOpenErr = getFileDestination(configParams, logLevelFile)
if fileDirErr == nil && fileOpenErr == nil && destination != nil {
dests = append(dests, destination)
}
}
// Syslog target. Again, we record the error if we fail to connect to syslog.
var sysErr error
if configParams.LogSeveritySys != "" {
var destination *logutils.Destination
destination, sysErr = getSyslogDestination(configParams, logLevelSyslog)
if sysErr == nil && destination != nil {
dests = append(dests, destination)
}
}
hook := logutils.NewBackgroundHook(logutils.FilterLevels(mostVerboseLevel), logLevelSyslog, dests, counterDroppedLogs)
hook.Start()
log.AddHook(hook)
// Disable logrus' default output, which only supports a single destination. We use the
// hook above to fan out logs to multiple destinations.
log.SetOutput(&logutils.NullWriter{})
// Since we push our logs onto a second thread via a channel, we can disable the
// Logger's built-in mutex completely.
log.StandardLogger().SetNoLock()
// Do any deferred error logging.
if fileDirErr != nil {
log.WithError(fileDirErr).WithField("file", configParams.LogFilePath).
Fatal("Failed to create log file directory.")
}
if fileOpenErr != nil {
log.WithError(fileOpenErr).WithField("file", configParams.LogFilePath).
Fatal("Failed to open log file.")
}
if sysErr != nil {
// We don't bail out if we can't connect to syslog because our default is to try to
// connect but it's very common for syslog to be disabled when we're run in a
// container.
log.WithError(sysErr).Error(
"Failed to connect to syslog. To prevent this error, either set config " +
"parameter LogSeveritySys=none or configure a local syslog service.")
}
}
func getScreenDestination(configParams *config.Config, logLevel log.Level) *logutils.Destination {
return logutils.NewStreamDestination(
logLevel,
os.Stdout,
make(chan logutils.QueuedLog, logQueueSize),
configParams.DebugDisableLogDropping,
counterLogErrors,
)
}