-
Notifications
You must be signed in to change notification settings - Fork 90
/
cloudwatch.go
161 lines (132 loc) · 3.76 KB
/
cloudwatch.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package cloudwatch
import (
"fmt"
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
)
func init() {
context.SetHandlerName("github.com/iris-contrib/middleware/cloudwatch.*", "iris-contrib.cloudwatch")
}
// PutMetricContextKey is the context key which the metrics are stored.
const PutMetricContextKey = "PUT_METRIC"
// GetPutFunc returns the put func based on the context,
// this can be called to add more metrics after the middleware's handler is executed.
func GetPutFunc(ctx iris.Context) func([]*cloudwatch.MetricDatum) {
put := ctx.Values().Get(PutMetricContextKey)
if put == nil {
return nil
}
if putFunc, ok := put.(func([]*cloudwatch.MetricDatum)); ok {
return putFunc
}
return nil
}
// BeforeFunc called before handler
type BeforeFunc func(iris.Context, *Cloudwatch)
// AfterFunc is the func type called after calling the next func in
// the middleware chain
type AfterFunc func(iris.Context, time.Duration, *Cloudwatch)
// Cloudwatch is the metrics handler.
type Cloudwatch struct {
// CloudWatch underline Service
Service *cloudwatch.CloudWatch
// CloudWatch underline namespace
Namespace string
// Latency underlime metric name
LatencyMetricName string
Before BeforeFunc
After AfterFunc
PutMetric func(data []*cloudwatch.MetricDatum)
// ExcludeURLs from logging
ExcludeURLs []string
}
// New retruns a new *Cloudwatch metrics middleware
// call its ServeHTTP to adapt it on the chain.
func New(region, namespace string) *Cloudwatch {
cw := &Cloudwatch{
Service: cloudwatch.New(session.New(), aws.NewConfig().WithRegion(region).WithMaxRetries(5)),
Namespace: namespace,
LatencyMetricName: "Latency",
Before: DefaultBefore,
After: DefaultAfter,
}
cw.PutMetric = func(data []*cloudwatch.MetricDatum) {
putMetric(cw, data)
}
return cw
}
func putMetric(cw *Cloudwatch, data []*cloudwatch.MetricDatum) {
params := &cloudwatch.PutMetricDataInput{
MetricData: data,
Namespace: aws.String(cw.Namespace),
}
_, err := cw.Service.PutMetricData(params)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
fmt.Println(awsErr.Code())
// if "NoSuchBucket" == awsErr.Code() {
// return *resp
// }
} else {
fmt.Println(err.Error())
}
return
}
}
func (cw *Cloudwatch) isExcludedURL(s string) bool {
for i := range cw.ExcludeURLs {
if cw.ExcludeURLs[i] == s {
return true
}
}
return false
}
func (cw *Cloudwatch) ServeHTTP(ctx iris.Context) {
if cw.Before == nil {
cw.Before = DefaultBefore
}
if cw.After == nil {
cw.After = DefaultAfter
}
r := ctx.Request()
if ok := cw.isExcludedURL(r.URL.Path); ok {
return
}
start := time.Now()
cw.Before(ctx, cw)
ctx.Next()
latency := time.Since(start)
cw.After(ctx, latency, cw)
}
// DefaultBefore is the default func assigned to *Cloudwatch.Before
func DefaultBefore(ctx iris.Context, cw *Cloudwatch) {
ctx.Values().Set(PutMetricContextKey, cw.PutMetric)
}
// DefaultAfter is the default func assigned to *Cloudwatch.After
func DefaultAfter(ctx iris.Context, latency time.Duration, cw *Cloudwatch) {
ms := float64(latency.Nanoseconds() * 1000)
cw.PutMetric([]*cloudwatch.MetricDatum{
{
MetricName: aws.String(cw.LatencyMetricName),
Dimensions: []*cloudwatch.Dimension{
{
Name: aws.String("RequestURI"),
Value: aws.String(ctx.Request().RequestURI),
},
{
Name: aws.String("RemoteAddr"),
Value: aws.String(ctx.RemoteAddr()),
},
},
Timestamp: aws.Time(time.Now()),
Unit: aws.String("Microseconds"),
Value: aws.Float64(ms),
},
})
ctx.Values().Remove(PutMetricContextKey)
}