Repository: syncthing/syncthing (language: go)

Issue:
syncthing generate command writes the deviceId to stderr instead of stdout as INFO

  - syncthing version v1.22.1
  -  ubuntu 18

I had a script in which I was running `syncthing -generate=my_dir` and I was grabbing the `DevideId` from the output, after upgrading to new version of syncthing, that part of code broke, because apperantly now the deviceId is getting printed to stderr instead of stdout and my code cannot capture it. I did fix it by redirecting stderr to stdout. But it would be nice if I didn't have to do that since that is not actually an error.

the output of `syncthing -generate` before:
```
16:28:02 INFO: Device ID: 6C4FCZ7-N2OUG4Y-YYNLDGP-FSRR6RY-SQUUGRH-TIV4AAK-C3SDB62-Q4QBDQC
16:28:02 INFO: Default folder created and/or linked to new config
```

and the output I am getting now:
```
2022/11/16 16:34:33 INFO: Generating ECDSA key and certificate for syncthing...
2022/11/16 16:34:33 Device ID: 3IPJSF4-TWXI57O-P64DONH-DRCPFEL-JRWFFJT-4MLAFUR-CTJNSQG-U2JFZAO
2022/11/16 16:34:33 INFO: Default folder created and/or linked to new config
```
the first and third are INFo and are in stdout, but the middle one, devideId, is getting printed in stderr


Discussion / hints:
Huh. Which version is "before"?
This changed between 1.18.4 and 1.18.5, inadvertently, as part of moving the `generate` command.

Fix patch:
diff --git a/cmd/syncthing/generate/generate.go b/cmd/syncthing/generate/generate.go
index 9afb170dc5e..6b3d0b36902 100644
--- a/cmd/syncthing/generate/generate.go
+++ b/cmd/syncthing/generate/generate.go
@@ -13,7 +13,6 @@ import (
 	"crypto/tls"
 	"errors"
 	"fmt"
-	"log"
 	"os"
 
 	"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
@@ -21,6 +20,7 @@ import (
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/locations"
+	"github.com/syncthing/syncthing/lib/logger"
 	"github.com/syncthing/syncthing/lib/osutil"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/syncthing"
@@ -32,9 +32,7 @@ type CLI struct {
 	GUIPassword string `placeholder:"STRING" help:"Specify new GUI authentication password (use - to read from standard input)"`
 }
 
-func (c *CLI) Run() error {
-	log.SetFlags(0)
-
+func (c *CLI) Run(l logger.Logger) error {
 	if c.HideConsole {
 		osutil.HideConsole()
 	}
@@ -59,13 +57,13 @@ func (c *CLI) Run() error {
 		c.GUIPassword = string(password)
 	}
 
-	if err := Generate(c.ConfDir, c.GUIUser, c.GUIPassword, c.NoDefaultFolder, c.SkipPortProbing); err != nil {
+	if err := Generate(l, c.ConfDir, c.GUIUser, c.GUIPassword, c.NoDefaultFolder, c.SkipPortProbing); err != nil {
 		return fmt.Errorf("failed to generate config and keys: %w", err)
 	}
 	return nil
 }
 
-func Generate(confDir, guiUser, guiPassword string, noDefaultFolder, skipPortProbing bool) error {
+func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFolder, skipPortProbing bool) error {
 	dir, err := fs.ExpandTilde(confDir)
 	if err != nil {
 		return err
@@ -80,7 +78,7 @@ func Generate(confDir, guiUser, guiPassword string, noDefaultFolder, skipPortPro
 	certFile, keyFile := locations.Get(locations.CertFile), locations.Get(locations.KeyFile)
 	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
 	if err == nil {
-		log.Println("WARNING: Key exists; will not overwrite.")
+		l.Warnln("Key exists; will not overwrite.")
 	} else {
 		cert, err = syncthing.GenerateCertificate(certFile, keyFile)
 		if err != nil {
@@ -88,7 +86,7 @@ func Generate(confDir, guiUser, guiPassword string, noDefaultFolder, skipPortPro
 		}
 	}
 	myID = protocol.NewDeviceID(cert.Certificate[0])
-	log.Println("Device ID:", myID)
+	l.Infoln("Device ID:", myID)
 
 	cfgFile := locations.Get(locations.ConfigFile)
 	cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger)
@@ -106,7 +104,7 @@ func Generate(confDir, guiUser, guiPassword string, noDefaultFolder, skipPortPro
 
 	var updateErr error
 	waiter, err := cfg.Modify(func(cfg *config.Configuration) {
-		updateErr = updateGUIAuthentication(&cfg.GUI, guiUser, guiPassword)
+		updateErr = updateGUIAuthentication(l, &cfg.GUI, guiUser, guiPassword)
 	})
 	if err != nil {
 		return fmt.Errorf("modify config: %w", err)
@@ -122,17 +120,17 @@ func Generate(confDir, guiUser, guiPassword string, noDefaultFolder, skipPortPro
 	return nil
 }
 
-func updateGUIAuthentication(guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
+func updateGUIAuthentication(l logger.Logger, guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
 	if guiUser != "" && guiCfg.User != guiUser {
 		guiCfg.User = guiUser
-		log.Println("Updated GUI authentication user name:", guiUser)
+		l.Infoln("Updated GUI authentication user name:", guiUser)
 	}
 
 	if guiPassword != "" && guiCfg.Password != guiPassword {
 		if err := guiCfg.HashAndSetPassword(guiPassword); err != nil {
 			return fmt.Errorf("failed to set GUI authentication password: %w", err)
 		}
-		log.Println("Updated GUI authentication password.")
+		l.Infoln("Updated GUI authentication password.")
 	}
 	return nil
 }
diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go
index 3d6255a342e..5690440cfd0 100644
--- a/cmd/syncthing/main.go
+++ b/cmd/syncthing/main.go
@@ -251,6 +251,7 @@ func main() {
 
 	ctx, err := parser.Parse(args)
 	parser.FatalIfErrorf(err)
+	ctx.BindTo(l, (*logger.Logger)(nil)) // main logger available to subcommands
 	err = ctx.Run()
 	parser.FatalIfErrorf(err)
 }
@@ -345,7 +346,7 @@ func (options serveOptions) Run() error {
 	}
 
 	if options.GenerateDir != "" {
-		if err := generate.Generate(options.GenerateDir, "", "", options.NoDefaultFolder, options.SkipPortProbing); err != nil {
+		if err := generate.Generate(l, options.GenerateDir, "", "", options.NoDefaultFolder, options.SkipPortProbing); err != nil {
 			l.Warnln("Failed to generate config and keys:", err)
 			os.Exit(svcutil.ExitError.AsInt())
 		}


Test patch: