diff --git a/cli/cli.go b/cli/cli.go index 009fe891103..4fb884f9b6b 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "os" + "strings" "github.com/arduino/arduino-cli/cli/board" "github.com/arduino/arduino-cli/cli/compile" @@ -51,8 +52,9 @@ var ( PersistentPreRun: preRun, } - verbose bool - logFile string + verbose bool + logFile string + logFormat string ) const ( @@ -80,6 +82,7 @@ func createCliCommandTree(cmd *cobra.Command) { cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the logs on the standard output.") cmd.PersistentFlags().StringVar(&globals.LogLevel, "log-level", defaultLogLevel, "Messages with this level and above will be logged (default: warn).") cmd.PersistentFlags().StringVar(&logFile, "log-file", "", "Path to the file where logs will be written.") + cmd.PersistentFlags().StringVar(&logFormat, "log-format", "text", "The output format for the logs, can be [text|json].") cmd.PersistentFlags().StringVar(&globals.OutputFormat, "format", "text", "The output format, can be [text|json].") cmd.PersistentFlags().StringVar(&globals.YAMLConfigFile, "config-file", "", "The custom config file (if not specified the default will be used).") cmd.PersistentFlags().StringSliceVar(&globals.AdditionalUrls, "additional-urls", []string{}, "Additional URLs for the board manager.") @@ -111,6 +114,10 @@ func parseFormatString(arg string) (feedback.OutputFormat, bool) { } func preRun(cmd *cobra.Command, args []string) { + // normalize the format strings + globals.OutputFormat = strings.ToLower(globals.OutputFormat) + logFormat = strings.ToLower(logFormat) + // should we log to file? if logFile != "" { file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) @@ -120,7 +127,11 @@ func preRun(cmd *cobra.Command, args []string) { } // we use a hook so we don't get color codes in the log file - logrus.AddHook(lfshook.NewHook(file, &logrus.TextFormatter{})) + if logFormat == "json" { + logrus.AddHook(lfshook.NewHook(file, &logrus.JSONFormatter{})) + } else { + logrus.AddHook(lfshook.NewHook(file, &logrus.TextFormatter{})) + } } // should we log to stdout? @@ -142,15 +153,21 @@ func preRun(cmd *cobra.Command, args []string) { logrus.SetLevel(lvl) } - // check the right format was passed - if f, found := parseFormatString(globals.OutputFormat); !found { + // set the Logger format + if logFormat == "json" { + logrus.SetFormatter(&logrus.JSONFormatter{}) + } + + // check the right output format was passed + format, found := parseFormatString(globals.OutputFormat) + if !found { feedback.Error("Invalid output format: " + globals.OutputFormat) os.Exit(errorcodes.ErrBadCall) - } else { - // use the format to configure the Feedback - feedback.SetFormat(f) } + // use the output format to configure the Feedback + feedback.SetFormat(format) + globals.InitConfigs() logrus.Info(globals.VersionInfo.Application + "-" + globals.VersionInfo.VersionString) diff --git a/test/test_main.py b/test/test_main.py index 3612b4e2f3a..bfdd903aa32 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -12,7 +12,9 @@ # otherwise use the software for commercial activities involving the Arduino # software without disclosing the source code of your own applications. To purchase # a commercial license, send an email to license@arduino.cc. +import os import json + import semver @@ -36,3 +38,37 @@ def test_version(run_command): assert parsed_out.get("Application", False) == "arduino-cli" assert isinstance(semver.parse(parsed_out.get("VersionString", False)), dict) assert isinstance(parsed_out.get("Commit", False), str) + + +def test_log_options(run_command, data_dir): + """ + using `version` as a test command + """ + + # no logs + out_lines = run_command("version").stdout.strip().split("\n") + assert len(out_lines) == 1 + + # plain text logs on stdoud + out_lines = run_command("version -v").stdout.strip().split("\n") + assert len(out_lines) > 1 + assert out_lines[0].startswith("\x1b[36mINFO\x1b[0m") # account for the colors + + # plain text logs on file + log_file = os.path.join(data_dir, "log.txt") + run_command("version --log-file " + log_file) + with open(log_file) as f: + lines = f.readlines() + assert lines[0].startswith('time="') # file format is different from console + + # json on stdout + out_lines = run_command("version -v --log-format JSON").stdout.strip().split("\n") + lg = json.loads(out_lines[0]) + assert "level" in lg + + # json on file + log_file = os.path.join(data_dir, "log.json") + run_command("version --log-format json --log-file " + log_file) + with open(log_file) as f: + for line in f.readlines(): + json.loads(line)