Skip to content

support multi-group log commands #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,31 +75,37 @@ sudo dpkg -i <the_deb_name>
saw groups

# Get list of streams for production log group
saw streams production
saw streams "^production$"
```

- Watch
```sh
# Watch production log group
saw watch production
saw watch "^production$"

# Watch production and staging log groups
saw watch "^(production|staging)$"

# Watch production log group streams for api
saw watch production --prefix api
saw watch "^production$" --prefix api

# Watch production log group streams for api and filter for "error"
saw watch production --prefix api --filter error
saw watch "^production$" --prefix api --filter error
```

- Get
```sh
# Get production log group for the last 2 hours
saw get production --start -2h
saw get "^production$" --start -2h

# Get production and staging log group for the last 2 hours
saw get "^(production|staging)$" --start -2h

# Get production log group for the last 2 hours and filter for "error"
saw get production --start -2h --filter error
saw get "^production$" --start -2h --filter error

# Get production log group for api between 26th June 2018 and 28th June 2018
saw get production --prefix api --start 2018-06-26 --stop 2018-06-28
saw get "^production$" --prefix api --start 2018-06-26 --stop 2018-06-28
```

## Features
Expand Down
11 changes: 6 additions & 5 deletions blade/blade.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (b *Blade) GetEvents() {
handlePage := func(page *cloudwatchlogs.FilterLogEventsOutput, lastPage bool) bool {
for _, event := range page.Events {
if b.output.Pretty {
fmt.Println(formatEvent(formatter, event))
fmt.Println(formatEvent(formatter, event, b.config.Group))
} else {
fmt.Println(*event.Message)
}
Expand Down Expand Up @@ -141,7 +141,7 @@ func (b *Blade) StreamEvents() {
if b.output.Raw {
message = *event.Message
} else {
message = formatEvent(formatter, event)
message = formatEvent(formatter, event, b.config.Group)
}
message = strings.TrimRight(message, "\n")
fmt.Println(message)
Expand All @@ -165,8 +165,9 @@ func (b *Blade) StreamEvents() {
}

// formatEvent returns a CloudWatch log event as a formatted string using the provided formatter
func formatEvent(formatter *colorjson.Formatter, event *cloudwatchlogs.FilteredLogEvent) string {
func formatEvent(formatter *colorjson.Formatter, event *cloudwatchlogs.FilteredLogEvent, group string) string {
red := color.New(color.FgRed).SprintFunc()
yellow := color.New(color.FgYellow).SprintFunc()
white := color.New(color.FgWhite).SprintFunc()

str := aws.StringValue(event.Message)
Expand All @@ -177,9 +178,9 @@ func formatEvent(formatter *colorjson.Formatter, event *cloudwatchlogs.FilteredL
jl := map[string]interface{}{}

if err := json.Unmarshal(bytes, &jl); err != nil {
return fmt.Sprintf("[%s] (%s) %s", red(dateStr), white(streamStr), str)
return fmt.Sprintf("[%s] (%s:%s) %s", red(dateStr), yellow(group), white(streamStr), str)
}

output, _ := formatter.Marshal(jl)
return fmt.Sprintf("[%s] (%s) %s", red(dateStr), white(streamStr), output)
return fmt.Sprintf("[%s] (%s:%s) %s", red(dateStr), yellow(group), white(streamStr), output)
}
38 changes: 23 additions & 15 deletions cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra"
)

var getConfig config.Configuration
var getConfigGlobal config.Configuration
var getOutputConfig config.OutputConfiguration

var getCommand = &cobra.Command{
Expand All @@ -24,40 +24,48 @@ var getCommand = &cobra.Command{
return nil
},
Run: func(cmd *cobra.Command, args []string) {
getConfig.Group = args[0]
b := blade.NewBlade(&getConfig, &awsConfig, &getOutputConfig)
if getConfig.Prefix != "" {
streams := b.GetLogStreams()
if len(streams) == 0 {
fmt.Printf("No streams found in %s with prefix %s\n", getConfig.Group, getConfig.Prefix)
fmt.Printf("To view available streams: `saw streams %s`\n", getConfig.Group)
os.Exit(3)
err := runMultiGroup(args[0], func(group string) {
getConfig := getConfigGlobal
getConfig.Group = group
b := blade.NewBlade(&getConfig, &awsConfig, &getOutputConfig)
if getConfig.Prefix != "" {
streams := b.GetLogStreams()
if len(streams) == 0 {
fmt.Printf("No streams found in %s with prefix %s\n", getConfig.Group, getConfig.Prefix)
fmt.Printf("To view available streams: `saw streams %s`\n", getConfig.Group)
os.Exit(3)
}
getConfig.Streams = streams
}
getConfig.Streams = streams
b.GetEvents()
})

if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
b.GetEvents()
},
}

func init() {
getCommand.Flags().StringVar(&getConfig.Prefix, "prefix", "", "log group prefix filter")
getCommand.Flags().StringVar(&getConfigGlobal.Prefix, "prefix", "", "log group prefix filter")
getCommand.Flags().StringVar(
&getConfig.Start,
&getConfigGlobal.Start,
"start",
"",
`start getting the logs from this point
Takes an absolute timestamp in RFC3339 format, or a relative time (eg. -2h).
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`,
)
getCommand.Flags().StringVar(
&getConfig.End,
&getConfigGlobal.End,
"stop",
"now",
`stop getting the logs at this point
Takes an absolute timestamp in RFC3339 format, or a relative time (eg. -2h).
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`,
)
getCommand.Flags().StringVar(&getConfig.Filter, "filter", "", "event filter pattern")
getCommand.Flags().StringVar(&getConfigGlobal.Filter, "filter", "", "event filter pattern")
getCommand.Flags().BoolVar(&getOutputConfig.Pretty, "pretty", false, "print timestamp and stream name prefix")
getCommand.Flags().BoolVar(&getOutputConfig.Expand, "expand", false, "indent JSON log messages")
getCommand.Flags().BoolVar(&getOutputConfig.Invert, "invert", false, "invert colors for light terminal themes")
Expand Down
39 changes: 39 additions & 0 deletions cmd/saw.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package cmd

import (
"fmt"
"regexp"
"sync"

"github.com/TylerBrock/saw/blade"
"github.com/TylerBrock/saw/config"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -29,3 +34,37 @@ func init() {
SawCommand.PersistentFlags().StringVar(&awsConfig.Region, "region", "", "override profile AWS region")
SawCommand.PersistentFlags().StringVar(&awsConfig.Profile, "profile", "", "override default AWS profile")
}

func runMultiGroup(pattern string, fn func(string)) error {
var groups []string

re, err := regexp.Compile(pattern)
if err != nil {
return fmt.Errorf("invalid <log group> pattern: %s", err)
}

b := blade.NewBlade(&config.Configuration{}, &awsConfig, nil)
for _, g := range b.GetLogGroups() {
group := g.LogGroupName
if group == nil || !re.MatchString(*group) {
continue
}

groups = append(groups, *group)
}
if len(groups) == 0 {
return fmt.Errorf("no groups found matching pattern: %s", pattern)
}

var wg sync.WaitGroup
for _, group := range groups {
wg.Add(1)
go func(group string) {
defer wg.Done()
fn(group)
}(group)
}

wg.Wait()
return nil
}
27 changes: 18 additions & 9 deletions cmd/streams.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package cmd
import (
"errors"
"fmt"
"os"

"github.com/TylerBrock/saw/blade"
"github.com/TylerBrock/saw/config"
"github.com/spf13/cobra"
)

var streamsConfig config.Configuration
var streamsConfigGlobal config.Configuration

var streamsCommand = &cobra.Command{
Use: "streams <log group>",
Expand All @@ -22,18 +23,26 @@ var streamsCommand = &cobra.Command{
return nil
},
Run: func(cmd *cobra.Command, args []string) {
streamsConfig.Group = args[0]
b := blade.NewBlade(&streamsConfig, &awsConfig, nil)
err := runMultiGroup(args[0], func(group string) {
streamsConfig := streamsConfigGlobal
streamsConfig.Group = group
b := blade.NewBlade(&streamsConfig, &awsConfig, nil)

logStreams := b.GetLogStreams()
for _, stream := range logStreams {
fmt.Println(*stream.LogStreamName)
logStreams := b.GetLogStreams()
for _, stream := range logStreams {
fmt.Println(*stream.LogStreamName)
}
})

if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
},
}

func init() {
streamsCommand.Flags().StringVar(&streamsConfig.Prefix, "prefix", "", "stream prefix filter")
streamsCommand.Flags().StringVar(&streamsConfig.OrderBy, "orderBy", "LogStreamName", "order streams by LogStreamName or LastEventTime")
streamsCommand.Flags().BoolVar(&streamsConfig.Descending, "descending", false, "order streams descending")
streamsCommand.Flags().StringVar(&streamsConfigGlobal.Prefix, "prefix", "", "stream prefix filter")
streamsCommand.Flags().StringVar(&streamsConfigGlobal.OrderBy, "orderBy", "LogStreamName", "order streams by LogStreamName or LastEventTime")
streamsCommand.Flags().BoolVar(&streamsConfigGlobal.Descending, "descending", false, "order streams descending")
}
34 changes: 21 additions & 13 deletions cmd/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra"
)

var watchConfig config.Configuration
var watchConfigGlobal config.Configuration

var watchOutputConfig config.OutputConfiguration

Expand All @@ -25,24 +25,32 @@ var watchCommand = &cobra.Command{
return nil
},
Run: func(cmd *cobra.Command, args []string) {
watchConfig.Group = args[0]
b := blade.NewBlade(&watchConfig, &awsConfig, &watchOutputConfig)
if watchConfig.Prefix != "" {
streams := b.GetLogStreams()
if len(streams) == 0 {
fmt.Printf("No streams found in %s with prefix %s\n", watchConfig.Group, watchConfig.Prefix)
fmt.Printf("To view available streams: `saw streams %s`\n", watchConfig.Group)
os.Exit(3)
err := runMultiGroup(args[0], func(group string) {
watchConfig := watchConfigGlobal
watchConfig.Group = group
b := blade.NewBlade(&watchConfig, &awsConfig, &watchOutputConfig)
if watchConfig.Prefix != "" {
streams := b.GetLogStreams()
if len(streams) == 0 {
fmt.Printf("No streams found in %s with prefix %s\n", watchConfig.Group, watchConfig.Prefix)
fmt.Printf("To view available streams: `saw streams %s`\n", watchConfig.Group)
os.Exit(3)
}
watchConfig.Streams = streams
}
watchConfig.Streams = streams
b.StreamEvents()
})

if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
b.StreamEvents()
},
}

func init() {
watchCommand.Flags().StringVar(&watchConfig.Prefix, "prefix", "", "log stream prefix filter")
watchCommand.Flags().StringVar(&watchConfig.Filter, "filter", "", "event filter pattern")
watchCommand.Flags().StringVar(&watchConfigGlobal.Prefix, "prefix", "", "log stream prefix filter")
watchCommand.Flags().StringVar(&watchConfigGlobal.Filter, "filter", "", "event filter pattern")
watchCommand.Flags().BoolVar(&watchOutputConfig.Raw, "raw", false, "print raw log event without timestamp or stream prefix")
watchCommand.Flags().BoolVar(&watchOutputConfig.Expand, "expand", false, "indent JSON log messages")
watchCommand.Flags().BoolVar(&watchOutputConfig.Invert, "invert", false, "invert colors for light terminal themes")
Expand Down