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

Metrics - Cloudwatch v2 #668

Merged
merged 6 commits into from
Mar 6, 2018
Merged

Metrics - Cloudwatch v2 #668

merged 6 commits into from
Mar 6, 2018

Conversation

nelz9999
Copy link
Contributor

Per the conversation in the Slack channel: https://gophers.slack.com/archives/C04S3T99A/p1519410405000323

I have created the metrics/cloudwatch2 package.

This package targets using the V2 SDK (https://github.com/aws/aws-sdk-go-v2). It also opts for using the CloudWatch native client-side aggregation into "StatisticsSet" -
https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_StatisticSet.html. (Whereas the existing metrics/cloudwatch package does addition lossy aggregations and just emits a single "Value".) Also, as per CloudWatch idiom, Counters / Histograms / Gauges are all sent to CloudWatch in exactly the same way.

(I mostly cribbed from the existing cloudwatch package, simplified the work it does, and touched up around the edges to adjust to the delta in API between v1 and v2 versions of the AWS SDK.)

Since Histograms/Gauges all reduce down to the exact same behavior as Counters, I created the convert package to easily swap in Histogram/Gauge API semantics. I made it its own package in case any other metrics systems have a need for similar generification... Though if this is an overshoot, I could dumb that down and just isolate it into the cloudwatch2 package.

Copy link
Contributor

@groob groob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits.


type option func(*CloudWatch)

func (cw *CloudWatch) apply(opt option) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is never used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, yup. That's what I get for doin' a copypasta.

svc: svc,
counters: lv.NewSpace(),
numConcurrentRequests: 10,
logger: log.NewLogfmtLogger(os.Stderr),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default should be NewNopLogger

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// manually or with one of the helper methods.
func New(namespace string, svc cloudwatchiface.CloudWatchAPI, options ...option) *CloudWatch {
cw := &CloudWatch{
sem: nil, // set below
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this field be omitted since it's set below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

}

var zero = float64(0.0)
var zeros = cloudwatch.StatisticSet{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is only used once, any reason it should be a package var?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intent was to avoid paying the "construction costs" every time a zero set is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Should I do something different to make that apparent?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just a light comment could work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing!


var errors = make(chan error, len(batches))
for _, batch := range batches {
go func(batch []cloudwatch.MetricDatum) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the goroutines could run as an errorgroup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh, cool! I hadn't yet added errorgroup to my toolbelt. Added!

@nelz9999
Copy link
Contributor Author

@peterbourgon How are you feeling about this PR?

Copy link
Member

@peterbourgon peterbourgon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely bit of code to read. A few nits.

numConcurrentRequests int
}

type option func(*CloudWatch)
Copy link
Member

@peterbourgon peterbourgon Mar 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is returned by exported functions, it's nicer to also make it exported. (And commented, etc.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.


// WithLogger sets the Logger that will recieve error messages generated
// during the WriteLoop
func WithLogger(logger log.Logger) option {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it's nice if functional options describe the default behavior if the option isn't invoked. In this case, it might be nice to add: "By default, no logger is used."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing...

// WithConcurrentRequests sets the upper limit on how many
// cloudwatch.PutMetricDataRequest may be under way at any
// given time. If n is greater than 20, 20 is used.
func WithConcurrentRequests(n int) option {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly: "By default, 10 concurrent requests are used."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding...


"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/cloudwatchiface"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package cloudwatchiface 🤦‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah. It's supposed to be in support of mocking out their service... But it feels so icky.


func makeDimensions(labelValues ...string) []cloudwatch.Dimension {
dimensions := make([]cloudwatch.Dimension, len(labelValues)/2)
for i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha! Wild. Nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't take credit for this... I lifted this directly from the existing cloudwatch package.

@@ -0,0 +1,135 @@
// Package convert provides a way to use Counters, Histograms, or Gauges
// as one of the other types
package convert
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this is tailor-made to cloudwatch2, and I'm a little bit scared that folks will see it and think it's something they should be doing in their own code. Would you be OK with moving this underneath the internal/ dir?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

}
}

type mockCloudWatch struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😊

h := NewCounterAsHistogram(c)
top := NewHistogramAsCounter(h).With("label", "counter").(histogramCounter)
mid := top.h.(counterHistogram)
low := mid.c.(*generic.Counter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hehe, nice!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙇

@nelz9999
Copy link
Contributor Author

nelz9999 commented Mar 3, 2018

@peterbourgon Back atcha! 😄

@peterbourgon
Copy link
Member

Nice! Thanks!

@peterbourgon peterbourgon merged commit 7b0e27d into go-kit:master Mar 6, 2018
@nelz9999 nelz9999 deleted the cloudwatch-v2 branch March 6, 2018 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants