Skip to content
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

add trace level logging to enable sdk debug mode #430

Merged
merged 17 commits into from May 19, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,7 @@
- Added AWS Single Sign-On (SSO) profiles support. ([#385](https://github.com/peak/s5cmd/issues/385))
- Added `--use-list-objects-v1` flag to force using S3 ListObjects API instead of ListObjectsV2 API. ([#405](https://github.com/peak/s5cmd/issues/405))
- Added `--request-payer` flag to include `x-amz-request-payer` in header while sending GET, POST and HEAD requests. ([#297](https://github.com/peak/s5cmd/issues/297))
- Added trace log level(`--log=trace`) which enables SDK debug logs.([#363](https://github.com/peak/s5cmd/issues/363))

#### Improvements
- Upgrade minimum required Go version to 1.16.
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -494,6 +494,7 @@ ERROR "cp s3://somebucket/file.txt file.txt": object already exists
"error": "'cp s3://somebucket/file.txt file.txt': object already exists"
}
```

## Benchmarks
Some benchmarks regarding the performance of `s5cmd` are introduced below. For more
details refer to this [post](https://medium.com/@joshua_robinson/s5cmd-for-high-performance-object-storage-7071352cc09d)
Expand Down
19 changes: 11 additions & 8 deletions command/app.go
Expand Up @@ -48,10 +48,13 @@ var app = &cli.App{
Name: "no-verify-ssl",
Usage: "disable SSL certificate verification",
},
&cli.StringFlag{
Name: "log",
Value: "info",
Usage: "log level: (debug, info, error)",
&cli.GenericFlag{
Name: "log",
Value: &EnumValue{
Enum: []string{"trace", "debug", "info", "error"},
Default: "info",
},
Usage: "log level: (trace, debug, info, error)",
},
&cli.BoolFlag{
Name: "install-completion",
Expand Down Expand Up @@ -142,13 +145,13 @@ var app = &cli.App{
// NewStorageOpts creates storage.Options object from the given context.
func NewStorageOpts(c *cli.Context) storage.Options {
return storage.Options{
MaxRetries: c.Int("retry-count"),
Endpoint: c.String("endpoint-url"),
NoVerifySSL: c.Bool("no-verify-ssl"),
DryRun: c.Bool("dry-run"),
Endpoint: c.String("endpoint-url"),
MaxRetries: c.Int("retry-count"),
NoSignRequest: c.Bool("no-sign-request"),
UseListObjectsV1: c.Bool("use-list-objects-v1"),
NoVerifySSL: c.Bool("no-verify-ssl"),
RequestPayer: c.String("request-payer"),
UseListObjectsV1: c.Bool("use-list-objects-v1"),
}
}

Expand Down
30 changes: 30 additions & 0 deletions command/flag.go
@@ -0,0 +1,30 @@
package command

import (
"fmt"
"strings"
)

type EnumValue struct {
Enum []string
Default string
selected string
}

func (e *EnumValue) Set(value string) error {
for _, enum := range e.Enum {
if enum == value {
e.selected = value
return nil
}
}

return fmt.Errorf("allowed values: [%s]", strings.Join(e.Enum, ", "))
}

func (e EnumValue) String() string {
if e.selected == "" {
return e.Default
}
return e.selected
}
7 changes: 5 additions & 2 deletions command/select.go
Expand Up @@ -47,10 +47,13 @@ func NewSelectCommand() *cli.Command {
Usage: "input compression format",
Value: "NONE",
},
&cli.StringFlag{
&cli.GenericFlag{
Name: "format",
Usage: "input data format (only JSON supported for the moment)",
Value: "JSON",
Value: &EnumValue{
Enum: []string{"JSON"},
Default: "JSON",
},
},
&cli.StringSliceFlag{
Name: "exclude",
Expand Down
16 changes: 15 additions & 1 deletion log/log.go
Expand Up @@ -22,6 +22,11 @@ func Init(level string, json bool) {
global = New(level, json)
}

// Trace prints message in trace mode.
func Trace(msg Message) {
global.printf(levelTrace, msg, os.Stdout)
}

// Debug prints message in debug mode.
func Debug(msg Message) {
global.printf(levelDebug, msg, os.Stdout)
Expand Down Expand Up @@ -94,7 +99,8 @@ func (l *Logger) out() {
type logLevel int

const (
levelDebug logLevel = iota
levelTrace logLevel = iota
levelDebug
levelInfo
levelError
)
Expand All @@ -108,6 +114,12 @@ func (l logLevel) String() string {
return "ERROR "
case levelDebug:
return "DEBUG "
case levelTrace:
// levelTrace is used for printing aws sdk logs and
// aws-sdk-go already adds "DEBUG" prefix to logs.
// So do not add another prefix to log which makes it
// look weird.
return ""
igungor marked this conversation as resolved.
Show resolved Hide resolved
default:
return "UNKNOWN "
}
Expand All @@ -123,6 +135,8 @@ func levelFromString(s string) logLevel {
return levelInfo
case "error":
return levelError
case "trace":
return levelTrace
default:
return levelInfo
}
Expand Down
12 changes: 12 additions & 0 deletions log/message.go
Expand Up @@ -63,6 +63,18 @@ type DebugMessage struct {
Err string `json:"error"`
}

type TraceMessage struct {
Message string `json:"message"`
}

func (t TraceMessage) String() string {
return t.Message
}

func (t TraceMessage) JSON() string {
return strutil.JSON(t)
}

// String is the string representation of ErrorMessage.
func (d DebugMessage) String() string {
if d.Command == "" {
Expand Down
13 changes: 12 additions & 1 deletion storage/s3.go
Expand Up @@ -740,6 +740,15 @@ func (s *S3) RemoveBucket(ctx context.Context, name string) error {
return err
}

type sdkLogger struct{}

func (l sdkLogger) Log(args ...interface{}) {
msg := log.TraceMessage{
Message: fmt.Sprint(args...),
}
log.Trace(msg)
}

// SessionCache holds session.Session according to s3Opts and it synchronizes
// access/modification.
type SessionCache struct {
Expand Down Expand Up @@ -790,7 +799,9 @@ func (sc *SessionCache) newSession(ctx context.Context, opts Options) (*session.
WithEndpoint(endpointURL.String()).
WithS3ForcePathStyle(!isVirtualHostStyle).
WithS3UseAccelerate(useAccelerate).
WithHTTPClient(httpClient)
WithHTTPClient(httpClient).
WithLogLevel(aws.LogDebug).
WithLogger(sdkLogger{})

awsCfg.Retryer = newCustomRetryer(opts.MaxRetries)

Expand Down