cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
55ed995bf08df2859e6e0da36b4a77358efef8f0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

logger/create.go

276lines · modecode

1package logger
2
3import (
4 "fmt"
5 "io"
6 "os"
7 "path"
8 "path/filepath"
9 "sync"
10 "time"
11
12 "github.com/mattn/go-colorable"
13 "github.com/rs/zerolog"
14 fallbacklog "github.com/rs/zerolog/log"
15 "github.com/urfave/cli/v2"
16 "golang.org/x/term"
17 "gopkg.in/natefinch/lumberjack.v2"
18
19 "github.com/cloudflare/cloudflared/features"
20 "github.com/cloudflare/cloudflared/management"
21)
22
23const (
24 EnableTerminalLog = false
25 DisableTerminalLog = true
26
27 LogLevelFlag = "loglevel"
28 LogFileFlag = "logfile"
29 LogDirectoryFlag = "log-directory"
30 LogTransportLevelFlag = "transport-loglevel"
31
32 LogSSHDirectoryFlag = "log-directory"
33 LogSSHLevelFlag = "log-level"
34
35 dirPermMode = 0744 // rwxr--r--
36 filePermMode = 0644 // rw-r--r--
37
38 consoleTimeFormat = time.RFC3339
39)
40
41var (
42 ManagementLogger *management.Logger
43)
44
45func init() {
46 zerolog.TimeFieldFormat = time.RFC3339
47 zerolog.TimestampFunc = utcNow
48
49 if features.Contains(features.FeatureManagementLogs) {
50 // Management logger needs to be initialized before any of the other loggers as to not capture
51 // it's own logging events.
52 ManagementLogger = management.NewLogger()
53 }
54}
55
56func utcNow() time.Time {
57 return time.Now().UTC()
58}
59
60func fallbackLogger(err error) *zerolog.Logger {
61 failLog := fallbacklog.With().Logger()
62 fallbacklog.Error().Msgf("Falling back to a default logger due to logger setup failure: %s", err)
63
64 return &failLog
65}
66
67// resilientMultiWriter is an alternative to zerolog's so that we can make it resilient to individual
68// writer's errors. E.g., when running as a Windows service, the console writer fails, but we don't want to
69// allow that to prevent all logging to fail due to breaking the for loop upon an error.
70type resilientMultiWriter struct {
71 level zerolog.Level
72 writers []io.Writer
73 managementWriter zerolog.LevelWriter
74}
75
76func (t resilientMultiWriter) Write(p []byte) (n int, err error) {
77 for _, w := range t.writers {
78 _, _ = w.Write(p)
79 }
80 if t.managementWriter != nil {
81 _, _ = t.managementWriter.Write(p)
82 }
83 return len(p), nil
84}
85
86func (t resilientMultiWriter) WriteLevel(level zerolog.Level, p []byte) (n int, err error) {
87 // Only write the event to normal writers if it exceeds the level, but always write to the
88 // management logger and let it decided with the provided level of the log event.
89 if t.level <= level {
90 for _, w := range t.writers {
91 _, _ = w.Write(p)
92 }
93 }
94 if t.managementWriter != nil {
95 _, _ = t.managementWriter.WriteLevel(level, p)
96 }
97 return len(p), nil
98}
99
100var levelErrorLogged = false
101
102func newZerolog(loggerConfig *Config) *zerolog.Logger {
103 var writers []io.Writer
104
105 if loggerConfig.ConsoleConfig != nil {
106 writers = append(writers, createConsoleLogger(*loggerConfig.ConsoleConfig))
107 }
108
109 if loggerConfig.FileConfig != nil {
110 fileLogger, err := createFileWriter(*loggerConfig.FileConfig)
111 if err != nil {
112 return fallbackLogger(err)
113 }
114
115 writers = append(writers, fileLogger)
116 }
117
118 if loggerConfig.RollingConfig != nil {
119 rollingLogger, err := createRollingLogger(*loggerConfig.RollingConfig)
120 if err != nil {
121 return fallbackLogger(err)
122 }
123
124 writers = append(writers, rollingLogger)
125 }
126
127 var managementWriter zerolog.LevelWriter
128 if features.Contains(features.FeatureManagementLogs) {
129 managementWriter = ManagementLogger
130 }
131
132 level, levelErr := zerolog.ParseLevel(loggerConfig.MinLevel)
133 if levelErr != nil {
134 level = zerolog.InfoLevel
135 }
136
137 multi := resilientMultiWriter{level, writers, managementWriter}
138 log := zerolog.New(multi).With().Timestamp().Logger()
139 if !levelErrorLogged && levelErr != nil {
140 log.Error().Msgf("Failed to parse log level %q, using %q instead", loggerConfig.MinLevel, level)
141 levelErrorLogged = true
142 }
143
144 return &log
145}
146
147func CreateTransportLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
148 return createFromContext(c, LogTransportLevelFlag, LogDirectoryFlag, disableTerminal)
149}
150
151func CreateLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
152 return createFromContext(c, LogLevelFlag, LogDirectoryFlag, disableTerminal)
153}
154
155func CreateSSHLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
156 return createFromContext(c, LogSSHLevelFlag, LogSSHDirectoryFlag, disableTerminal)
157}
158
159func createFromContext(
160 c *cli.Context,
161 logLevelFlagName,
162 logDirectoryFlagName string,
163 disableTerminal bool,
164) *zerolog.Logger {
165 logLevel := c.String(logLevelFlagName)
166 logFile := c.String(LogFileFlag)
167 logDirectory := c.String(logDirectoryFlagName)
168
169 loggerConfig := CreateConfig(
170 logLevel,
171 disableTerminal,
172 logDirectory,
173 logFile,
174 )
175
176 log := newZerolog(loggerConfig)
177 if incompatibleFlagsSet := logFile != "" && logDirectory != ""; incompatibleFlagsSet {
178 log.Error().Msgf("Your config includes values for both %s and %s, but they are incompatible. %s takes precedence.", LogFileFlag, logDirectoryFlagName, LogFileFlag)
179 }
180 return log
181}
182
183func Create(loggerConfig *Config) *zerolog.Logger {
184 if loggerConfig == nil {
185 loggerConfig = &Config{
186 defaultConfig.ConsoleConfig,
187 nil,
188 nil,
189 defaultConfig.MinLevel,
190 }
191 }
192 return newZerolog(loggerConfig)
193}
194
195func createConsoleLogger(config ConsoleConfig) io.Writer {
196 consoleOut := os.Stderr
197 return zerolog.ConsoleWriter{
198 Out: colorable.NewColorable(consoleOut),
199 NoColor: config.noColor || !term.IsTerminal(int(consoleOut.Fd())),
200 TimeFormat: consoleTimeFormat,
201 }
202}
203
204type fileInitializer struct {
205 once sync.Once
206 writer io.Writer
207 creationError error
208}
209
210var (
211 singleFileInit fileInitializer
212 rotatingFileInit fileInitializer
213)
214
215func createFileWriter(config FileConfig) (io.Writer, error) {
216 singleFileInit.once.Do(func() {
217
218 var logFile io.Writer
219 fullpath := config.Fullpath()
220
221 // Try to open the existing file
222 logFile, err := os.OpenFile(fullpath, os.O_APPEND|os.O_WRONLY, filePermMode)
223 if err != nil {
224 // If the existing file wasn't found, or couldn't be opened, just ignore
225 // it and recreate a new one.
226 logFile, err = createDirFile(config)
227 // If creating a new logfile fails, then we have no choice but to error out.
228 if err != nil {
229 singleFileInit.creationError = err
230 return
231 }
232 }
233
234 singleFileInit.writer = logFile
235 })
236
237 return singleFileInit.writer, singleFileInit.creationError
238}
239
240func createDirFile(config FileConfig) (io.Writer, error) {
241 if config.Dirname != "" {
242 err := os.MkdirAll(config.Dirname, dirPermMode)
243
244 if err != nil {
245 return nil, fmt.Errorf("unable to create directories for new logfile: %s", err)
246 }
247 }
248
249 mode := os.FileMode(filePermMode)
250
251 fullPath := filepath.Join(config.Dirname, config.Filename)
252 logFile, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, mode)
253 if err != nil {
254 return nil, fmt.Errorf("unable to create a new logfile: %s", err)
255 }
256
257 return logFile, nil
258}
259
260func createRollingLogger(config RollingConfig) (io.Writer, error) {
261 rotatingFileInit.once.Do(func() {
262 if err := os.MkdirAll(config.Dirname, dirPermMode); err != nil {
263 rotatingFileInit.creationError = err
264 return
265 }
266
267 rotatingFileInit.writer = &lumberjack.Logger{
268 Filename: path.Join(config.Dirname, config.Filename),
269 MaxBackups: config.maxBackups,
270 MaxSize: config.maxSize,
271 MaxAge: config.maxAge,
272 }
273 })
274
275 return rotatingFileInit.writer, rotatingFileInit.creationError
276}
277