Skip to content

Commit 15659dc

Browse files
brianterrystilvoid
authored andcommitted
Fix metric namespace (#108)
* Updated metric namespace to match other plugins * Add accountID to constructor * Update start logic to avoid a non lambda start * Convert SetResourceTypeName method to a ResourceTypeName function * Update testing for thr function * remove code duplication * remove code duplication * Don't send metric data when no tag is provided * log metric errors and move on * Remove metric build tag because it is not needed * Regenerate Makefile when cfn generate is ran * Run pre-commit * Fix name typo * Update test resource name * simplify Start function logic
1 parent 68ab223 commit 15659dc

File tree

7 files changed

+114
-368
lines changed

7 files changed

+114
-368
lines changed

cfn/cfn.go

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ func Start(h Handler) {
7676

7777
// MODE is an environment variable that is set ONLY
7878
// when contract test are performed.
79-
if mode, ok := os.LookupEnv("MODE"); ok == true {
80-
if mode == "Test" {
81-
lambda.Start(makeTestEventFunc(h))
79+
mode, _ := os.LookupEnv("MODE")
8280

83-
} else {
84-
lambda.Start(makeEventFunc(h))
85-
}
81+
if mode == "Test" {
82+
lambda.Start(makeTestEventFunc(h))
83+
} else {
84+
lambda.Start(makeEventFunc(h))
8685
}
86+
8787
}
8888

8989
// Tags are stored as key/value paired strings
@@ -135,37 +135,21 @@ func invoke(handlerFn handlerFunc, request handler.Request, metricsPublisher *me
135135
// Create a channel to received a signal that work is done.
136136
ch := make(chan handler.ProgressEvent, 1)
137137

138-
// Create a channel to received error.
139-
cherror := make(chan error, 1)
140-
141138
// Ask the goroutine to do some work for us.
142139
go func() {
143140
//start the timer
144141
start := time.Now()
145-
if err := metricsPublisher.PublishInvocationMetric(time.Now(), string(action)); err != nil {
146-
cherror <- err
147-
}
142+
metricsPublisher.PublishInvocationMetric(time.Now(), string(action))
148143

149144
// Report the work is done.
150145
progEvt := handlerFn(request)
151-
152146
elapsed := time.Since(start)
153-
154-
if err := metricsPublisher.PublishDurationMetric(time.Now(), string(action), elapsed.Seconds()*1e3); err != nil {
155-
cherror <- err
156-
}
157-
147+
metricsPublisher.PublishDurationMetric(time.Now(), string(action), elapsed.Seconds()*1e3)
158148
ch <- progEvt
159149
}()
160150

161151
// Wait for the work to finish. If it takes too long move on. If the function returns an error, signal the error channel.
162152
select {
163-
case e := <-cherror:
164-
cfnErr := cfnerr.New(timeoutError, "Handler error", e)
165-
metricsPublisher.PublishExceptionMetric(time.Now(), string(action), cfnErr)
166-
//The handler returned an error.
167-
return handler.ProgressEvent{}, e
168-
169153
case d := <-ch:
170154
//Return the response from the handler.
171155
return d, nil
@@ -263,8 +247,7 @@ func makeEventFunc(h Handler) eventFunc {
263247
// Set default logger to output to CWL in the provider account
264248
logging.SetProviderLogOutput(logsProvider)
265249

266-
metricsPublisher := metrics.New(cloudwatch.New(platformSession))
267-
metricsPublisher.SetResourceTypeName(event.ResourceType)
250+
metricsPublisher := metrics.New(cloudwatch.New(platformSession), event.AWSAccountID, event.ResourceType)
268251
callbackAdapter := callback.New(cloudformation.New(platformSession), event.BearerToken)
269252
invokeScheduler := scheduler.New(cloudwatchevents.New(platformSession))
270253
re := newReportErr(callbackAdapter, metricsPublisher)

cfn/entry_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ func TestHandler(t *testing.T) {
128128

129129
func TestInvoke(t *testing.T) {
130130
mockClient := NewMockedMetrics()
131-
mockPub := metrics.New(mockClient)
132-
mockPub.SetResourceTypeName("dsf::fs::sfa")
131+
mockPub := metrics.New(mockClient, "12345678", "foo::bar::test")
133132

134133
// For test purposes, set the timeout low
135134
Timeout = time.Second

cfn/metrics/publisher.go

Lines changed: 30 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
// +build metrics
2-
31
package metrics
42

53
import (
6-
"errors"
74
"fmt"
85
"log"
96
"os"
107
"strings"
118
"time"
129

13-
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/cfnerr"
1410
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/logging"
1511
"github.com/aws/aws-sdk-go/aws"
1612
"github.com/aws/aws-sdk-go/service/cloudwatch"
@@ -30,108 +26,76 @@ const (
3026
DimensionKeyAcionType = "Action"
3127
//DimensionKeyExceptionType is the ExceptionType in the dimension.
3228
DimensionKeyExceptionType = "ExceptionType"
33-
//DimensionKeyResouceType is the ResourceType in the dimension.
34-
DimensionKeyResouceType = "ResourceType"
29+
//DimensionKeyResourceType is the ResourceType in the dimension.
30+
DimensionKeyResourceType = "ResourceType"
3531
//ServiceInternalError ...
3632
ServiceInternalError string = "ServiceInternal"
3733
)
3834

3935
// A Publisher represents an object that publishes metrics to AWS Cloudwatch.
4036
type Publisher struct {
41-
client cloudwatchiface.CloudWatchAPI // AWS CloudWatch Service Client
42-
namespace string // custom resouces's namespace
43-
logger *log.Logger
37+
client cloudwatchiface.CloudWatchAPI // AWS CloudWatch Service Client
38+
namespace string // custom resouces's namespace
39+
logger *log.Logger
40+
resourceType string // type of resource
4441
}
4542

4643
// New creates a new Publisher.
47-
func New(client cloudwatchiface.CloudWatchAPI) *Publisher {
44+
func New(client cloudwatchiface.CloudWatchAPI, account string, resType string) *Publisher {
4845
if len(os.Getenv("AWS_SAM_LOCAL")) > 0 {
4946
client = newNoopClient()
5047
}
5148

49+
rn := ResourceTypeName(resType)
5250
return &Publisher{
53-
client: client,
54-
logger: logging.New("metrics"),
51+
client: client,
52+
logger: logging.New("metrics"),
53+
namespace: fmt.Sprintf("%s/%s/%s", MetricNameSpaceRoot, account, rn),
54+
resourceType: rn,
5555
}
5656
}
5757

5858
// PublishExceptionMetric publishes an exception metric.
59-
func (p *Publisher) PublishExceptionMetric(date time.Time, action string, e error) error {
60-
61-
if len(p.namespace) == 0 {
62-
message := fmt.Sprintf("Name Space was not set")
63-
err := errors.New(message)
64-
return cfnerr.New(ServiceInternalError, "Publisher error", err)
65-
}
66-
59+
func (p *Publisher) PublishExceptionMetric(date time.Time, action string, e error) {
6760
dimensions := map[string]string{
6861
DimensionKeyAcionType: string(action),
6962
DimensionKeyExceptionType: e.Error(),
70-
DimensionKeyResouceType: p.namespace,
71-
}
72-
73-
_, err := p.publishMetric(MetricNameHanderException, dimensions, cloudwatch.StandardUnitCount, 1.0, date)
74-
75-
if err != nil {
76-
return cfnerr.New(ServiceInternalError, "Publisher error", err)
63+
DimensionKeyResourceType: p.resourceType,
7764
}
7865

79-
return nil
66+
p.publishMetric(MetricNameHanderException, dimensions, cloudwatch.StandardUnitCount, 1.0, date)
8067
}
8168

8269
// PublishInvocationMetric publishes an invocation metric.
83-
func (p *Publisher) PublishInvocationMetric(date time.Time, action string) error {
84-
85-
if len(p.namespace) == 0 {
86-
message := fmt.Sprintf("Name Space was not set")
87-
err := errors.New(message)
88-
return cfnerr.New(ServiceInternalError, "Publisher error", err)
89-
}
90-
70+
func (p *Publisher) PublishInvocationMetric(date time.Time, action string) {
9171
dimensions := map[string]string{
92-
DimensionKeyAcionType: string(action),
93-
DimensionKeyResouceType: p.namespace,
72+
DimensionKeyAcionType: string(action),
73+
DimensionKeyResourceType: p.resourceType,
9474
}
9575

96-
_, err := p.publishMetric(MetricNameHanderInvocationCount, dimensions, cloudwatch.StandardUnitCount, 1.0, date)
76+
p.publishMetric(MetricNameHanderInvocationCount, dimensions, cloudwatch.StandardUnitCount, 1.0, date)
9777

98-
if err != nil {
99-
return cfnerr.New(ServiceInternalError, "Publisher error", err)
100-
}
101-
102-
return nil
10378
}
10479

10580
// PublishDurationMetric publishes an duration metric.
10681
//
10782
// A duration metric is the timing of something.
108-
func (p *Publisher) PublishDurationMetric(date time.Time, action string, secs float64) error {
109-
if len(p.namespace) == 0 {
110-
message := fmt.Sprintf("Name Space was not set")
111-
err := errors.New(message)
112-
return cfnerr.New(ServiceInternalError, "Publisher error", err)
113-
}
83+
func (p *Publisher) PublishDurationMetric(date time.Time, action string, secs float64) {
11484
dimensions := map[string]string{
115-
DimensionKeyAcionType: string(action),
116-
DimensionKeyResouceType: p.namespace,
85+
DimensionKeyAcionType: string(action),
86+
DimensionKeyResourceType: p.resourceType,
11787
}
11888

119-
_, err := p.publishMetric(MetricNameHanderDuration, dimensions, cloudwatch.StandardUnitMilliseconds, secs, date)
89+
p.publishMetric(MetricNameHanderDuration, dimensions, cloudwatch.StandardUnitMilliseconds, secs, date)
12090

121-
if err != nil {
122-
return cfnerr.New(ServiceInternalError, "Publisher error", err)
123-
}
124-
125-
return nil
12691
}
12792

128-
func (p *Publisher) publishMetric(metricName string, data map[string]string, unit string, value float64, date time.Time) (*cloudwatch.PutMetricDataOutput, error) {
93+
func (p *Publisher) publishMetric(metricName string, data map[string]string, unit string, value float64, date time.Time) {
12994

13095
var d []*cloudwatch.Dimension
13196

13297
for k, v := range data {
13398
dim := &cloudwatch.Dimension{
134-
13599
Name: aws.String(k),
136100
Value: aws.String(v),
137101
}
@@ -152,25 +116,22 @@ func (p *Publisher) publishMetric(metricName string, data map[string]string, uni
152116
MetricData: md,
153117
}
154118

155-
out, err := p.client.PutMetricData(&pi)
119+
_, err := p.client.PutMetricData(&pi)
156120

157121
if err != nil {
122+
p.logger.Printf("An error occurred while publishing metrics: %s", err)
158123

159-
return nil, cfnerr.New(ServiceInternalError, "Publisher error", err)
160124
}
161-
162-
return out, nil
163125
}
164126

165-
// SetResourceTypeName returns a type name by removing (::) and replaing with (/)
127+
// ResourceTypeName returns a type name by removing (::) and replaing with (/)
166128
//
167129
// Example
168130
//
169-
// pub := metrics.New(cw)
131+
// r := metrics.ResourceTypeName("AWS::Service::Resource")
170132
//
171133
// // Will return "AWS/Service/Resource"
172-
// pub.SetResourceTypeName("AWS::Service::Resource")
173-
func (p *Publisher) SetResourceTypeName(t string) {
174-
p.namespace = strings.ReplaceAll(t, "::", "/")
134+
func ResourceTypeName(t string) string {
135+
return strings.ReplaceAll(t, "::", "/")
175136

176137
}

0 commit comments

Comments
 (0)