Skip to content

Commit

Permalink
Add basic metrics unit tests (#2271)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ardagan authored Dec 15, 2021
1 parent 18d1d06 commit 67b06d6
Show file tree
Hide file tree
Showing 11 changed files with 603 additions and 18 deletions.
43 changes: 43 additions & 0 deletions common/metrics/defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const (
Matching
Worker
Server
UnitTestService
NumServices
)

Expand Down Expand Up @@ -1177,6 +1178,14 @@ const (
NumServerScopes
)

// -- Scopes for UnitTestService --
const (
TestScope1 = iota + NumServerScopes
TestScope2

NumUnitTestServiceScopes
)

// ScopeDefs record the scopes for all services
var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{
// common scope Names
Expand Down Expand Up @@ -1696,6 +1705,10 @@ var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{
Server: {
ServerTlsScope: {operation: "ServerTls"},
},
UnitTestService: {
TestScope1: {operation: "test_scope_1_operation"},
TestScope2: {operation: "test_scope_2_operation"},
},
}

// Common Metrics enum
Expand Down Expand Up @@ -2152,6 +2165,23 @@ const (
NumServerMetrics
)

// UnitTestService metrics enum
const (
TestCounterMetric1 = iota + NumServerMetrics
TestCounterMetric2
TestCounterRollupMetric1
TestTimerMetric1
TestTimerMetric2
TestGaugeMetric1
TestGaugeMetric2
TestBytesHistogramMetric1
TestBytesHistogramMetric2
TestDimensionlessHistogramMetric1
TestDimensionlessHistogramMetric2

NumUnitTestServiceMetrics
)

// MetricDefs record the metrics for all services
var MetricDefs = map[ServiceIdx]map[int]metricDefinition{
Common: {
Expand Down Expand Up @@ -2572,6 +2602,19 @@ var MetricDefs = map[ServiceIdx]map[int]metricDefinition{
TlsCertsExpired: NewGaugeDef("certificates_expired"),
TlsCertsExpiring: NewGaugeDef("certificates_expiring"),
},
UnitTestService: {
TestCounterMetric1: NewCounterDef("test_counter_metric_a"),
TestCounterMetric2: NewCounterDef("test_counter_metric_b"),
TestCounterRollupMetric1: NewRollupCounterDef("test_counter_rollup_metric_a", "test_counter_rollup_metric_a_rollup"),
TestTimerMetric1: NewTimerDef("test_timer_metric_a"),
TestTimerMetric2: NewTimerDef("test_timer_metric_b"),
TestGaugeMetric1: NewGaugeDef("test_gauge_metric_a"),
TestGaugeMetric2: NewGaugeDef("test_gauge_metric_b"),
TestBytesHistogramMetric1: NewBytesHistogramDef("test_bytes_histogram_metric_a"),
TestBytesHistogramMetric2: NewBytesHistogramDef("test_bytes_histogram_metric_b"),
TestDimensionlessHistogramMetric1: NewDimensionlessHistogramDef("test_bytes_histogram_metric_a"),
TestDimensionlessHistogramMetric2: NewDimensionlessHistogramDef("test_bytes_histogram_metric_b"),
},
}

// ErrorClass is an enum to help with classifying SLA vs. non-SLA errors (SLA = "service level agreement")
Expand Down
128 changes: 128 additions & 0 deletions common/metrics/metric_client_tests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package metrics

import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type (
MetricTestUtility interface {
GetClient(config *ClientConfig, scopeIdx ServiceIdx) Client
ContainsCounter(name MetricName, labels map[string]string, value int64) error
ContainsGauge(name MetricName, labels map[string]string, value float64) error
ContainsTimer(name MetricName, labels map[string]string, value time.Duration) error
ContainsHistogram(name MetricName, labels map[string]string, value int) error
CollectionSize() int
}

CounterMetadata struct {
name string
labels map[string]string
value int64
floatValue float64
}

MetricTestSuiteBase struct {
suite.Suite
MetricTestUtilityProvider func() MetricTestUtility
metricTestUtility MetricTestUtility
testClient Client
}
)

var defaultConfig = ClientConfig{
Tags: nil,
ExcludeTags: map[string][]string{},
Prefix: "",
PerUnitHistogramBoundaries: map[string][]float64{Dimensionless: {0, 10, 100}, Bytes: {1024, 2048}},
}

func (s *MetricTestSuiteBase) SetupTest() {
s.metricTestUtility = s.MetricTestUtilityProvider()
s.testClient = s.metricTestUtility.GetClient(&defaultConfig, UnitTestService)
}

func (s *MetricTestSuiteBase) TestClientReportCounter() {
s.testClient.AddCounter(
TestScope1,
TestCounterMetric1, 66)
testDef := MetricDefs[UnitTestService][TestCounterMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsCounter(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, 66))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}

func (s *MetricTestSuiteBase) TestClientReportGauge() {
s.testClient.UpdateGauge(
TestScope1,
TestGaugeMetric1, 66)
testDef := MetricDefs[UnitTestService][TestGaugeMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsGauge(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, 66))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}

func (s *MetricTestSuiteBase) TestClientReportTimer() {
targetDuration := time.Second * 100
s.testClient.RecordTimer(
TestScope1,
TestTimerMetric1, targetDuration)
testDef := MetricDefs[UnitTestService][TestTimerMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsTimer(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, targetDuration))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}

func (s *MetricTestSuiteBase) TestClientReportHistogram() {
s.testClient.RecordDistribution(
TestScope1,
TestDimensionlessHistogramMetric1, 66)
testDef := MetricDefs[UnitTestService][TestDimensionlessHistogramMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsHistogram(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, 66))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}

func (s *MetricTestSuiteBase) TestScopeReportCounter() {
s.testClient.Scope(TestScope1).AddCounter(TestCounterMetric1, 66)
testDef := MetricDefs[UnitTestService][TestCounterMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsCounter(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, 66))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}

func (s *MetricTestSuiteBase) TestScopeReportGauge() {
s.testClient.Scope(TestScope1).UpdateGauge(TestGaugeMetric1, 66)
testDef := MetricDefs[UnitTestService][TestGaugeMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsGauge(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, 66))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}

func (s *MetricTestSuiteBase) TestScopeReportTimer() {
targetDuration := time.Second * 100
s.testClient.Scope(TestScope1).RecordTimer(TestTimerMetric1, targetDuration)
testDef := MetricDefs[UnitTestService][TestTimerMetric1]
assert.NoError(s.T(), s.metricTestUtility.ContainsTimer(testDef.metricName, map[string]string{namespace: namespaceAllValue, OperationTagName: ScopeDefs[UnitTestService][TestScope1].operation}, targetDuration))
assert.Equal(s.T(), 1, s.metricTestUtility.CollectionSize())
}
2 changes: 1 addition & 1 deletion common/metrics/opentelemetry_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type (

// NewOpentelemeteryClientByReporter creates and returns a new instance of Client implementation
// serviceIdx indicates the service type in (InputhostIndex, ... StorageIndex)
func newOpentelemeteryClient(clientConfig *ClientConfig, serviceIdx ServiceIdx, reporter *OpentelemetryReporter, logger log.Logger, gaugeCache OtelGaugeCache) (Client, error) {
func newOpentelemeteryClient(clientConfig *ClientConfig, serviceIdx ServiceIdx, reporter OpentelemetryReporter, logger log.Logger, gaugeCache OtelGaugeCache) (Client, error) {
tagsFilterConfig := NewTagFilteringScopeConfig(clientConfig.ExcludeTags)

scopeWrapper := func(impl internalScope) internalScope {
Expand Down
27 changes: 16 additions & 11 deletions common/metrics/opentelemetry_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,18 @@ import (
"go.temporal.io/server/common/log/tag"
)

var _ Reporter = (OpentelemetryReporter)(nil)
var _ OpentelemetryReporter = (*opentelemetryReporterImpl)(nil)

type (
// OpentelemetryReporter is a base class for reporting metrics to opentelemetry.
OpentelemetryReporter struct {
OpentelemetryReporter interface {
NewClient(logger log.Logger, serviceIdx ServiceIdx) (Client, error)
Stop(logger log.Logger)
GetMeterMust() metric.MeterMust
}

// opentelemetryReporterImpl is a base class for reporting metrics to opentelemetry.
opentelemetryReporterImpl struct {
exporter *prometheus.Exporter
meter metric.Meter
meterMust metric.MeterMust
Expand All @@ -60,7 +69,7 @@ func newOpentelemeteryReporter(
logger log.Logger,
prometheusConfig *PrometheusConfig,
clientConfig *ClientConfig,
) (*OpentelemetryReporter, error) {
) (*opentelemetryReporterImpl, error) {
histogramBoundaries := prometheusConfig.DefaultHistogramBoundaries
if len(histogramBoundaries) == 0 {
histogramBoundaries = defaultHistogramBoundaries
Expand Down Expand Up @@ -88,7 +97,7 @@ func newOpentelemeteryReporter(
metricServer := initPrometheusListener(prometheusConfig, logger, exporter)

meter := c.Meter("temporal")
reporter := &OpentelemetryReporter{
reporter := &opentelemetryReporterImpl{
exporter: exporter,
meter: meter,
meterMust: metric.Must(meter),
Expand Down Expand Up @@ -123,23 +132,19 @@ func initPrometheusListener(config *PrometheusConfig, logger log.Logger, exporte
return server
}

func (r *OpentelemetryReporter) GetMeter() metric.Meter {
return r.meter
}

func (r *OpentelemetryReporter) GetMeterMust() metric.MeterMust {
func (r *opentelemetryReporterImpl) GetMeterMust() metric.MeterMust {
return r.meterMust
}

func (r *OpentelemetryReporter) NewClient(logger log.Logger, serviceIdx ServiceIdx) (Client, error) {
func (r *opentelemetryReporterImpl) NewClient(logger log.Logger, serviceIdx ServiceIdx) (Client, error) {
if r.gaugeCache == nil {
r.gaugeCache = NewOtelGaugeCache(r)
}

return newOpentelemeteryClient(r.clientConfig, serviceIdx, r, logger, r.gaugeCache)
}

func (r *OpentelemetryReporter) Stop(logger log.Logger) {
func (r *opentelemetryReporterImpl) Stop(logger log.Logger) {
ctx, closeCtx := context.WithTimeout(context.Background(), time.Second)
defer closeCtx()
if err := r.server.Shutdown(ctx); !(err == nil || err == http.ErrServerClosed) {
Expand Down
4 changes: 2 additions & 2 deletions common/metrics/opentelemetry_scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (
type (
opentelemetryScope struct {
serviceIdx ServiceIdx
reporter *OpentelemetryReporter
reporter OpentelemetryReporter
labels []attribute.KeyValue
tags map[string]string
rootScope *opentelemetryScope
Expand All @@ -49,7 +49,7 @@ type (

func newOpentelemetryScope(
serviceIdx ServiceIdx,
reporter *OpentelemetryReporter,
reporter OpentelemetryReporter,
rootScope *opentelemetryScope,
tags map[string]string,
defs map[int]metricDefinition,
Expand Down
4 changes: 2 additions & 2 deletions common/metrics/opentelemetry_user_scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ import (
)

type opentelemetryUserScope struct {
reporter *OpentelemetryReporter
reporter OpentelemetryReporter
labels []attribute.KeyValue
tags map[string]string

gaugeCache OtelGaugeCache
}

func newOpentelemetryUserScope(
reporter *OpentelemetryReporter,
reporter OpentelemetryReporter,
tags map[string]string,
gaugeCache OtelGaugeCache,
) *opentelemetryUserScope {
Expand Down
4 changes: 2 additions & 2 deletions common/metrics/otel_gauge_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type (
otelGaugeCache struct {
lock sync.RWMutex
gauges map[string]*gaugeValues
reporter *OpentelemetryReporter
reporter OpentelemetryReporter
}

gaugeValues struct {
Expand Down Expand Up @@ -94,7 +94,7 @@ func (o *gaugeValues) Update(tags map[string]string, value float64) {
gaugeValue.value.Store(value)
}

func NewOtelGaugeCache(reporter *OpentelemetryReporter) OtelGaugeCache {
func NewOtelGaugeCache(reporter OpentelemetryReporter) OtelGaugeCache {
return &otelGaugeCache{
lock: sync.RWMutex{},
gauges: make(map[string]*gaugeValues),
Expand Down
Loading

0 comments on commit 67b06d6

Please sign in to comment.