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:
parent
ca540cc736
commit
4ba5901aaf
61
cmd/logs.go
61
cmd/logs.go
|
@ -39,6 +39,7 @@ func newLogsCmd() *cobra.Command {
|
||||||
var follow bool
|
var follow bool
|
||||||
var since string
|
var since string
|
||||||
var resource string
|
var resource string
|
||||||
|
var jsonOut bool
|
||||||
|
|
||||||
logsCmd := &cobra.Command{
|
logsCmd := &cobra.Command{
|
||||||
Use: "logs",
|
Use: "logs",
|
||||||
|
@ -64,11 +65,13 @@ func newLogsCmd() *cobra.Command {
|
||||||
resourceFilter = &rf
|
resourceFilter = &rf
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(
|
if !jsonOut {
|
||||||
opts.Color.Colorize(colors.BrightMagenta+"Collecting logs for stack %s since %s.\n\n"+colors.Reset),
|
fmt.Printf(
|
||||||
s.Ref().String(),
|
opts.Color.Colorize(colors.BrightMagenta+"Collecting logs for stack %s since %s.\n\n"+colors.Reset),
|
||||||
startTime.Format(timeFormat),
|
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
|
// 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.
|
// 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")
|
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 {
|
for _, logEntry := range logs {
|
||||||
if _, shownAlready := shown[logEntry]; !shownAlready {
|
if _, shownAlready := shown[logEntry]; !shownAlready {
|
||||||
eventTime := time.Unix(0, logEntry.Timestamp*1000000)
|
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
|
shown[logEntry] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +144,8 @@ func newLogsCmd() *cobra.Command {
|
||||||
logsCmd.PersistentFlags().StringVarP(
|
logsCmd.PersistentFlags().StringVarP(
|
||||||
&stack, "stack", "s", "",
|
&stack, "stack", "s", "",
|
||||||
"The name of the stack to operate on. Defaults to the current stack")
|
"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(
|
logsCmd.PersistentFlags().BoolVarP(
|
||||||
&follow, "follow", "f", false,
|
&follow, "follow", "f", false,
|
||||||
"Follow the log stream in real time (like tail -f)")
|
"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)
|
startTime := time.Unix(startTimeSec, startTimeNs)
|
||||||
return &startTime, nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -117,7 +116,7 @@ func formatJSON(b backend.Backend, currentStack string, stackSummaries []backend
|
||||||
}
|
}
|
||||||
|
|
||||||
if summary.LastUpdate() != nil {
|
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 {
|
if httpBackend, ok := b.(httpstate.Backend); ok {
|
||||||
|
|
Loading…
Reference in a new issue