Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to format logs with JSON #378

Merged
merged 3 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/arduino/arduino-cli/cli/board"
"github.com/arduino/arduino-cli/cli/compile"
Expand Down Expand Up @@ -51,8 +52,9 @@ var (
PersistentPreRun: preRun,
}

verbose bool
logFile string
verbose bool
logFile string
logFormat string
)

const (
Expand Down Expand Up @@ -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.")
Expand Down Expand Up @@ -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)
Expand All @@ -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?
Expand All @@ -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)
Expand Down
36 changes: 36 additions & 0 deletions test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected].
import os
import json

import semver


Expand All @@ -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)