From f6547d0576d58f27161aaa4135fcbb5881c511f1 Mon Sep 17 00:00:00 2001 From: Juergen Kellerer Date: Sun, 29 Oct 2023 18:05:01 +0100 Subject: [PATCH] logging: allow to setup default log output in global --- commands_display.go | 9 -- config/global.go | 1 + docs/content/configuration/logs/_index.md | 49 ++++++++++ main.go | 113 ++++++++++++---------- 4 files changed, 110 insertions(+), 62 deletions(-) diff --git a/commands_display.go b/commands_display.go index e569c814..9215f56b 100644 --- a/commands_display.go +++ b/commands_display.go @@ -158,15 +158,6 @@ func displayResticHelp(output io.Writer, configuration *config.Config, flags com out, closer := displayWriter(output, flags) defer closer() - // try to load the config - if configuration == nil { - if file, err := filesearch.FindConfigurationFile(flags.config); err == nil { - if configuration, err = config.LoadFile(file, flags.format); err != nil { - configuration = nil - } - } - } - resticBinary := "" if configuration != nil { if section, err := configuration.GetGlobalSection(); err == nil { diff --git a/config/global.go b/config/global.go index 6440e153..7e8b4db5 100644 --- a/config/global.go +++ b/config/global.go @@ -23,6 +23,7 @@ type Global struct { ShellBinary []string `mapstructure:"shell" default:"auto" examples:"sh;bash;pwsh;powershell;cmd" description:"The shell that is used to run commands (default is OS specific)"` MinMemory uint64 `mapstructure:"min-memory" default:"100" description:"Minimum available memory (in MB) required to run any commands - see https://creativeprojects.github.io/resticprofile/usage/memory/"` Scheduler string `mapstructure:"scheduler" description:"Leave blank for the default scheduler or use \"crond\" to select cron on supported operating systems"` + Log string `mapstructure:"log" default:"" description:"Sets the default log destination to be used if not specified in '--log' or 'schedule-log' - see https://creativeprojects.github.io/resticprofile/configuration/logs/"` LegacyArguments bool `mapstructure:"legacy-arguments" default:"false" deprecated:"0.20.0" description:"Legacy, broken arguments mode of resticprofile before version 0.15"` SystemdUnitTemplate string `mapstructure:"systemd-unit-template" default:"" description:"File containing the go template to generate a systemd unit - see https://creativeprojects.github.io/resticprofile/schedules/systemd/"` SystemdTimerTemplate string `mapstructure:"systemd-timer-template" default:"" description:"File containing the go template to generate a systemd timer - see https://creativeprojects.github.io/resticprofile/schedules/systemd/"` diff --git a/docs/content/configuration/logs/_index.md b/docs/content/configuration/logs/_index.md index 4f5c938d..81738d66 100644 --- a/docs/content/configuration/logs/_index.md +++ b/docs/content/configuration/logs/_index.md @@ -11,6 +11,7 @@ You can redirect the logs to a local file, a temporary file or a syslog server. ## Destination The log destination syntax is a such: +* `-` {{% icon icon="arrow-right" %}} redirects all the logs to the console / stdout (is the default log destination) * `filename` {{% icon icon="arrow-right" %}} redirects all the logs to the local file called **filename** * `temp:filename` {{% icon icon="arrow-right" %}} redirects all the logs to a temporary file available during the whole session, and deleted afterwards. * `tcp://syslog_server:514` or `udp://syslog_server:514` {{% icon icon="arrow-right" %}} redirects all the logs to the **syslog** server. @@ -19,6 +20,54 @@ The log destination syntax is a such: If the location cannot be opened, **resticprofile** will default to send the logs to the console. {{% /notice %}} +## Default + +You can adjust the default log destination in the `global` section: + +{{< tabs groupid="config-with-json" >}} +{{% tab title="toml" %}} + +```toml +version = "1" + +[global] +log = "resticprofile.log" +``` + +{{% /tab %}} +{{% tab title="yaml" %}} + +```yaml +version: "1" + +global: + log: "resticprofile.log" +``` + +{{% /tab %}} +{{% tab title="hcl" %}} + +```hcl +"global" { + "log" = "resticprofile.log" +} +``` + +{{% /tab %}} +{{% tab title="json" %}} + +```json +{ + "version": "1", + "global": { + "log": "resticprofile.log" + } +} +``` + +{{% /tab %}} +{{< /tabs >}} + ## Command line You can redirect the logs on the command line with the `--log` flag: diff --git a/main.go b/main.go index f10a139c..e14979a5 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "math/rand" "os" @@ -75,42 +76,41 @@ func main() { } // help - if flags.help || flagErr == pflag.ErrHelp { + if flags.help || errors.Is(flagErr, pflag.ErrHelp) { _ = displayHelpCommand(os.Stdout, commandRequest{ownCommands: ownCommands, flags: flags, args: args}) return } - // setting up the logger - we can start logging right after - if flags.isChild { - // use a remote logger - client := remote.NewClient(flags.parentPort) - setupRemoteLogger(flags, client) - - // also redirect the terminal through the client - term.SetAllOutput(term.NewRemoteTerm(client)) + // logger setup logic - is delayed until config was loaded (or attempted) + setupLogging := func(global *config.Global) (logCloser func()) { + logCloser = func() {} - // If this is running in elevated mode we'll need to send a finished signal if flags.isChild { - defer func(port int) { - client := remote.NewClient(port) - client.Done() - }(flags.parentPort) - } + // use a remote logger + client := remote.NewClient(flags.parentPort) + logCloser = func() { _ = client.Done() } + setupRemoteLogger(flags, client) - } else if flags.log != "" { - handle, err := setupTargetLogger(flags) - if err != nil { - // back to a console logger - setupConsoleLogger(flags) - clog.Errorf("cannot open log target: %s", err) + // also redirect the terminal through the client + term.SetAllOutput(term.NewRemoteTerm(client)) } else { - // close the log file at the end - defer handle.Close() + if flags.log == "" && global != nil { + flags.log = global.Log + } + if flags.log != "" && flags.log != "-" { + if closer, err := setupTargetLogger(flags); err == nil { + logCloser = func() { _ = closer.Close() } + } else { + // fallback to a console logger + setupConsoleLogger(flags) + clog.Errorf("cannot open log target: %s", err) + } + } else { + // use the console logger + setupConsoleLogger(flags) + } } - - } else { - // Use the console logger - setupConsoleLogger(flags) + return } // keep this one last if possible (so it will be first at the end) @@ -118,10 +118,13 @@ func main() { banner() - // resticprofile own commands (configuration file NOT loaded) + // resticprofile own commands (configuration file may NOT be loaded) if len(flags.resticArgs) > 0 { if ownCommands.Exists(flags.resticArgs[0], false) { - err = ownCommands.Run(nil, flags.resticArgs[0], flags, flags.resticArgs[1:]) + // try to load the config and setup logging for own command + configuration, global, _ := loadConfig(flags, true) + defer setupLogging(global)() + err = ownCommands.Run(configuration, flags.resticArgs[0], flags, flags.resticArgs[1:]) if err != nil { clog.Error(err) exitCode = 1 @@ -131,6 +134,15 @@ func main() { } } + // Load the mandatory configuration and setup logging (before returning on error) + c, global, err := loadConfig(flags, false) + defer setupLogging(global)() + if err != nil { + clog.Error(err) + exitCode = 1 + return + } + // check if we're running on battery if flags.ignoreOnBattery > 0 && flags.ignoreOnBattery <= constants.BatteryFull { battery, charge, err := IsRunningOnBattery() @@ -152,30 +164,6 @@ func main() { } } - configFile, err := filesearch.FindConfigurationFile(flags.config) - if err != nil { - clog.Error(err) - exitCode = 1 - return - } - if configFile != flags.config { - clog.Infof("using configuration file: %s", configFile) - } - - c, err := config.LoadFile(configFile, flags.format) - if err != nil { - clog.Errorf("cannot load configuration file: %v", err) - exitCode = 1 - return - } - - global, err := c.GetGlobalSection() - if err != nil { - clog.Errorf("cannot load global configuration: %v", err) - exitCode = 1 - return - } - // prevent computer from sleeping var caffeinate *preventsleep.Caffeinate if global.PreventSleep { @@ -309,6 +297,25 @@ func banner() { clog.Debugf("resticprofile %s compiled with %s", version, runtime.Version()) } +func loadConfig(flags commandLineFlags, silent bool) (cfg *config.Config, global *config.Global, err error) { + var configFile string + if configFile, err = filesearch.FindConfigurationFile(flags.config); err == nil { + if configFile != flags.config && !silent { + clog.Infof("using configuration file: %s", configFile) + } + + if cfg, err = config.LoadFile(configFile, flags.format); err == nil { + global, err = cfg.GetGlobalSection() + if err != nil { + err = fmt.Errorf("cannot load global configuration: %w", err) + } + } else { + err = fmt.Errorf("cannot load configuration file: %w", err) + } + } + return +} + func setPriority(nice int, class string) error { var err error