-
Notifications
You must be signed in to change notification settings - Fork 32
/
publisher.go
128 lines (114 loc) · 4.01 KB
/
publisher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package metrics
import (
"fmt"
"log"
"os"
"strings"
"time"
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/logging"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
)
const (
// MetricNameSpaceRoot is the Metric name space root.
MetricNameSpaceRoot = "AWS/CloudFormation"
//MetricNameHanderException is a metric type.
MetricNameHanderException = "HandlerException"
//MetricNameHanderDuration is a metric type.
MetricNameHanderDuration = "HandlerInvocationDuration"
//MetricNameHanderInvocationCount is a metric type.
MetricNameHanderInvocationCount = "HandlerInvocationCount"
//DimensionKeyAcionType is the Action key in the dimension.
DimensionKeyAcionType = "Action"
//DimensionKeyExceptionType is the ExceptionType in the dimension.
DimensionKeyExceptionType = "ExceptionType"
//DimensionKeyResourceType is the ResourceType in the dimension.
DimensionKeyResourceType = "ResourceType"
//ServiceInternalError ...
ServiceInternalError string = "ServiceInternal"
)
// A Publisher represents an object that publishes metrics to AWS Cloudwatch.
type Publisher struct {
client cloudwatchiface.CloudWatchAPI // AWS CloudWatch Service Client
namespace string // custom resouces's namespace
logger *log.Logger
resourceType string // type of resource
}
// New creates a new Publisher.
func New(client cloudwatchiface.CloudWatchAPI, account string, resType string) *Publisher {
if len(os.Getenv("AWS_SAM_LOCAL")) > 0 {
client = newNoopClient()
}
rn := ResourceTypeName(resType)
return &Publisher{
client: client,
logger: logging.New("metrics"),
namespace: fmt.Sprintf("%s/%s/%s", MetricNameSpaceRoot, account, rn),
resourceType: rn,
}
}
// PublishExceptionMetric publishes an exception metric.
func (p *Publisher) PublishExceptionMetric(date time.Time, action string, e error) {
v := strings.ReplaceAll(e.Error(), "\n", " ")
dimensions := map[string]string{
DimensionKeyAcionType: string(action),
DimensionKeyExceptionType: v,
DimensionKeyResourceType: p.resourceType,
}
p.publishMetric(MetricNameHanderException, dimensions, cloudwatch.StandardUnitCount, 1.0, date)
}
// PublishInvocationMetric publishes an invocation metric.
func (p *Publisher) PublishInvocationMetric(date time.Time, action string) {
dimensions := map[string]string{
DimensionKeyAcionType: string(action),
DimensionKeyResourceType: p.resourceType,
}
p.publishMetric(MetricNameHanderInvocationCount, dimensions, cloudwatch.StandardUnitCount, 1.0, date)
}
// PublishDurationMetric publishes an duration metric.
//
// A duration metric is the timing of something.
func (p *Publisher) PublishDurationMetric(date time.Time, action string, secs float64) {
dimensions := map[string]string{
DimensionKeyAcionType: string(action),
DimensionKeyResourceType: p.resourceType,
}
p.publishMetric(MetricNameHanderDuration, dimensions, cloudwatch.StandardUnitMilliseconds, secs, date)
}
func (p *Publisher) publishMetric(metricName string, data map[string]string, unit string, value float64, date time.Time) {
var d []*cloudwatch.Dimension
for k, v := range data {
dim := &cloudwatch.Dimension{
Name: aws.String(k),
Value: aws.String(v),
}
d = append(d, dim)
}
md := []*cloudwatch.MetricDatum{
&cloudwatch.MetricDatum{
MetricName: aws.String(metricName),
Unit: aws.String(unit),
Value: aws.Float64(value),
Dimensions: d,
Timestamp: &date},
}
pi := cloudwatch.PutMetricDataInput{
Namespace: aws.String(p.namespace),
MetricData: md,
}
_, err := p.client.PutMetricData(&pi)
if err != nil {
p.logger.Printf("An error occurred while publishing metrics: %s", err)
}
}
// ResourceTypeName returns a type name by removing (::) and replaing with (/)
//
// Example
//
// r := metrics.ResourceTypeName("AWS::Service::Resource")
//
// // Will return "AWS/Service/Resource"
func ResourceTypeName(t string) string {
return strings.ReplaceAll(t, "::", "/")
}