Adding JSON logging format and simplifying/centralizing spinner output

This commit is contained in:
Phil Kedy 2021-03-16 16:26:28 -04:00
parent a888f5a818
commit 47d369d490
4 changed files with 111 additions and 43 deletions

View File

@ -11,6 +11,7 @@ import (
"strings"
"github.com/dapr/cli/pkg/api"
"github.com/dapr/cli/pkg/print"
"github.com/dapr/cli/pkg/standalone"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -31,6 +32,8 @@ var RootCmd = &cobra.Command{
Distributed Application Runtime`,
}
var logAsJSON bool
// Execute adds all child commands to the root command.
func Execute(version, apiVersion string) {
RootCmd.Version = version
@ -52,7 +55,15 @@ func setVersion() {
}
func initConfig() {
if logAsJSON {
print.EnableJSONFormat()
}
viper.SetEnvPrefix("dapr")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.AutomaticEnv()
}
func init() {
RootCmd.PersistentFlags().BoolVarP(&logAsJSON, "log-as-json", "", false, "Log output in JSON format")
}

View File

@ -11,10 +11,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"time"
"github.com/briandowns/spinner"
"github.com/dapr/cli/pkg/print"
helm "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
@ -43,30 +40,17 @@ type InitConfiguration struct {
// Init deploys the Dapr operator using the supplied runtime version.
func Init(config InitConfiguration) error {
msg := "Deploying the Dapr control plane to your cluster..."
var s *spinner.Spinner
if runtime.GOOS == "windows" {
print.InfoStatusEvent(os.Stdout, msg)
} else {
s = spinner.New(spinner.CharSets[0], 100*time.Millisecond)
s.Writer = os.Stdout
s.Color("cyan")
s.Suffix = fmt.Sprintf(" %s", msg)
s.Start()
}
stopSpinning := print.Spinner(os.Stdout, msg)
defer stopSpinning(print.Failure)
err := install(config)
if err != nil {
if s != nil {
s.Stop()
}
return err
}
if s != nil {
s.Stop()
print.SuccessStatusEvent(os.Stdout, msg)
}
stopSpinning(print.Success)
return nil
}

View File

@ -6,10 +6,14 @@
package print
import (
"encoding/json"
"fmt"
"io"
"runtime"
"sync"
"time"
"github.com/briandowns/spinner"
"github.com/fatih/color"
)
@ -17,6 +21,13 @@ const (
windowsOS = "windows"
)
type Result bool
const (
Success Result = true
Failure Result = false
)
var (
Yellow = color.New(color.FgHiYellow, color.Bold).SprintFunc()
Green = color.New(color.FgHiGreen, color.Bold).SprintFunc()
@ -27,9 +38,21 @@ var (
WhiteBold = color.New(color.FgWhite, color.Bold).SprintFunc()
)
var logAsJSON bool
func EnableJSONFormat() {
logAsJSON = true
}
func IsJSONFormat() bool {
return logAsJSON
}
// SuccessStatusEvent reports on a success event.
func SuccessStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
if runtime.GOOS == windowsOS {
if logAsJSON {
logJSON(w, "success", fmt.Sprintf(fmtstr, a...))
} else if runtime.GOOS == windowsOS {
fmt.Fprintf(w, "%s\n", fmt.Sprintf(fmtstr, a...))
} else {
fmt.Fprintf(w, "✅ %s\n", fmt.Sprintf(fmtstr, a...))
@ -38,7 +61,9 @@ func SuccessStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
// FailureStatusEvent reports on a failure event.
func FailureStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
if runtime.GOOS == windowsOS {
if logAsJSON {
logJSON(w, "failure", fmt.Sprintf(fmtstr, a...))
} else if runtime.GOOS == windowsOS {
fmt.Fprintf(w, "%s\n", fmt.Sprintf(fmtstr, a...))
} else {
fmt.Fprintf(w, "❌ %s\n", fmt.Sprintf(fmtstr, a...))
@ -47,7 +72,9 @@ func FailureStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
// WarningStatusEvent reports on a failure event.
func WarningStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
if runtime.GOOS == windowsOS {
if logAsJSON {
logJSON(w, "warning", fmt.Sprintf(fmtstr, a...))
} else if runtime.GOOS == windowsOS {
fmt.Fprintf(w, "%s\n", fmt.Sprintf(fmtstr, a...))
} else {
fmt.Fprintf(w, "⚠ %s\n", fmt.Sprintf(fmtstr, a...))
@ -56,7 +83,9 @@ func WarningStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
// PendingStatusEvent reports on a pending event.
func PendingStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
if runtime.GOOS == windowsOS {
if logAsJSON {
logJSON(w, "pending", fmt.Sprintf(fmtstr, a...))
} else if runtime.GOOS == windowsOS {
fmt.Fprintf(w, "%s\n", fmt.Sprintf(fmtstr, a...))
} else {
fmt.Fprintf(w, "⌛ %s\n", fmt.Sprintf(fmtstr, a...))
@ -65,9 +94,68 @@ func PendingStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
// InfoStatusEvent reports status information on an event.
func InfoStatusEvent(w io.Writer, fmtstr string, a ...interface{}) {
if runtime.GOOS == windowsOS {
if logAsJSON {
logJSON(w, "info", fmt.Sprintf(fmtstr, a...))
} else if runtime.GOOS == windowsOS {
fmt.Fprintf(w, "%s\n", fmt.Sprintf(fmtstr, a...))
} else {
fmt.Fprintf(w, " %s\n", fmt.Sprintf(fmtstr, a...))
}
}
func Spinner(w io.Writer, fmtstr string, a ...interface{}) func(result Result) {
msg := fmt.Sprintf(fmtstr, a...)
var once sync.Once
var s *spinner.Spinner
if logAsJSON {
logJSON(w, "pending", msg)
} else if runtime.GOOS == windowsOS {
fmt.Fprintf(w, "%s\n", msg)
return func(Result) {} // Return a dummy func
} else {
s = spinner.New(spinner.CharSets[0], 100*time.Millisecond)
s.Writer = w
s.Color("cyan")
s.Suffix = fmt.Sprintf(" %s", msg)
s.Start()
}
return func(result Result) {
once.Do(func() {
if s != nil {
s.Stop()
}
if result {
SuccessStatusEvent(w, msg)
} else {
FailureStatusEvent(w, msg)
}
})
}
}
func logJSON(w io.Writer, status, message string) {
type jsonLog struct {
Time time.Time `json:"time"`
Status string `json:"status"`
Message string `json:"msg"`
}
l := jsonLog{
Time: time.Now().UTC(),
Status: status,
Message: message,
}
jsonBytes, err := json.Marshal(&l)
if err != nil {
// Fall back on printing the simple message without JSON.
// This is unlikely.
fmt.Fprintln(w, message)
return
}
fmt.Fprintf(w, "%s\n", string(jsonBytes))
}

View File

@ -21,9 +21,7 @@ import (
"runtime"
"strings"
"sync"
"time"
"github.com/briandowns/spinner"
"github.com/dapr/cli/pkg/print"
cli_ver "github.com/dapr/cli/pkg/version"
"github.com/dapr/cli/utils"
@ -134,16 +132,8 @@ func Init(runtimeVersion, dashboardVersion string, dockerNetwork string, slimMod
wg.Add(len(initSteps))
msg := "Downloading binaries and setting up components..."
var s *spinner.Spinner
if runtime.GOOS == daprWindowsOS {
print.InfoStatusEvent(os.Stdout, msg)
} else {
s = spinner.New(spinner.CharSets[0], 100*time.Millisecond)
s.Writer = os.Stdout
s.Color("cyan")
s.Suffix = fmt.Sprintf(" %s", msg)
s.Start()
}
stopSpinning := print.Spinner(os.Stdout, msg)
defer stopSpinning(print.Failure)
// Make default components directory
err = makeDefaultComponentsDir()
@ -174,16 +164,11 @@ func Init(runtimeVersion, dashboardVersion string, dockerNetwork string, slimMod
for err := range errorChan {
if err != nil {
if s != nil {
s.Stop()
}
return err
}
}
if s != nil {
s.Stop()
}
stopSpinning(print.Success)
msg = "Downloaded binaries and completed components set up."
print.SuccessStatusEvent(os.Stdout, msg)