pulumi/cmd/logs.go

108 lines
3.1 KiB
Go
Raw Normal View History

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
package cmd
import (
"fmt"
"time"
2017-11-28 21:54:36 +01:00
mobytime "github.com/moby/moby/api/types/time"
"github.com/pkg/errors"
"github.com/spf13/cobra"
2017-11-20 07:28:49 +01:00
"github.com/pulumi/pulumi/pkg/operations"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
)
func newLogsCmd() *cobra.Command {
var stack string
var follow bool
var since string
2017-11-23 05:58:46 +01:00
var resource string
logsCmd := &cobra.Command{
Use: "logs",
Short: "Show aggregated logs for a project",
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
stackName, err := explicitOrCurrent(stack, backend)
if err != nil {
return err
}
2017-11-28 21:54:36 +01:00
startTime, err := parseSince(since)
if err != nil {
return errors.Wrapf(err, "failed to parse argument to '--since' as duration or timestamp")
}
2017-11-23 05:58:46 +01:00
var resourceFilter *operations.ResourceFilter
if resource != "" {
var rf = operations.ResourceFilter(resource)
resourceFilter = &rf
}
// 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.
//
// Note: Just tracking latest log date is not sufficient - as stale logs may show up which should have been
// displayed before previously rendered log entries, but weren't available at the time, so still need to be
// rendered now even though they are technically out of order.
shown := map[operations.LogEntry]bool{}
for {
logs, err := backend.GetLogs(stackName, operations.LogQuery{
StartTime: startTime,
ResourceFilter: resourceFilter,
})
if err != nil {
2017-11-28 21:54:36 +01:00
return errors.Wrapf(err, "failed to get logs")
}
for _, logEntry := range logs {
if _, shownAlready := shown[logEntry]; !shownAlready {
eventTime := time.Unix(0, logEntry.Timestamp*1000000)
2017-11-21 08:18:47 +01:00
fmt.Printf("%30.30s[%30.30s] %v\n", eventTime.Format(time.RFC3339Nano), logEntry.ID, logEntry.Message)
shown[logEntry] = true
}
}
if !follow {
return nil
}
time.Sleep(time.Second)
}
}),
}
logsCmd.PersistentFlags().StringVarP(
&stack, "stack", "s", "",
"List configuration for a different stack than the currently selected stack")
logsCmd.PersistentFlags().BoolVarP(
&follow, "follow", "f", false,
"Follow the log stream in real time (like tail -f)")
logsCmd.PersistentFlags().StringVar(
&since, "since", "",
"Only return logs newer than a relative duration ('5s', '2m', '3h') or absolute timestamp. "+
"Defaults to returning all logs.")
2017-11-23 05:58:46 +01:00
logsCmd.PersistentFlags().StringVarP(
&resource, "resource", "r", "",
"Only return logs for the requested resource ('name', 'type::name' or full URN). Defaults to returning all logs.")
return logsCmd
}
2017-11-28 21:54:36 +01:00
func parseSince(since string) (*time.Time, error) {
startTimestamp, err := mobytime.GetTimestamp(since, time.Now())
if err != nil {
return nil, err
}
2017-11-28 21:54:36 +01:00
startTimeSec, startTimeNs, err := mobytime.ParseTimestamps(startTimestamp, 0)
if err != nil {
2017-11-28 21:54:36 +01:00
return nil, err
}
2017-11-28 21:54:36 +01:00
if startTimeSec == 0 && startTimeNs == 0 {
return nil, nil
}
2017-11-28 21:54:36 +01:00
startTime := time.Unix(startTimeSec, startTimeNs)
return &startTime, nil
}