1//go:build example
2// +build example
3
4package main
5
6import (
7	"fmt"
8	"io"
9	"log"
10	"os"
11	"runtime/debug"
12
13	"github.com/aws/aws-sdk-go/aws/request"
14	"github.com/aws/aws-sdk-go/aws/session"
15	"github.com/aws/aws-sdk-go/service/s3/s3manager"
16)
17
18// Usage:
19//   go run -tags example  <bucket> <key> <file to upload>
20//
21// Example:
22//   AWS_REGION=us-west-2 AWS_PROFILE=default go run . "mybucket" "10MB.file" ./10MB.file
23func main() {
24	sess, err := session.NewSession()
25	if err != nil {
26		log.Fatalf("failed to load session, %v", err)
27	}
28
29	uploader := s3manager.NewUploader(sess)
30
31	file, err := os.Open(os.Args[3])
32	if err != nil {
33		log.Fatalf("failed to open file, %v", err)
34	}
35	defer file.Close()
36
37	// Wrap the readSeeker with a logger that will log usage, and stack traces
38	// on errors.
39	readLogger := NewReadLogger(file, sess.Config.Logger)
40
41	// Upload with read logger
42	resp, err := uploader.Upload(&s3manager.UploadInput{
43		Bucket: &os.Args[1],
44		Key:    &os.Args[2],
45		Body:   readLogger,
46	}, func(u *s3manager.Uploader) {
47		u.Concurrency = 1
48		u.RequestOptions = append(u.RequestOptions, func(r *request.Request) {
49		})
50	})
51
52	fmt.Println(resp, err)
53}
54
55// Logger is a logger use for logging the readers usage.
56type Logger interface {
57	Log(args ...interface{})
58}
59
60// ReadSeeker interface provides the interface for a Reader, Seeker, and ReadAt.
61type ReadSeeker interface {
62	io.ReadSeeker
63	io.ReaderAt
64}
65
66// ReadLogger wraps an reader with logging for access.
67type ReadLogger struct {
68	reader ReadSeeker
69	logger Logger
70}
71
72// NewReadLogger a ReadLogger that wraps the passed in ReadSeeker (Reader,
73// Seeker, ReadAt) with a logger.
74func NewReadLogger(r ReadSeeker, logger Logger) *ReadLogger {
75	return &ReadLogger{
76		reader: r,
77		logger: logger,
78	}
79}
80
81// Seek offsets the reader's current position for the next read.
82func (s *ReadLogger) Seek(offset int64, mode int) (int64, error) {
83	newOffset, err := s.reader.Seek(offset, mode)
84	msg := fmt.Sprintf(
85		"ReadLogger.Seek(offset:%d, mode:%d) (newOffset:%d, err:%v)",
86		offset, mode, newOffset, err)
87	if err != nil {
88		msg += fmt.Sprintf("\n\tStack:\n%s", string(debug.Stack()))
89	}
90
91	s.logger.Log(msg)
92	return newOffset, err
93}
94
95// Read attempts to read from the reader, returning the bytes read, or error.
96func (s *ReadLogger) Read(b []byte) (int, error) {
97	n, err := s.reader.Read(b)
98	msg := fmt.Sprintf(
99		"ReadLogger.Read(len(bytes):%d) (read:%d, err:%v)",
100		len(b), n, err)
101	if err != nil {
102		msg += fmt.Sprintf("\n\tStack:\n%s", string(debug.Stack()))
103	}
104
105	s.logger.Log(msg)
106	return n, err
107}
108
109// ReadAt will read the underlying reader starting at the offset.
110func (s *ReadLogger) ReadAt(b []byte, offset int64) (int, error) {
111	n, err := s.reader.ReadAt(b, offset)
112	msg := fmt.Sprintf(
113		"ReadLogger.ReadAt(len(bytes):%d, offset:%d) (read:%d, err:%v)",
114		len(b), offset, n, err)
115	if err != nil {
116		msg += fmt.Sprintf("\n\tStack:\n%s", string(debug.Stack()))
117	}
118
119	s.logger.Log(msg)
120	return n, err
121}
122