Skip to content

Commit aef7461

Browse files
author
Massimiliano Pippi
authored
Add an option to format logs with JSON (#378)
* add an option to format logs with JSON * added integration tests for log format * clean up format options
1 parent b035918 commit aef7461

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

Diff for: cli/cli.go

+25-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"io/ioutil"
2323
"os"
24+
"strings"
2425

2526
"github.com/arduino/arduino-cli/cli/board"
2627
"github.com/arduino/arduino-cli/cli/compile"
@@ -51,8 +52,9 @@ var (
5152
PersistentPreRun: preRun,
5253
}
5354

54-
verbose bool
55-
logFile string
55+
verbose bool
56+
logFile string
57+
logFormat string
5658
)
5759

5860
const (
@@ -80,6 +82,7 @@ func createCliCommandTree(cmd *cobra.Command) {
8082
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the logs on the standard output.")
8183
cmd.PersistentFlags().StringVar(&globals.LogLevel, "log-level", defaultLogLevel, "Messages with this level and above will be logged (default: warn).")
8284
cmd.PersistentFlags().StringVar(&logFile, "log-file", "", "Path to the file where logs will be written.")
85+
cmd.PersistentFlags().StringVar(&logFormat, "log-format", "text", "The output format for the logs, can be [text|json].")
8386
cmd.PersistentFlags().StringVar(&globals.OutputFormat, "format", "text", "The output format, can be [text|json].")
8487
cmd.PersistentFlags().StringVar(&globals.YAMLConfigFile, "config-file", "", "The custom config file (if not specified the default will be used).")
8588
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) {
111114
}
112115

113116
func preRun(cmd *cobra.Command, args []string) {
117+
// normalize the format strings
118+
globals.OutputFormat = strings.ToLower(globals.OutputFormat)
119+
logFormat = strings.ToLower(logFormat)
120+
114121
// should we log to file?
115122
if logFile != "" {
116123
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) {
120127
}
121128

122129
// we use a hook so we don't get color codes in the log file
123-
logrus.AddHook(lfshook.NewHook(file, &logrus.TextFormatter{}))
130+
if logFormat == "json" {
131+
logrus.AddHook(lfshook.NewHook(file, &logrus.JSONFormatter{}))
132+
} else {
133+
logrus.AddHook(lfshook.NewHook(file, &logrus.TextFormatter{}))
134+
}
124135
}
125136

126137
// should we log to stdout?
@@ -142,15 +153,21 @@ func preRun(cmd *cobra.Command, args []string) {
142153
logrus.SetLevel(lvl)
143154
}
144155

145-
// check the right format was passed
146-
if f, found := parseFormatString(globals.OutputFormat); !found {
156+
// set the Logger format
157+
if logFormat == "json" {
158+
logrus.SetFormatter(&logrus.JSONFormatter{})
159+
}
160+
161+
// check the right output format was passed
162+
format, found := parseFormatString(globals.OutputFormat)
163+
if !found {
147164
feedback.Error("Invalid output format: " + globals.OutputFormat)
148165
os.Exit(errorcodes.ErrBadCall)
149-
} else {
150-
// use the format to configure the Feedback
151-
feedback.SetFormat(f)
152166
}
153167

168+
// use the output format to configure the Feedback
169+
feedback.SetFormat(format)
170+
154171
globals.InitConfigs()
155172

156173
logrus.Info(globals.VersionInfo.Application + "-" + globals.VersionInfo.VersionString)

Diff for: test/test_main.py

+36
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# otherwise use the software for commercial activities involving the Arduino
1313
# software without disclosing the source code of your own applications. To purchase
1414
# a commercial license, send an email to [email protected].
15+
import os
1516
import json
17+
1618
import semver
1719

1820

@@ -36,3 +38,37 @@ def test_version(run_command):
3638
assert parsed_out.get("Application", False) == "arduino-cli"
3739
assert isinstance(semver.parse(parsed_out.get("VersionString", False)), dict)
3840
assert isinstance(parsed_out.get("Commit", False), str)
41+
42+
43+
def test_log_options(run_command, data_dir):
44+
"""
45+
using `version` as a test command
46+
"""
47+
48+
# no logs
49+
out_lines = run_command("version").stdout.strip().split("\n")
50+
assert len(out_lines) == 1
51+
52+
# plain text logs on stdoud
53+
out_lines = run_command("version -v").stdout.strip().split("\n")
54+
assert len(out_lines) > 1
55+
assert out_lines[0].startswith("\x1b[36mINFO\x1b[0m") # account for the colors
56+
57+
# plain text logs on file
58+
log_file = os.path.join(data_dir, "log.txt")
59+
run_command("version --log-file " + log_file)
60+
with open(log_file) as f:
61+
lines = f.readlines()
62+
assert lines[0].startswith('time="') # file format is different from console
63+
64+
# json on stdout
65+
out_lines = run_command("version -v --log-format JSON").stdout.strip().split("\n")
66+
lg = json.loads(out_lines[0])
67+
assert "level" in lg
68+
69+
# json on file
70+
log_file = os.path.join(data_dir, "log.json")
71+
run_command("version --log-format json --log-file " + log_file)
72+
with open(log_file) as f:
73+
for line in f.readlines():
74+
json.loads(line)

0 commit comments

Comments
 (0)