8 "github.com/aws/aws-sdk-go/aws"
9 "github.com/aws/aws-sdk-go/aws/awserr"
10 "github.com/aws/aws-sdk-go/aws/request"
14 // DefaultPort is used when no port is specified
18 // Reporter will gather metrics of API requests made and
19 // send those metrics to the CSM endpoint.
20 type Reporter struct {
32 func connect(url string) error {
34 if err := sender.connect(network, url); err != nil {
38 if sender.done == nil {
39 sender.done = make(chan struct{})
46 func newReporter(clientID, url string) *Reporter {
50 metricsCh: newMetricChan(MetricsChannelSize),
54 func (rep *Reporter) sendAPICallAttemptMetric(r *request.Request) {
60 creds, _ := r.Config.Credentials.Get()
63 ClientID: aws.String(rep.clientID),
64 API: aws.String(r.Operation.Name),
65 Service: aws.String(r.ClientInfo.ServiceID),
66 Timestamp: (*metricTime)(&now),
67 UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")),
68 Region: r.Config.Region,
69 Type: aws.String("ApiCallAttempt"),
72 XAmzRequestID: aws.String(r.RequestID),
74 AttemptCount: aws.Int(r.RetryCount + 1),
75 AttemptLatency: aws.Int(int(now.Sub(r.AttemptTime).Nanoseconds() / int64(time.Millisecond))),
76 AccessKey: aws.String(creds.AccessKeyID),
79 if r.HTTPResponse != nil {
80 m.HTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode)
84 if awserr, ok := r.Error.(awserr.Error); ok {
85 m.SetException(getMetricException(awserr))
93 func getMetricException(err awserr.Error) metricException {
100 request.CanceledErrorCode:
102 requestException{exception: code, message: msg},
106 requestException{exception: code, message: msg},
111 func (rep *Reporter) sendAPICallMetric(r *request.Request) {
118 ClientID: aws.String(rep.clientID),
119 API: aws.String(r.Operation.Name),
120 Service: aws.String(r.ClientInfo.ServiceID),
121 Timestamp: (*metricTime)(&now),
122 UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")),
123 Type: aws.String("ApiCall"),
124 AttemptCount: aws.Int(r.RetryCount + 1),
125 Region: r.Config.Region,
126 Latency: aws.Int(int(time.Now().Sub(r.Time) / time.Millisecond)),
127 XAmzRequestID: aws.String(r.RequestID),
128 MaxRetriesExceeded: aws.Int(boolIntValue(r.RetryCount >= r.MaxRetries())),
131 if r.HTTPResponse != nil {
132 m.FinalHTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode)
136 if awserr, ok := r.Error.(awserr.Error); ok {
137 m.SetFinalException(getMetricException(awserr))
143 // TODO: Probably want to figure something out for logging dropped
145 rep.metricsCh.Push(m)
148 func (rep *Reporter) connect(network, url string) error {
153 conn, err := net.Dial(network, url)
155 return awserr.New("UDPError", "Could not connect", err)
163 func (rep *Reporter) close() {
168 rep.metricsCh.Pause()
171 func (rep *Reporter) start() {
173 rep.metricsCh.Pause()
181 case m := <-rep.metricsCh.ch:
182 // TODO: What to do with this error? Probably should just log
183 b, err := json.Marshal(m)
193 // Pause will pause the metric channel preventing any new metrics from
195 func (rep *Reporter) Pause() {
206 // Continue will reopen the metric channel and allow for monitoring
208 func (rep *Reporter) Continue() {
215 if !rep.metricsCh.IsPaused() {
219 rep.metricsCh.Continue()
222 // InjectHandlers will will enable client side metrics and inject the proper
223 // handlers to handle how metrics are sent.
226 // // Start must be called in order to inject the correct handlers
227 // r, err := csm.Start("clientID", "127.0.0.1:8094")
229 // panic(fmt.Errorf("expected no error, but received %v", err))
232 // sess := session.NewSession()
233 // r.InjectHandlers(&sess.Handlers)
235 // // create a new service client with our client side metric session
236 // svc := s3.New(sess)
237 func (rep *Reporter) InjectHandlers(handlers *request.Handlers) {
242 handlers.Complete.PushFrontNamed(request.NamedHandler{
243 Name: APICallMetricHandlerName,
244 Fn: rep.sendAPICallMetric,
247 handlers.CompleteAttempt.PushFrontNamed(request.NamedHandler{
248 Name: APICallAttemptMetricHandlerName,
249 Fn: rep.sendAPICallAttemptMetric,
253 // boolIntValue return 1 for true and 0 for false.
254 func boolIntValue(b bool) int {