forked from stripe/stripe-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce Log Tailing command (stripe#57)
* Log tailing command in top-level command dir * Logs command in subdir * Add websocket test for request log events * output formatting * new format for created_at * remove empty test file * add flag for json formatting' * switch to aurora * new linting fixes * adjust imports * fix new linting errors * implement filter args * filter out sessions logs * fix linting * some PR feedback and linkify request id * add requestlogid comment clarification * move requestlogid comment * rename logs packages * fix help messages * fix linting * shorten short message * Add UTC to displayed time * Add default value for missing path * --help formatting (stripe#76) * new help format * API -> Stripe * new arg validators * add source validator * add validators to log tail execution * Add new filters and filter lists * connect warning message * logs tail help formatting * fix missing quote * Address partial tomer feedback with help messages * remove unnecessary todo comment * convert status code type to be specified as 2XX,4XX,5XX * local timezone formatting * fix X replace bug * rebase on master and fix api key call
- Loading branch information
1 parent
d7ed39d
commit c6630f4
Showing
13 changed files
with
773 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
|
||
logs "github.com/stripe/stripe-cli/pkg/cmd/logs" | ||
"github.com/stripe/stripe-cli/pkg/config" | ||
"github.com/stripe/stripe-cli/pkg/validators" | ||
) | ||
|
||
// LogsCmd is a wrapper for the base logs command | ||
type LogsCmd struct { | ||
Cmd *cobra.Command | ||
cfg *config.Config | ||
} | ||
|
||
func newLogsCmd(config *config.Config) *LogsCmd { | ||
logsCmd := &LogsCmd{ | ||
cfg: config, | ||
} | ||
|
||
logsCmd.Cmd = &cobra.Command{ | ||
Use: "logs", | ||
Args: validators.NoArgs, | ||
Short: "Interact with Stripe request logs", | ||
Long: `The logs command allows you to interact with your API request logs from Stripe. The first supported feature is log tailing, which allows you to view your API request logs in real-time.`, | ||
} | ||
|
||
logsCmd.Cmd.AddCommand(logs.NewTailCmd(logsCmd.cfg).Cmd) | ||
|
||
return logsCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package logs | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/stripe/stripe-cli/pkg/config" | ||
logTailing "github.com/stripe/stripe-cli/pkg/logtailing" | ||
"github.com/stripe/stripe-cli/pkg/validators" | ||
) | ||
|
||
const requestLogsWebSocketFeature = "request_logs" | ||
|
||
// TailCmd wraps the configuration for the tail command | ||
type TailCmd struct { | ||
apiBaseURL string | ||
cfg *config.Config | ||
Cmd *cobra.Command | ||
format string | ||
LogFilters *logTailing.LogFilters | ||
noWSS bool | ||
} | ||
|
||
// NewTailCmd creates and initializes the tail command for the logs package | ||
func NewTailCmd(config *config.Config) *TailCmd { | ||
tailCmd := &TailCmd{ | ||
cfg: config, | ||
LogFilters: &logTailing.LogFilters{}, | ||
} | ||
|
||
tailCmd.Cmd = &cobra.Command{ | ||
Use: "tail", | ||
Args: validators.NoArgs, | ||
Short: "Tails request logs created by making API requests to Stripe.", | ||
Long: fmt.Sprintf(`The tail command lets you tail API request logs from Stripe. | ||
The command establishes a direct connection with Stripe to send the request logs to your local machine. | ||
Watch for all request logs sent from Stripe: | ||
$ stripe logs tail`), | ||
RunE: tailCmd.runTailCmd, | ||
} | ||
|
||
tailCmd.Cmd.Flags().StringVar( | ||
&tailCmd.format, | ||
"format", | ||
"", | ||
`Specifies the output format of request logs | ||
Acceptable values: | ||
'JSON' - Output logs in JSON format`, | ||
) | ||
|
||
// Log filters | ||
tailCmd.Cmd.Flags().StringSliceVar( | ||
&tailCmd.LogFilters.FilterAccount, | ||
"filter-account", | ||
[]string{}, | ||
`*CONNECT ONLY* Filter request logs by source and destination account | ||
Acceptable values: | ||
'connect_in' - Incoming connect requests | ||
'connect_out' - Outgoing connect requests | ||
'self' - Non-connect requests`, | ||
) | ||
tailCmd.Cmd.Flags().StringSliceVar(&tailCmd.LogFilters.FilterIPAddress, "filter-ip-address", []string{}, "Filter request logs by ip address") | ||
tailCmd.Cmd.Flags().StringSliceVar( | ||
&tailCmd.LogFilters.FilterHTTPMethod, | ||
"filter-http-method", | ||
[]string{}, | ||
`Filter request logs by http method | ||
Acceptable values: | ||
'GET' - HTTP get requests | ||
'POST' - HTTP post requests | ||
'DELETE' - HTTP delete requests`, | ||
) | ||
tailCmd.Cmd.Flags().StringSliceVar(&tailCmd.LogFilters.FilterRequestPath, "filter-request-path", []string{}, "Filter request logs by request path") | ||
tailCmd.Cmd.Flags().StringSliceVar( | ||
&tailCmd.LogFilters.FilterRequestStatus, | ||
"filter-request-status", | ||
[]string{}, | ||
`Filter request logs by request status | ||
Acceptable values: | ||
'SUCCEEDED' - Requests that succeeded (status codes 200, 201, 202) | ||
'FAILED' - Requests that failed`, | ||
) | ||
tailCmd.Cmd.Flags().StringSliceVar( | ||
&tailCmd.LogFilters.FilterSource, | ||
"filter-source", | ||
[]string{}, | ||
`Filter request logs by source | ||
Acceptable values: | ||
'API' - Requests that came through the Stripe API | ||
'DASHBOARD' - Requests that came through the Stripe Dashboard`, | ||
) | ||
tailCmd.Cmd.Flags().StringSliceVar(&tailCmd.LogFilters.FilterStatusCode, "filter-status-code", []string{}, "Filter request logs by status code") | ||
tailCmd.Cmd.Flags().StringSliceVar( | ||
&tailCmd.LogFilters.FilterStatusCodeType, | ||
"filter-status-code-type", | ||
[]string{}, | ||
`Filter request logs by status code type | ||
Acceptable values: | ||
'2XX' - All 2XX status codes | ||
'4XX' - All 4XX status codes | ||
'5XX' - All 5XX status codes`, | ||
) | ||
|
||
// Hidden configuration flags, useful for dev/debugging | ||
tailCmd.Cmd.Flags().StringVar(&tailCmd.apiBaseURL, "api-base", "", "Sets the API base URL") | ||
tailCmd.Cmd.Flags().MarkHidden("api-base") // #nosec G104 | ||
|
||
tailCmd.Cmd.Flags().BoolVar(&tailCmd.noWSS, "no-wss", false, "Force unencrypted ws:// protocol instead of wss://") | ||
tailCmd.Cmd.Flags().MarkHidden("no-wss") // #nosec G104 | ||
|
||
return tailCmd | ||
} | ||
|
||
func (tailCmd *TailCmd) runTailCmd(cmd *cobra.Command, args []string) error { | ||
err := tailCmd.validateArgs() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = tailCmd.convertArgs() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
deviceName, err := tailCmd.cfg.Profile.GetDeviceName() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
key, err := tailCmd.cfg.Profile.GetAPIKey() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
tailer := logTailing.New(&logTailing.Config{ | ||
APIBaseURL: tailCmd.apiBaseURL, | ||
DeviceName: deviceName, | ||
Filters: tailCmd.LogFilters, | ||
Key: key, | ||
Log: log.StandardLogger(), | ||
NoWSS: tailCmd.noWSS, | ||
OutputFormat: tailCmd.format, | ||
WebSocketFeature: requestLogsWebSocketFeature, | ||
}) | ||
|
||
err = tailer.Run() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (tailCmd *TailCmd) validateArgs() error { | ||
err := validators.CallNonEmptyArray(validators.Account, tailCmd.LogFilters.FilterAccount) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validators.CallNonEmptyArray(validators.HTTPMethod, tailCmd.LogFilters.FilterHTTPMethod) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validators.CallNonEmptyArray(validators.StatusCode, tailCmd.LogFilters.FilterStatusCode) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validators.CallNonEmptyArray(validators.StatusCodeType, tailCmd.LogFilters.FilterStatusCodeType) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validators.CallNonEmptyArray(validators.RequestSource, tailCmd.LogFilters.FilterSource) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validators.CallNonEmptyArray(validators.RequestStatus, tailCmd.LogFilters.FilterRequestStatus) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (tailCmd *TailCmd) convertArgs() error { | ||
// The backend expects to receive the status code type as a string representing the start of the range (e.g., '200') | ||
if len(tailCmd.LogFilters.FilterStatusCodeType) > 0 { | ||
for i, code := range tailCmd.LogFilters.FilterStatusCodeType { | ||
tailCmd.LogFilters.FilterStatusCodeType[i] = strings.ReplaceAll(strings.ToUpper(code), "X", "0") | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.