Add --json to pulumi logs

When outputing JSON, if we have a fixed number of log entries (i.e. we
are not `--follow`'ing, we wrap each entry in array. Otherwise, we
just emit each log entry as an object at top level.

As part of this change, I've adopted a slightly more precise time
output format in `pulumi stack ls` when using JSON output. These times
now match the default output from `console.log(new Date())`
This commit is contained in:
Matt Ellis 2018-10-29 11:30:35 -07:00
parent ca540cc736
commit 4ba5901aaf
2 changed files with 56 additions and 8 deletions

View file

@ -39,6 +39,7 @@ func newLogsCmd() *cobra.Command {
var follow bool
var since string
var resource string
var jsonOut bool
logsCmd := &cobra.Command{
Use: "logs",
@ -64,11 +65,13 @@ func newLogsCmd() *cobra.Command {
resourceFilter = &rf
}
fmt.Printf(
opts.Color.Colorize(colors.BrightMagenta+"Collecting logs for stack %s since %s.\n\n"+colors.Reset),
s.Ref().String(),
startTime.Format(timeFormat),
)
if !jsonOut {
fmt.Printf(
opts.Color.Colorize(colors.BrightMagenta+"Collecting logs for stack %s since %s.\n\n"+colors.Reset),
s.Ref().String(),
startTime.Format(timeFormat),
)
}
// IDEA: This map will grow forever as new log entries are found. We may need to do a more approximate
// approach here to ensure we don't grow memory unboundedly while following logs.
@ -86,10 +89,45 @@ func newLogsCmd() *cobra.Command {
return errors.Wrapf(err, "failed to get logs")
}
// When we are emitting a fixed number of log entries, and outputing JSON, wrap them in an array.
if !follow && jsonOut {
entries := make([]logEntryJSON, 0, len(logs))
for _, logEntry := range logs {
if _, shownAlready := shown[logEntry]; !shownAlready {
eventTime := time.Unix(0, logEntry.Timestamp*1000000)
entries = append(entries, logEntryJSON{
ID: logEntry.ID,
Timestamp: eventTime.UTC().Format(timeFormat),
Message: logEntry.Message,
})
shown[logEntry] = true
}
}
return printJSON(entries)
}
for _, logEntry := range logs {
if _, shownAlready := shown[logEntry]; !shownAlready {
eventTime := time.Unix(0, logEntry.Timestamp*1000000)
fmt.Printf("%30.30s[%30.30s] %v\n", eventTime.Format(timeFormat), logEntry.ID, logEntry.Message)
if !jsonOut {
fmt.Printf("%30.30s[%30.30s] %v\n", eventTime.Format(timeFormat),
logEntry.ID, logEntry.Message)
} else {
err = printJSON(logEntryJSON{
ID: logEntry.ID,
Timestamp: eventTime.UTC().Format(timeFormat),
Message: logEntry.Message,
})
if err != nil {
return err
}
}
shown[logEntry] = true
}
}
@ -106,6 +144,8 @@ func newLogsCmd() *cobra.Command {
logsCmd.PersistentFlags().StringVarP(
&stack, "stack", "s", "",
"The name of the stack to operate on. Defaults to the current stack")
logsCmd.PersistentFlags().BoolVarP(
&jsonOut, "json", "j", false, "Emit outputs as JSON")
logsCmd.PersistentFlags().BoolVarP(
&follow, "follow", "f", false,
"Follow the log stream in real time (like tail -f)")
@ -135,3 +175,12 @@ func parseSince(since string, reference time.Time) (*time.Time, error) {
startTime := time.Unix(startTimeSec, startTimeNs)
return &startTime, nil
}
// logEntryJSON is the shape of the --json output of this command. When --json is passed, if we are not following the
// log stream, we print an array of logEntry objects. If we are following the log stream, we instead print each object
// at top level.
type logEntryJSON struct {
ID string
Timestamp string
Message string
}

View file

@ -18,7 +18,6 @@ import (
"fmt"
"sort"
"strconv"
"time"
"github.com/dustin/go-humanize"
"github.com/pkg/errors"
@ -117,7 +116,7 @@ func formatJSON(b backend.Backend, currentStack string, stackSummaries []backend
}
if summary.LastUpdate() != nil {
summaryJSON.LastUpdate = summary.LastUpdate().UTC().Format(time.RFC3339)
summaryJSON.LastUpdate = summary.LastUpdate().UTC().Format(timeFormat)
}
if httpBackend, ok := b.(httpstate.Backend); ok {