Skip to content

Commit 04a2025

Browse files
Metrics: add attributes and switch to snake_case (#63)
* Metrics: add attributes and switch to camel_case * Fix examples
1 parent 3113629 commit 04a2025

File tree

10 files changed

+161
-40
lines changed

10 files changed

+161
-40
lines changed

examples/client/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"net/http"
2222
"time"
2323

24+
"github.com/scalyr/dataset-go/pkg/meter_config"
25+
2426
"go.opentelemetry.io/otel"
2527

2628
"github.com/scalyr/dataset-go/pkg/server_host_config"
@@ -67,7 +69,7 @@ func main() {
6769
&http.Client{},
6870
zap.Must(zap.NewDevelopment()),
6971
&libraryConsumerUserAgentSuffix,
70-
&meter,
72+
meter_config.NewMeterConfig(&meter, "all", "example"),
7173
)
7274
if err != nil {
7375
panic(err)

examples/readme/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"net/http"
2222
"time"
2323

24+
"github.com/scalyr/dataset-go/pkg/meter_config"
25+
2426
"go.opentelemetry.io/otel"
2527

2628
"github.com/scalyr/dataset-go/pkg/server_host_config"
@@ -84,7 +86,7 @@ func main() {
8486
libraryConsumerUserAgentSuffix := "OtelCollector-readme;1.2.3"
8587
meter := otel.Meter("example.readme")
8688
// build client
87-
cl, err := client.NewClient(cfg, &http.Client{}, logger, &libraryConsumerUserAgentSuffix, &meter)
89+
cl, err := client.NewClient(cfg, &http.Client{}, logger, &libraryConsumerUserAgentSuffix, meter_config.NewMeterConfig(&meter, "all", "example"))
8890
if err != nil {
8991
panic(err)
9092
}

pkg/client/add_events_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ import (
3131
"testing"
3232
"time"
3333

34-
"go.opentelemetry.io/otel"
35-
"go.opentelemetry.io/otel/metric"
34+
"github.com/scalyr/dataset-go/pkg/meter_config"
3635

3736
"github.com/scalyr/dataset-go/pkg/server_host_config"
37+
"go.opentelemetry.io/otel"
3838

3939
"github.com/stretchr/testify/assert"
4040
"github.com/stretchr/testify/require"
@@ -241,15 +241,15 @@ func TestAddEvents(t *testing.T) {
241241
meter := otel.Meter("test")
242242
tests := []struct {
243243
name string
244-
meter *metric.Meter
244+
meter *meter_config.MeterConfig
245245
}{
246246
{
247247
name: "no meter",
248248
meter: nil,
249249
},
250250
{
251251
name: "with meter",
252-
meter: &meter,
252+
meter: meter_config.NewMeterConfig(&meter, "e", "n"),
253253
},
254254
}
255255
for _, tt := range tests {

pkg/client/client.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
"sync/atomic"
2828
"time"
2929

30+
"github.com/scalyr/dataset-go/pkg/meter_config"
31+
3032
"golang.org/x/exp/slices"
3133

3234
"github.com/scalyr/dataset-go/pkg/version"
@@ -43,7 +45,6 @@ import (
4345

4446
"github.com/cskr/pubsub"
4547
"github.com/google/uuid"
46-
"go.opentelemetry.io/otel/metric"
4748
"go.uber.org/zap"
4849
)
4950

@@ -104,7 +105,7 @@ func NewClient(
104105
client *http.Client,
105106
logger *zap.Logger,
106107
userAgentSuffix *string,
107-
meter *metric.Meter,
108+
meterConfig *meter_config.MeterConfig,
108109
) (*DataSetClient, error) {
109110
logger.Info(
110111
"Using config: ",
@@ -158,7 +159,7 @@ func NewClient(
158159
}
159160
logger.Info("Using User-Agent: ", zap.String("User-Agent", userAgent))
160161

161-
stats, err := statistics.NewStatistics(meter, logger)
162+
stats, err := statistics.NewStatistics(meterConfig, logger)
162163
if err != nil {
163164
return nil, fmt.Errorf("it was not possible to create statistics: %w", err)
164165
}

pkg/meter_config/meter_config.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2023 SentinelOne, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package meter_config
18+
19+
import "go.opentelemetry.io/otel/metric"
20+
21+
type MeterConfig struct {
22+
entity string
23+
name string
24+
meter *metric.Meter
25+
}
26+
27+
func NewMeterConfig(meter *metric.Meter, entity string, name string) *MeterConfig {
28+
return &MeterConfig{
29+
entity: entity,
30+
name: name,
31+
meter: meter,
32+
}
33+
}
34+
35+
func (c *MeterConfig) Entity() string {
36+
return c.entity
37+
}
38+
39+
func (c *MeterConfig) Name() string {
40+
return c.name
41+
}
42+
43+
func (c *MeterConfig) Meter() *metric.Meter {
44+
return c.meter
45+
}

pkg/meter_config/meter_config_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2023 SentinelOne, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package meter_config
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
"go.opentelemetry.io/otel"
24+
)
25+
26+
func TestNewMeterConfig(t *testing.T) {
27+
meter := otel.Meter("AAA")
28+
cfg := NewMeterConfig(&meter, "entity", "name")
29+
assert.Equal(t, "entity", cfg.Entity())
30+
assert.Equal(t, "name", cfg.Name())
31+
assert.NotNil(t, cfg.Meter())
32+
}

pkg/statistics/statistics.go

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import (
2222
"sync/atomic"
2323
"time"
2424

25+
"github.com/scalyr/dataset-go/pkg/meter_config"
26+
"go.opentelemetry.io/otel/attribute"
27+
2528
"go.uber.org/zap"
2629

2730
"github.com/scalyr/dataset-go/pkg/buffer_config"
@@ -44,8 +47,10 @@ type Statistics struct {
4447
bytesAPISent atomic.Uint64
4548
bytesAPIAccepted atomic.Uint64
4649

47-
meter *metric.Meter
48-
logger *zap.Logger
50+
config *meter_config.MeterConfig
51+
meter *metric.Meter
52+
logger *zap.Logger
53+
attributes []attribute.KeyValue
4954

5055
cBuffersEnqueued metric.Int64UpDownCounter
5156
cBuffersProcessed metric.Int64UpDownCounter
@@ -67,7 +72,7 @@ type Statistics struct {
6772
// NewStatistics creates structure to keep track of data processing.
6873
// If meter is not nil, then Open Telemetry is used for collecting metrics
6974
// as well.
70-
func NewStatistics(meter *metric.Meter, logger *zap.Logger) (*Statistics, error) {
75+
func NewStatistics(config *meter_config.MeterConfig, logger *zap.Logger) (*Statistics, error) {
7176
logger.Info("Initialising statistics")
7277
statistics := &Statistics{
7378
buffersEnqueued: atomic.Uint64{},
@@ -83,8 +88,10 @@ func NewStatistics(meter *metric.Meter, logger *zap.Logger) (*Statistics, error)
8388
bytesAPIAccepted: atomic.Uint64{},
8489
bytesAPISent: atomic.Uint64{},
8590

86-
meter: meter,
87-
logger: logger,
91+
config: config,
92+
meter: nil,
93+
logger: logger,
94+
attributes: []attribute.KeyValue{},
8895
}
8996

9097
err := statistics.initMetrics()
@@ -97,54 +104,70 @@ func key(key string) string {
97104
}
98105

99106
func (stats *Statistics) initMetrics() error {
107+
// if there is no config, there is no need to initialise counters
108+
if stats.config == nil {
109+
stats.logger.Info("OTel metrics WILL NOT be collected (MeterConfig is nil)")
110+
return nil
111+
}
112+
113+
// update meter with config meter
114+
stats.meter = stats.config.Meter()
100115
meter := stats.meter
116+
101117
// if there is no meter, there is no need to initialise counters
102118
if meter == nil {
103-
stats.logger.Info("OTel metrics WILL NOT be collected")
119+
stats.logger.Info("OTel metrics WILL NOT be collected (Meter is nil)")
104120
return nil
105121
}
106122
stats.logger.Info("OTel metrics WILL be collected")
107123

124+
// set attributes so that we can track multiple instances
125+
stats.attributes = []attribute.KeyValue{
126+
{Key: "entity", Value: attribute.StringValue(stats.config.Entity())},
127+
{Key: "name", Value: attribute.StringValue(stats.config.Name())},
128+
}
129+
metric.WithAttributes(stats.attributes...)
130+
108131
err := error(nil)
109-
stats.cBuffersEnqueued, err = (*meter).Int64UpDownCounter(key("buffersEnqueued"))
132+
stats.cBuffersEnqueued, err = (*meter).Int64UpDownCounter(key("buffers_enqueued"))
110133
if err != nil {
111134
return err
112135
}
113-
stats.cBuffersProcessed, err = (*meter).Int64UpDownCounter(key("buffersProcessed"))
136+
stats.cBuffersProcessed, err = (*meter).Int64UpDownCounter(key("buffers_processed"))
114137
if err != nil {
115138
return err
116139
}
117-
stats.cBuffersDropped, err = (*meter).Int64UpDownCounter(key("buffersDropped"))
140+
stats.cBuffersDropped, err = (*meter).Int64UpDownCounter(key("buffers_dropped"))
118141
if err != nil {
119142
return err
120143
}
121-
stats.cBuffersBroken, err = (*meter).Int64UpDownCounter(key("buffersBroken"))
144+
stats.cBuffersBroken, err = (*meter).Int64UpDownCounter(key("buffers_broken"))
122145
if err != nil {
123146
return err
124147
}
125148

126-
stats.cEventsEnqueued, err = (*meter).Int64UpDownCounter(key("eventsEnqueued"))
149+
stats.cEventsEnqueued, err = (*meter).Int64UpDownCounter(key("events_enqueued"))
127150
if err != nil {
128151
return err
129152
}
130-
stats.cEventsProcessed, err = (*meter).Int64UpDownCounter(key("eventsProcessed"))
153+
stats.cEventsProcessed, err = (*meter).Int64UpDownCounter(key("events_processed"))
131154
if err != nil {
132155
return err
133156
}
134-
stats.cEventsDropped, err = (*meter).Int64UpDownCounter(key("eventsDropped"))
157+
stats.cEventsDropped, err = (*meter).Int64UpDownCounter(key("events_dropped"))
135158
if err != nil {
136159
return err
137160
}
138-
stats.cEventsBroken, err = (*meter).Int64UpDownCounter(key("eventsBroken"))
161+
stats.cEventsBroken, err = (*meter).Int64UpDownCounter(key("events_broken"))
139162
if err != nil {
140163
return err
141164
}
142165

143-
stats.cBytesAPISent, err = (*meter).Int64UpDownCounter(key("bytesAPISent"))
166+
stats.cBytesAPISent, err = (*meter).Int64UpDownCounter(key("bytes_api_sent"))
144167
if err != nil {
145168
return err
146169
}
147-
stats.cBytesAPIAccepted, err = (*meter).Int64UpDownCounter(key("bytesAPIAccepted"))
170+
stats.cBytesAPIAccepted, err = (*meter).Int64UpDownCounter(key("bytes_api_accepted"))
148171
if err != nil {
149172
return err
150173
}
@@ -158,7 +181,7 @@ func (stats *Statistics) initMetrics() error {
158181
zap.Float64s("buckets", payloadBuckets),
159182
)
160183
stats.hPayloadSize, err = (*meter).Int64Histogram(key(
161-
"payloadSize"),
184+
"payload_size"),
162185
metric.WithExplicitBucketBoundaries(payloadBuckets...),
163186
metric.WithUnit("b"),
164187
)
@@ -175,7 +198,7 @@ func (stats *Statistics) initMetrics() error {
175198
zap.Float64s("buckets", responseBuckets),
176199
)
177200
stats.hResponseTime, err = (*meter).Int64Histogram(key(
178-
"responseTime"),
201+
"response_time"),
179202
metric.WithExplicitBucketBoundaries(responseBuckets...),
180203
metric.WithUnit("ms"),
181204
)
@@ -278,19 +301,31 @@ func (stats *Statistics) BytesAPIAcceptedAdd(i uint64) {
278301

279302
func (stats *Statistics) PayloadSizeRecord(payloadSizeInBytes int64) {
280303
if stats.hPayloadSize != nil {
281-
stats.hPayloadSize.Record(context.Background(), payloadSizeInBytes)
304+
stats.hPayloadSize.Record(
305+
context.Background(),
306+
payloadSizeInBytes,
307+
metric.WithAttributes(stats.attributes...),
308+
)
282309
}
283310
}
284311

285312
func (stats *Statistics) ResponseTimeRecord(duration time.Duration) {
286313
if stats.hResponseTime != nil {
287-
stats.hResponseTime.Record(context.Background(), duration.Milliseconds())
314+
stats.hResponseTime.Record(
315+
context.Background(),
316+
duration.Milliseconds(),
317+
metric.WithAttributes(stats.attributes...),
318+
)
288319
}
289320
}
290321

291322
func (stats *Statistics) add(counter metric.Int64UpDownCounter, i uint64) {
292323
if counter != nil {
293-
counter.Add(context.Background(), int64(i))
324+
counter.Add(
325+
context.Background(),
326+
int64(i),
327+
metric.WithAttributes(stats.attributes...),
328+
)
294329
}
295330
}
296331

pkg/statistics/statistics_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,32 @@ import (
2121
"testing"
2222
"time"
2323

24+
"github.com/scalyr/dataset-go/pkg/meter_config"
25+
2426
"go.uber.org/zap"
2527

28+
"github.com/stretchr/testify/assert"
2629
"github.com/stretchr/testify/require"
2730
"go.opentelemetry.io/otel"
28-
"go.opentelemetry.io/otel/metric"
29-
30-
"github.com/stretchr/testify/assert"
3131
)
3232

3333
var (
3434
meter = otel.Meter("test")
3535
tests = []struct {
3636
name string
37-
meter *metric.Meter
37+
meter *meter_config.MeterConfig
3838
}{
3939
{
40-
name: "no meter",
40+
name: "no config",
4141
meter: nil,
4242
},
43+
{
44+
name: "no meter",
45+
meter: meter_config.NewMeterConfig(nil, "e", "n"),
46+
},
4347
{
4448
name: "with meter",
45-
meter: &meter,
49+
meter: meter_config.NewMeterConfig(&meter, "e", "n"),
4650
},
4751
}
4852
)

0 commit comments

Comments
 (0)