Skip to content
This repository was archived by the owner on Jan 20, 2026. It is now read-only.

Commit 9590e75

Browse files
committed
Added dogstatsd monitoring service
Signed-off-by: Caleb Stewart <caleb.stewart94@gmail.com>
1 parent b12921d commit 9590e75

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package dogstatsd
2+
3+
import (
4+
"sync/atomic"
5+
6+
"github.com/DataDog/datadog-go/v5/statsd"
7+
8+
"github.com/vmware/vmware-go-kcl-v2/logger"
9+
)
10+
11+
const (
12+
// Labels attached to metric submissions
13+
streamNameLabel = "kinesis.stream"
14+
shardIdLabel = "kinesis.shardId"
15+
workerIdLabel = "kcl.workerId"
16+
applicationNameLabel = "kcl.application-name"
17+
18+
// Names for the metrics submitted below
19+
recordsProcessedMetric = "kcl.records_processed"
20+
bytesProcessedMetric = "kcl.bytes_processed"
21+
millisBehindLatestMetric = "kcl.millis_behind_latest"
22+
shardLeasesOwnedMetric = "kcl.shard_leases_owned"
23+
getRecordsTimeMetric = "kcl.get_records_time"
24+
processRecordsTimeMetric = "kcl.process_records_time"
25+
)
26+
27+
// MonitoringService publishes KCL metrics to Datadog using the dogstatsd client
28+
// package.
29+
type MonitoringService struct {
30+
Client statsd.ClientInterface // Client used to push metrics
31+
SamplingRate float64 // Sampling rate for all metrics
32+
Logger logger.Logger // Logger used for metric push errors
33+
34+
leaseCount atomic.Int64 // Number of leases current held
35+
tags []string // List of tags to send with every metric
36+
}
37+
38+
// New creates a new dogstatsd monitoring service. The given sampling rate will
39+
// be used for all submitted metrics. The logger is only used if submissions
40+
// fail.
41+
func New(client statsd.ClientInterface, samplingRate float64, logger logger.Logger) *MonitoringService {
42+
return &MonitoringService{
43+
Client: client,
44+
SamplingRate: samplingRate,
45+
Logger: logger,
46+
leaseCount: atomic.Int64{},
47+
tags: []string{},
48+
}
49+
}
50+
51+
func (s *MonitoringService) Init(appName, streamName, workerID string) (err error) {
52+
s.tags = []string{
53+
applicationNameLabel + ":" + appName,
54+
streamNameLabel + ":" + streamName,
55+
workerIdLabel + ":" + workerID,
56+
}
57+
return err
58+
}
59+
60+
func (s *MonitoringService) Start() error {
61+
return nil
62+
}
63+
64+
func (s *MonitoringService) Shutdown() {}
65+
66+
// If the error is non-nil, log it. This should be inlined by the
67+
// compiler so no overhead, and simplifies the metrics methods below,
68+
// since the log entry is essentially always the same.
69+
func (s *MonitoringService) logFailure(shard string, metric string, err error) {
70+
if err != nil {
71+
s.Logger.WithFields(logger.Fields{
72+
"error": err,
73+
"shardId": shard,
74+
"metric": metric,
75+
}).Errorf("failed to push metric")
76+
}
77+
}
78+
79+
// Add the tags for a specific metric to the global monitoring service tags
80+
func (s *MonitoringService) buildTags(tags ...string) []string {
81+
return append(tags, s.tags...)
82+
}
83+
84+
func (s *MonitoringService) IncrRecordsProcessed(shard string, count int) {
85+
err := s.Client.Count(
86+
recordsProcessedMetric,
87+
int64(count),
88+
s.buildTags(shardIdLabel+":"+shard),
89+
s.SamplingRate,
90+
)
91+
s.logFailure(shard, recordsProcessedMetric, err)
92+
}
93+
94+
func (s *MonitoringService) IncrBytesProcessed(shard string, count int64) {
95+
err := s.Client.Count(
96+
bytesProcessedMetric,
97+
count,
98+
s.buildTags(shardIdLabel+":"+shard),
99+
s.SamplingRate,
100+
)
101+
s.logFailure(shard, recordsProcessedMetric, err)
102+
}
103+
104+
func (s *MonitoringService) MillisBehindLatest(shard string, millis float64) {
105+
err := s.Client.Gauge(
106+
millisBehindLatestMetric,
107+
millis,
108+
s.buildTags(shardIdLabel+":"+shard),
109+
s.SamplingRate,
110+
)
111+
s.logFailure(shard, millisBehindLatestMetric, err)
112+
}
113+
114+
func (s *MonitoringService) DeleteMetricMillisBehindLatest(shard string) {
115+
s.MillisBehindLatest(shard, 0)
116+
}
117+
118+
func (s *MonitoringService) LeaseGained(shard string) {
119+
leaseCount := s.leaseCount.Add(1)
120+
err := s.Client.Gauge(
121+
shardLeasesOwnedMetric,
122+
float64(leaseCount),
123+
s.buildTags(shardIdLabel+":"+shard),
124+
s.SamplingRate,
125+
)
126+
s.logFailure(shard, shardLeasesOwnedMetric, err)
127+
}
128+
129+
func (s *MonitoringService) LeaseLost(shard string) {
130+
leaseCount := s.leaseCount.Add(-1)
131+
err := s.Client.Gauge(
132+
shardLeasesOwnedMetric,
133+
float64(leaseCount),
134+
s.buildTags(shardIdLabel+":"+shard),
135+
s.SamplingRate,
136+
)
137+
s.logFailure(shard, shardLeasesOwnedMetric, err)
138+
}
139+
140+
func (s *MonitoringService) LeaseRenewed(shard string) {
141+
leaseCount := s.leaseCount.Load()
142+
err := s.Client.Gauge(
143+
shardLeasesOwnedMetric,
144+
float64(leaseCount),
145+
s.buildTags(shardIdLabel+":"+shard),
146+
s.SamplingRate,
147+
)
148+
s.logFailure(shard, shardLeasesOwnedMetric, err)
149+
}
150+
151+
func (s *MonitoringService) RecordGetRecordsTime(shard string, time float64) {
152+
err := s.Client.Count(
153+
getRecordsTimeMetric,
154+
int64(time),
155+
s.buildTags(shardIdLabel+":"+shard),
156+
s.SamplingRate,
157+
)
158+
s.logFailure(shard, getRecordsTimeMetric, err)
159+
}
160+
161+
func (s *MonitoringService) RecordProcessRecordsTime(shard string, time float64) {
162+
err := s.Client.Count(
163+
processRecordsTimeMetric,
164+
int64(time),
165+
s.buildTags(shardIdLabel+":"+shard),
166+
s.SamplingRate,
167+
)
168+
s.logFailure(shard, processRecordsTimeMetric, err)
169+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ require (
2323

2424
require (
2525
github.com/BurntSushi/toml v0.4.1 // indirect
26+
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
27+
github.com/Microsoft/go-winio v0.5.0 // indirect
2628
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.0.0 // indirect
2729
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 // indirect
2830
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2 // indirect

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
3535
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
3636
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
3737
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
38+
github.com/DataDog/datadog-go/v5 v5.3.0 h1:2q2qjFOb3RwAZNU+ez27ZVDwErJv5/VpbBPprz7Z+s8=
39+
github.com/DataDog/datadog-go/v5 v5.3.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q=
40+
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
41+
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
3842
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
3943
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
4044
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -125,6 +129,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
125129
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
126130
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
127131
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
132+
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
128133
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
129134
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
130135
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -237,6 +242,7 @@ github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+
237242
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
238243
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
239244
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
245+
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
240246
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
241247
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
242248
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -449,6 +455,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
449455
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
450456
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
451457
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
458+
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
452459
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
453460
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
454461
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

0 commit comments

Comments
 (0)