From cad58b9b57165bb74cc89d357f9ec467ee0a1f9a Mon Sep 17 00:00:00 2001 From: Areg Harutyunyan Date: Sun, 15 Nov 2020 01:49:44 +0000 Subject: [PATCH] TUN-3561: Unified logger configuration --- cmd/cloudflared/access/carrier.go | 16 +-- cmd/cloudflared/access/cmd.go | 28 ++--- cmd/cloudflared/app_service.go | 1 + cmd/cloudflared/config/configuration.go | 29 ----- cmd/cloudflared/linux_service.go | 4 +- cmd/cloudflared/macos_service.go | 8 +- cmd/cloudflared/main.go | 15 +-- cmd/cloudflared/tunnel/cmd.go | 48 +------- cmd/cloudflared/tunnel/login.go | 2 +- cmd/cloudflared/tunnel/subcommand_context.go | 2 +- cmd/cloudflared/updater/update.go | 2 +- cmd/cloudflared/windows_service.go | 4 +- dbconnect/proxy.go | 2 +- logger/configuration.go | 118 +++++++++++++++++++ logger/create.go | 71 +++++++++++ tunneldns/tunnel.go | 4 +- 16 files changed, 230 insertions(+), 124 deletions(-) create mode 100644 logger/configuration.go diff --git a/cmd/cloudflared/access/carrier.go b/cmd/cloudflared/access/carrier.go index 435ef303..87a4584e 100644 --- a/cmd/cloudflared/access/carrier.go +++ b/cmd/cloudflared/access/carrier.go @@ -11,7 +11,7 @@ import ( "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/validation" "github.com/pkg/errors" - cli "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) // StartForwarder starts a client side websocket forward @@ -52,19 +52,7 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, logger // useful for proxying other protocols (like ssh) over websockets // (which you can put Access in front of) func ssh(c *cli.Context) error { - logDirectory, logLevel := config.FindLogSettings() - - flagLogDirectory := c.String(sshLogDirectoryFlag) - if flagLogDirectory != "" { - logDirectory = flagLogDirectory - } - - flagLogLevel := c.String(sshLogLevelFlag) - if flagLogLevel != "" { - logLevel = flagLogLevel - } - - logger, err := logger.New(logger.DefaultFile(logDirectory), logger.LogLevelString(logLevel)) + logger, err := logger.CreateSSHLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return cliutil.PrintLoggerSetupError("error setting up logger", err) } diff --git a/cmd/cloudflared/access/cmd.go b/cmd/cloudflared/access/cmd.go index cd161678..20f70816 100644 --- a/cmd/cloudflared/access/cmd.go +++ b/cmd/cloudflared/access/cmd.go @@ -25,16 +25,14 @@ import ( ) const ( - sshHostnameFlag = "hostname" - sshDestinationFlag = "destination" - sshURLFlag = "url" - sshHeaderFlag = "header" - sshTokenIDFlag = "service-token-id" - sshTokenSecretFlag = "service-token-secret" - sshGenCertFlag = "short-lived-cert" - sshLogDirectoryFlag = "log-directory" - sshLogLevelFlag = "log-level" - sshConfigTemplate = ` + sshHostnameFlag = "hostname" + sshDestinationFlag = "destination" + sshURLFlag = "url" + sshHeaderFlag = "header" + sshTokenIDFlag = "service-token-id" + sshTokenSecretFlag = "service-token-secret" + sshGenCertFlag = "short-lived-cert" + sshConfigTemplate = ` Add to your {{.Home}}/.ssh/config: Host {{.Hostname}} @@ -157,12 +155,12 @@ func Commands() []*cli.Command { Usage: "specify an Access service token secret you wish to use.", }, &cli.StringFlag{ - Name: sshLogDirectoryFlag, + Name: logger.LogSSHDirectoryFlag, Aliases: []string{"logfile"}, //added to match the tunnel side Usage: "Save application log to this directory for reporting issues.", }, &cli.StringFlag{ - Name: sshLogLevelFlag, + Name: logger.LogSSHLevelFlag, Aliases: []string{"loglevel"}, //added to match the tunnel side Usage: "Application logging level {fatal, error, info, debug}. ", }, @@ -207,7 +205,7 @@ func login(c *cli.Context) error { return err } - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } @@ -252,7 +250,7 @@ func curl(c *cli.Context) error { if err := raven.SetDSN(sentryDSN); err != nil { return err } - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } @@ -331,7 +329,7 @@ func sshConfig(c *cli.Context) error { // sshGen generates a short lived certificate for provided hostname func sshGen(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } diff --git a/cmd/cloudflared/app_service.go b/cmd/cloudflared/app_service.go index d2c0b0c3..5f5197d0 100644 --- a/cmd/cloudflared/app_service.go +++ b/cmd/cloudflared/app_service.go @@ -37,6 +37,7 @@ func (s *AppService) Run() error { func (s *AppService) Shutdown() error { s.configManager.Shutdown() s.shutdownC <- struct{}{} + return nil } diff --git a/cmd/cloudflared/config/configuration.go b/cmd/cloudflared/config/configuration.go index e75e45a2..ce27505e 100644 --- a/cmd/cloudflared/config/configuration.go +++ b/cmd/cloudflared/config/configuration.go @@ -151,35 +151,6 @@ func FindOrCreateConfigPath() string { return path } -// FindLogSettings gets the log directory and level from the config file -func FindLogSettings() (string, string) { - configPath := FindOrCreateConfigPath() - defaultDirectory := DefaultLogDirectory() - defaultLevel := "info" - - file, err := os.Open(configPath) - if err != nil { - return defaultDirectory, defaultLevel - } - defer file.Close() - - var config Root - if err := yaml.NewDecoder(file).Decode(&config); err != nil { - return defaultDirectory, defaultLevel - } - - directory := defaultDirectory - if config.LogDirectory != "" { - directory = config.LogDirectory - } - - level := defaultLevel - if config.LogLevel != "" { - level = config.LogLevel - } - return directory, level -} - // ValidateUnixSocket ensures --unix-socket param is used exclusively // i.e. it fails if a user specifies both --url and --unix-socket func ValidateUnixSocket(c *cli.Context) (string, error) { diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index 3e04c704..a68751ea 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -211,7 +211,7 @@ func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile str } func installLinuxService(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } @@ -325,7 +325,7 @@ func installSysv(templateArgs *ServiceTemplateArgs, logger logger.Service) error } func uninstallLinuxService(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go index 43f65b20..0970fa04 100644 --- a/cmd/cloudflared/macos_service.go +++ b/cmd/cloudflared/macos_service.go @@ -6,11 +6,11 @@ import ( "fmt" "os" - "github.com/urfave/cli/v2" - "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/logger" + "github.com/pkg/errors" + "github.com/urfave/cli/v2" ) const ( @@ -107,7 +107,7 @@ func stderrPath() (string, error) { } func installLaunchd(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } @@ -163,7 +163,7 @@ func installLaunchd(c *cli.Context) error { } func uninstallLaunchd(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index 44b1028e..0be2952c 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -15,10 +15,9 @@ import ( "github.com/cloudflare/cloudflared/overwatch" "github.com/cloudflare/cloudflared/tunneldns" "github.com/cloudflare/cloudflared/watcher" - - raven "github.com/getsentry/raven-go" - homedir "github.com/mitchellh/go-homedir" - cli "github.com/urfave/cli/v2" + "github.com/getsentry/raven-go" + "github.com/mitchellh/go-homedir" + "github.com/urfave/cli/v2" "github.com/pkg/errors" ) @@ -147,7 +146,7 @@ func isEmptyInvocation(c *cli.Context) bool { func action(version string, shutdownC, graceShutdownC chan struct{}) cli.ActionFunc { return cliutil.ErrorHandler(func(c *cli.Context) (err error) { if isEmptyInvocation(c) { - return handleServiceMode(shutdownC) + return handleServiceMode(c, shutdownC) } tags := make(map[string]string) tags["hostname"] = c.String("hostname") @@ -184,15 +183,13 @@ func captureError(err error) { } // cloudflared was started without any flags -func handleServiceMode(shutdownC chan struct{}) error { +func handleServiceMode(c *cli.Context, shutdownC chan struct{}) error { defer log.SharedWriteManager.Shutdown() - logDirectory, logLevel := config.FindLogSettings() - logger, err := log.New(log.DefaultFile(logDirectory), log.LogLevelString(logLevel)) + logger, err := log.CreateLoggerFromContext(c, log.DisableTerminalLog) if err != nil { return cliutil.PrintLoggerSetupError("error setting up logger", err) } - logger.Infof("logging to directory: %s", logDirectory) // start the main run loop that reads from the config file f, err := watcher.NewFile() diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 8667146d..58f963a8 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -77,8 +77,6 @@ const ( // uiFlag is to enable launching cloudflared in interactive UI mode uiFlag = "ui" - logDirectoryFlag = "log-directory" - debugLevelWarning = "At debug level, request URL, method, protocol, content legnth and header will be logged. " + "Response status, content length and header will also be logged in debug level." ) @@ -209,40 +207,6 @@ func routeFromFlag(c *cli.Context) (tunnelstore.Route, bool) { return nil, false } -func createLogger(c *cli.Context, isTransport bool, disableTerminal bool) (*logger.OutputWriter, error) { - var loggerOpts []logger.Option - - logPath := c.String("logfile") - if logPath == "" { - logPath = c.String(logDirectoryFlag) - } - - if logPath != "" { - loggerOpts = append(loggerOpts, logger.DefaultFile(logPath)) - } - - logLevel := c.String("loglevel") - if isTransport { - logLevel = c.String("transport-loglevel") - if logLevel == "" { - logLevel = "fatal" - } - } - loggerOpts = append(loggerOpts, logger.LogLevelString(logLevel)) - - if disableTerminal { - disableOption := logger.DisableTerminal(true) - loggerOpts = append(loggerOpts, disableOption) - } - - l, err := logger.New(loggerOpts...) - if err != nil { - return nil, err - } - - return l, nil -} - func StartServer( c *cli.Context, version string, @@ -362,7 +326,7 @@ func StartServer( return fmt.Errorf(errText) } - transportLogger, err := createLogger(c, true, isUIEnabled) + transportLogger, err := logger.CreateTransportLoggerFromContext(c, isUIEnabled) if err != nil { return errors.Wrap(err, "error setting up transport logger") } @@ -416,7 +380,7 @@ func forceSetFlag(c *cli.Context, name, value string) { func SetFlagsFromConfigFile(c *cli.Context) error { const exitCode = 1 - log, err := createLogger(c, false, false) + log, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return cliutil.PrintLoggerSetupError("error setting up logger", err) } @@ -974,14 +938,14 @@ func sshFlags(shouldHide bool) []cli.Flag { func configureLoggingFlags(shouldHide bool) []cli.Flag { return []cli.Flag{ altsrc.NewStringFlag(&cli.StringFlag{ - Name: "loglevel", + Name: logger.LogLevelFlag, Value: "info", Usage: "Application logging level {fatal, error, info, debug}. " + debugLevelWarning, EnvVars: []string{"TUNNEL_LOGLEVEL"}, Hidden: shouldHide, }), altsrc.NewStringFlag(&cli.StringFlag{ - Name: "transport-loglevel", + Name: logger.LogTransportLevelFlag, Aliases: []string{"proto-loglevel"}, // This flag used to be called proto-loglevel Value: "info", Usage: "Transport logging level(previously called protocol logging level) {fatal, error, info, debug}", @@ -989,13 +953,13 @@ func configureLoggingFlags(shouldHide bool) []cli.Flag { Hidden: shouldHide, }), altsrc.NewStringFlag(&cli.StringFlag{ - Name: "logfile", + Name: logger.LogFileFlag, Usage: "Save application log to this file for reporting issues.", EnvVars: []string{"TUNNEL_LOGFILE"}, Hidden: shouldHide, }), altsrc.NewStringFlag(&cli.StringFlag{ - Name: logDirectoryFlag, + Name: logger.LogDirectoryFlag, Usage: "Save application log to this directory for reporting issues.", EnvVars: []string{"TUNNEL_LOGDIRECTORY"}, Hidden: shouldHide, diff --git a/cmd/cloudflared/tunnel/login.go b/cmd/cloudflared/tunnel/login.go index 48cba5f3..1f2aee2a 100644 --- a/cmd/cloudflared/tunnel/login.go +++ b/cmd/cloudflared/tunnel/login.go @@ -39,7 +39,7 @@ func buildLoginSubcommand(hidden bool) *cli.Command { } func login(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } diff --git a/cmd/cloudflared/tunnel/subcommand_context.go b/cmd/cloudflared/tunnel/subcommand_context.go index 130f4fa6..f5f3f4f8 100644 --- a/cmd/cloudflared/tunnel/subcommand_context.go +++ b/cmd/cloudflared/tunnel/subcommand_context.go @@ -46,7 +46,7 @@ func newSubcommandContext(c *cli.Context) (*subcommandContext, error) { isUIEnabled := c.IsSet(uiFlag) && c.String("name") != "" // If UI is enabled, terminal log output should be disabled -- log should be written into a UI log window instead - logger, err := createLogger(c, false, isUIEnabled) + logger, err := logger.CreateLoggerFromContext(c, isUIEnabled) if err != nil { return nil, errors.Wrap(err, "error setting up logger") } diff --git a/cmd/cloudflared/updater/update.go b/cmd/cloudflared/updater/update.go index 3ed86d45..30fa7bc7 100644 --- a/cmd/cloudflared/updater/update.go +++ b/cmd/cloudflared/updater/update.go @@ -114,7 +114,7 @@ func checkForUpdateAndApply(options updateOptions) UpdateOutcome { // Update is the handler for the update command from the command line func Update(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index 7b21e7f7..109ce696 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -173,7 +173,7 @@ func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeReques } func installWindowsService(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } @@ -219,7 +219,7 @@ func installWindowsService(c *cli.Context) error { } func uninstallWindowsService(c *cli.Context) error { - logger, err := logger.New() + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return errors.Wrap(err, "error setting up logger") } diff --git a/dbconnect/proxy.go b/dbconnect/proxy.go index fbdfb0b9..fc45676e 100644 --- a/dbconnect/proxy.go +++ b/dbconnect/proxy.go @@ -43,7 +43,7 @@ func NewInsecureProxy(ctx context.Context, origin string) (*Proxy, error) { return nil, errors.Wrap(err, "could not connect to the database") } - logger, err := logger.New() + logger, err := logger.New() // TODO: Does not obey log configuration if err != nil { return nil, errors.Wrap(err, "error setting up logger") } diff --git a/logger/configuration.go b/logger/configuration.go new file mode 100644 index 00000000..0b38e4fe --- /dev/null +++ b/logger/configuration.go @@ -0,0 +1,118 @@ +package logger + +var defaultConfig = createDefaultConfig() + +// Logging configuration +type Config struct { + ConsoleConfig *ConsoleConfig // If nil, the logger will not log into the console + FileConfig *FileConfig // If nil, the logger will not use an individual log file + RollingConfig *RollingConfig // If nil, the logger will not use a rolling log + + MinLevel string // debug | info | error | fatal +} + +type ConsoleConfig struct { + noColor bool +} + +type FileConfig struct { + Filepath string +} + +type RollingConfig struct { + Directory string + Filename string + + maxSize int // megabytes + maxBackups int // files + maxAge int // days +} + +func createDefaultConfig() Config { + const minLevel = "fatal" + + const RollingMaxSize = 1 // Mb + const RollingMaxBackups = 5 // files + const RollingMaxAge = 0 // Keep forever + const rollingLogFilename = "cloudflared.log" + + return Config{ + ConsoleConfig: &ConsoleConfig{ + noColor: false, + }, + FileConfig: &FileConfig{ + Filepath: "", + }, + RollingConfig: &RollingConfig{ + Directory: "", + Filename: rollingLogFilename, + maxSize: RollingMaxSize, + maxBackups: RollingMaxBackups, + maxAge: RollingMaxAge, + }, + MinLevel: minLevel, + } +} + +func CreateConfig( + minLevel string, + disableTerminal bool, + rollingLogPath, nonRollingLogFilePath string, +) *Config { + var console *ConsoleConfig + if !disableTerminal { + console = createConsoleConfig() + } + + var file *FileConfig + if nonRollingLogFilePath != "" { + file = createFileConfig(nonRollingLogFilePath) + } + + var rolling *RollingConfig + if rollingLogPath != "" { + rolling = createRollingConfig(rollingLogPath) + } + + if minLevel == "" { + minLevel = defaultConfig.MinLevel + } + + return &Config{ + ConsoleConfig: console, + FileConfig: file, + RollingConfig: rolling, + + MinLevel: minLevel, + } +} + +func createConsoleConfig() *ConsoleConfig { + return &ConsoleConfig{ + noColor: false, + } +} + +func createFileConfig(filepath string) *FileConfig { + if filepath == "" { + filepath = defaultConfig.FileConfig.Filepath + } + + return &FileConfig{ + Filepath: filepath, + } +} + +func createRollingConfig(directory string) *RollingConfig { + if directory == "" { + directory = defaultConfig.RollingConfig.Directory + } + + return &RollingConfig{ + Directory: directory, + Filename: defaultConfig.RollingConfig.Filename, + maxSize: defaultConfig.RollingConfig.maxSize, + maxBackups: defaultConfig.RollingConfig.maxBackups, + maxAge: defaultConfig.RollingConfig.maxAge, + } +} diff --git a/logger/create.go b/logger/create.go index c01702cb..03cf3c65 100644 --- a/logger/create.go +++ b/logger/create.go @@ -8,6 +8,20 @@ import ( "time" "github.com/alecthomas/units" + "github.com/urfave/cli/v2" +) + +const ( + EnableTerminalLog = false + DisableTerminalLog = true + + LogLevelFlag = "loglevel" + LogFileFlag = "logfile" + LogDirectoryFlag = "log-directory" + LogTransportLevelFlag = "transport-loglevel" + + LogSSHDirectoryFlag = "log-directory" + LogSSHLevelFlag = "log-level" ) // Option is to encaspulate actions that will be called by Parse and run later to build an Options struct @@ -127,6 +141,63 @@ func New(opts ...Option) (*OutputWriter, error) { return l, nil } +func NewInHouse(loggerConfig *Config) (*OutputWriter, error) { + var loggerOpts []Option + + var logPath string + if loggerConfig.FileConfig != nil { + logPath = loggerConfig.FileConfig.Filepath + } + if logPath == "" && loggerConfig.RollingConfig != nil { + logPath = loggerConfig.RollingConfig.Directory + } + + if logPath != "" { + loggerOpts = append(loggerOpts, DefaultFile(logPath)) + } + + loggerOpts = append(loggerOpts, LogLevelString(loggerConfig.MinLevel)) + + if loggerConfig.ConsoleConfig == nil { + disableOption := DisableTerminal(true) + loggerOpts = append(loggerOpts, disableOption) + } + + l, err := New(loggerOpts...) + if err != nil { + return nil, err + } + + return l, nil +} + +func CreateTransportLoggerFromContext(c *cli.Context, disableTerminal bool) (*OutputWriter, error) { + return createFromContext(c, LogTransportLevelFlag, LogDirectoryFlag, disableTerminal) +} + +func CreateLoggerFromContext(c *cli.Context, disableTerminal bool) (*OutputWriter, error) { + return createFromContext(c, LogLevelFlag, LogDirectoryFlag, disableTerminal) +} + +func CreateSSHLoggerFromContext(c *cli.Context, disableTerminal bool) (*OutputWriter, error) { + return createFromContext(c, LogSSHLevelFlag, LogSSHDirectoryFlag, disableTerminal) +} + +func createFromContext( + c *cli.Context, + logLevelFlagName, + logDirectoryFlagName string, + disableTerminal bool, +) (*OutputWriter, error) { + logLevel := c.String(logLevelFlagName) + logFile := c.String(LogFileFlag) + logDirectory := c.String(logDirectoryFlagName) + + loggerConfig := CreateConfig(logLevel, disableTerminal, logDirectory, logFile) + + return NewInHouse(loggerConfig) +} + // ParseLevelString returns the expected log levels based on the cmd flag func ParseLevelString(lvl string) ([]Level, error) { switch strings.ToLower(lvl) { diff --git a/tunneldns/tunnel.go b/tunneldns/tunnel.go index d0524f6d..f7bd6971 100644 --- a/tunneldns/tunnel.go +++ b/tunneldns/tunnel.go @@ -9,7 +9,6 @@ import ( "syscall" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" - "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/metrics" @@ -71,8 +70,7 @@ func Command(hidden bool) *cli.Command { // Run implements a foreground runner func Run(c *cli.Context) error { - logDirectory, logLevel := config.FindLogSettings() - logger, err := logger.New(logger.DefaultFile(logDirectory), logger.LogLevelString(logLevel)) + logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if err != nil { return cliutil.PrintLoggerSetupError("error setting up logger", err) }