pulumi/cmd/logs.go

117 lines
2.6 KiB
Go
Raw Normal View History

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
package cmd
import (
"fmt"
"regexp"
"strconv"
"time"
"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
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
}
startTime := parseRelativeDuration(since)
sinceTime := time.Unix(0, 0)
highestTimeSeen := time.Unix(0, 0)
for {
logs, err := backend.GetLogs(stackName, operations.LogQuery{
StartTime: startTime,
})
if err != nil {
return err
}
for _, logEntry := range logs {
eventTime := time.Unix(0, logEntry.Timestamp*1000000)
if eventTime.After(sinceTime) {
fmt.Printf("%29v[%25v] %v\n", eventTime.Format(time.RFC3339Nano), logEntry.ID, logEntry.Message)
}
if eventTime.After(highestTimeSeen) {
highestTimeSeen = eventTime
}
}
if !follow {
return nil
}
sinceTime = highestTimeSeen
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'). Defaults to returning all logs.")
return logsCmd
}
var durationRegexp = regexp.MustCompile(`(\d+)([y|w|d|h|m|s])`)
// parseRelativeDuration extracts a time.Time previous to now by the a relative duration in the format '5s', '2m', '3h'.
func parseRelativeDuration(duration string) *time.Time {
now := time.Now()
if duration == "" {
return nil
}
parts := durationRegexp.FindStringSubmatch(duration)
if parts == nil {
fmt.Printf("Err: %v\n", duration)
return nil
}
num, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
fmt.Printf("Err: %v\n", err)
return nil
}
d := time.Duration(-num)
switch parts[2] {
case "y":
d *= time.Hour * 24 * 365
case "w":
d *= time.Hour * 24 * 7
case "d":
d *= time.Hour * 24
case "h":
d *= time.Hour
case "m":
d *= time.Minute
case "s":
d *= time.Second
default:
return nil
}
// fmt.Printf("Duration: %v\n", d)
ret := now.Add(d)
fmt.Printf("Since: %v\n", ret.Format(time.RFC3339Nano))
return &ret
}