Skip to content

Commit

Permalink
Updates to metrics interface, road to deprecate metrics.Reporter (#2935)
Browse files Browse the repository at this point in the history
* Updates to metrics interface, road to deprecate metrics.Reporter

* adding tag filtering support and review comments
  • Loading branch information
jbreiding committed Jun 3, 2022
1 parent e3a5e78 commit ebe2262
Show file tree
Hide file tree
Showing 49 changed files with 768 additions and 2,809 deletions.
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ PROTO_DIRS = $(sort $(dir $(PROTO_FILES)))
PROTO_IMPORTS := -I=$(PROTO_ROOT)/internal -I=$(PROTO_ROOT)/api -I=$(GOPATH)/src/github.com/temporalio/gogo-protobuf/protobuf
PROTO_OUT := api

ALL_SRC := $(shell find . -name "*.go" | grep -v -e "^$(PROTO_OUT)")

ALL_SRC := $(shell find . -name "*.go")
ALL_SRC += go.mod
# Replace below with build tags and `go test ./...` for targets
TEST_DIRS := $(sort $(dir $(filter %_test.go,$(ALL_SRC))))
INTEG_TEST_DIRS := $(filter $(INTEG_TEST_ROOT)/ $(INTEG_TEST_NDC_ROOT)/,$(TEST_DIRS))
UNIT_TEST_DIRS := $(filter-out $(INTEG_TEST_ROOT)% $(INTEG_TEST_XDC_ROOT)% $(INTEG_TEST_NDC_ROOT)%,$(TEST_DIRS))
Expand All @@ -95,8 +98,6 @@ SUMMARY_COVER_PROFILE := $(COVER_ROOT)/summary.out
# Packages are specified as import paths.
INTEG_TEST_COVERPKG := -coverpkg="$(MODULE_ROOT)/client/...,$(MODULE_ROOT)/common/...,$(MODULE_ROOT)/service/..."

ALL_SRC := $(shell find . -name "*.go")

##### Tools #####
update-checkers:
@printf $(COLOR) "Install/update check tools..."
Expand Down
59 changes: 6 additions & 53 deletions common/metrics/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
package metrics

import (
"fmt"
"time"

"github.com/cactus/go-statsd-client/statsd"
Expand Down Expand Up @@ -240,50 +239,14 @@ var (
16777216,
},
}
)

// InitMetricsReporter is a method that initializes reporter to be used inside server.
//
// Reporter is a base for reporting metrics and is used to initialize MetricsClient or UserScope.
// MetricsClient is utilized internally in server to report metrics.
// UserScope is utilized by user to report metrics.
//
// Recommended to use for current support for reporting metrics in extensions.
//
// reporter := InitMetricsReporter()
// extension := MyExtensions(reporter.UserScope())
// serverOptions.WithReporter(reporter)
func InitMetricsReporter(logger log.Logger, c *Config) (Reporter, error) {
setDefaultPerUnitHistogramBoundaries(&c.ClientConfig)
if c.Prometheus != nil && len(c.Prometheus.Framework) > 0 {
return InitReporterFromPrometheusConfig(logger, c.Prometheus, &c.ClientConfig)
defaultConfig = ClientConfig{
Tags: nil,
ExcludeTags: map[string][]string{},
Prefix: "",
PerUnitHistogramBoundaries: map[string][]float64{Dimensionless: {0, 10, 100}, Bytes: {1024, 2048}},
}
return NewTallyReporterFromConfig(logger, c)
}

func NewTallyReporterFromConfig(logger log.Logger, c *Config) (*TallyReporter, error) {
scope := NewScope(logger, c)
reporter := NewTallyReporter(scope, &c.ClientConfig)
return reporter, nil
}

// InitReporterFromPrometheusConfig initializes reporter from PrometheusConfig
//
// This is a custom case of initializing temporal metrics reporter.
func InitReporterFromPrometheusConfig(logger log.Logger, config *PrometheusConfig, clientConfig *ClientConfig) (Reporter, error) {
// TODO: We should switch to this being the only metrics reporter constructor once we decide to deprecate tally and
// custom tally configs Config.Statsd and Config.M3.
switch config.Framework {
case FrameworkTally:
return NewTallyReporterFromPrometheusConfig(logger, config, clientConfig), nil
case FrameworkOpentelemetry:
return NewOpenTelemetryReporterFromPrometheusConfig(logger, config, clientConfig)
default:
err := fmt.Errorf("unsupported framework type specified in config: %q", config.Framework)
logger.Error(err.Error())
return nil, err
}
}
)

// NewScope builds a new tally scope for this metrics configuration
//
Expand All @@ -302,16 +265,6 @@ func NewScope(logger log.Logger, c *Config) tally.Scope {
return tally.NoopScope
}

func NewTallyReporterFromPrometheusConfig(
logger log.Logger,
config *PrometheusConfig,
clientConfig *ClientConfig,
) Reporter {
tallyConfig := convertPrometheusConfigToTally(config)
tallyScope := newPrometheusScope(logger, tallyConfig, clientConfig)
return NewTallyReporter(tallyScope, clientConfig)
}

func buildHistogramBuckets(
config *PrometheusConfig,
) []prometheus.HistogramObjective {
Expand Down
91 changes: 57 additions & 34 deletions common/metrics/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,20 @@ import (
)

type (
eventMetricProvider struct {
context context.Context
tags []Tag
eventsMetricProvider struct {
tags []Tag
exporter *event.Exporter
}

// MetricHandler represents the extension point for collection instruments
// typedef of event.Handler from https://pkg.go.dev/golang.org/x/exp/event#Handler
MetricHandler = event.Handler
MetricHandler interface {
event.Handler
Stop(logger log.Logger)
}
)

var _ MetricProvider = (*eventMetricProvider)(nil)
var _ MetricProvider = (*eventsMetricProvider)(nil)

// MetricHandlerFromConfig is used at startup to construct
func MetricHandlerFromConfig(logger log.Logger, c *Config) MetricHandler {
Expand All @@ -59,6 +62,7 @@ func MetricHandlerFromConfig(logger log.Logger, c *Config) MetricHandler {
convertPrometheusConfigToTally(c.Prometheus),
&c.ClientConfig,
),
c.ClientConfig,
c.ClientConfig.PerUnitHistogramBoundaries,
)
case FrameworkOpentelemetry:
Expand All @@ -67,78 +71,97 @@ func MetricHandlerFromConfig(logger log.Logger, c *Config) MetricHandler {
logger.Fatal(err.Error())
}

return NewOtelMetricHandler(logger, otelProvider.GetMeter())
return NewOtelMetricHandler(logger, otelProvider, c.ClientConfig)
}
}

return NewTallyMetricHandler(
logger,
NewScope(logger, c),
c.ClientConfig,
c.ClientConfig.PerUnitHistogramBoundaries,
)
}

// NewEventMetricProvider provides an eventMetricProvider given event.Exporter struct
func NewEventMetricProvider(h MetricHandler) *eventMetricProvider {
// NewEventsMetricProvider provides an eventsMetricProvider given event.Exporter struct
func NewEventsMetricProvider(h MetricHandler) eventsMetricProvider {
eo := &event.ExporterOptions{
DisableLogging: true,
DisableTracing: true,
DisableLogging: true,
DisableTracing: true,
EnableNamespaces: false,
}

return &eventMetricProvider{
context: event.WithExporter(context.Background(), event.NewExporter(h, eo)),
tags: []Tag{},
return eventsMetricProvider{
exporter: event.NewExporter(h, eo),
tags: []Tag{},
}
}

// WithTags creates a new MetricProvder with provided []Tag
// Tags are merged with registered Tags from the source MetricProvider
func (emp *eventMetricProvider) WithTags(tags ...Tag) MetricProvider {
return &eventMetricProvider{
context: emp.context,
tags: append(emp.tags, tags...),
func (emp eventsMetricProvider) WithTags(tags ...Tag) MetricProvider {
var t []Tag
t = append(t, emp.tags...)
return &eventsMetricProvider{
exporter: emp.exporter,
tags: append(t, tags...),
}
}

// Counter obtains a counter for the given name.
func (emp *eventMetricProvider) Counter(n string, m *MetricOptions) CounterMetric {
func (emp eventsMetricProvider) Counter(n string, m *MetricOptions) CounterMetric {
e := event.NewCounter(n, m)
return CounterMetricFunc(func(i int64, t ...Tag) {
e := event.NewCounter(n, m)
e.Record(emp.context, i, emp.tagsToLabels(t)...)
e.Record(
event.WithExporter(context.Background(), emp.exporter),
i,
tagsToLabels(emp.tags, t)...)
})
}

// Gauge obtains a gauge for the given name.
func (emp *eventMetricProvider) Gauge(n string, m *MetricOptions) GaugeMetric {
func (emp eventsMetricProvider) Gauge(n string, m *MetricOptions) GaugeMetric {
e := event.NewFloatGauge(n, m)
return GaugeMetricFunc(func(f float64, t ...Tag) {
e := event.NewFloatGauge(n, m)
e.Record(emp.context, f, emp.tagsToLabels(t)...)
e.Record(
event.WithExporter(context.Background(), emp.exporter),
f,
tagsToLabels(emp.tags, t)...)
})
}

// Timer obtains a timer for the given name.
func (emp *eventMetricProvider) Timer(n string, m *MetricOptions) TimerMetric {
func (emp eventsMetricProvider) Timer(n string, m *MetricOptions) TimerMetric {
e := event.NewDuration(n, m)
return TimerMetricFunc(func(d time.Duration, t ...Tag) {
e := event.NewDuration(n, m)
e.Record(emp.context, d, emp.tagsToLabels(t)...)
e.Record(
event.WithExporter(context.Background(), emp.exporter),
d,
tagsToLabels(emp.tags, t)...)
})
}

// Histogram obtains a histogram for the given name.
func (emp *eventMetricProvider) Histogram(n string, m *MetricOptions) HistogramMetric {
func (emp eventsMetricProvider) Histogram(n string, m *MetricOptions) HistogramMetric {
e := event.NewIntDistribution(n, m)
return HistogramMetricFunc(func(i int64, t ...Tag) {
e := event.NewIntDistribution(n, m)
e.Record(emp.context, i, emp.tagsToLabels(t)...)
e.Record(
event.WithExporter(context.Background(), emp.exporter),
i,
tagsToLabels(emp.tags, t)...)
})
}

// tagsToLabels helper to merge registred tags and additional tags converting to event.Label struct
func (emp *eventMetricProvider) tagsToLabels(tags []Tag) []event.Label {
l := make([]event.Label, len(emp.tags)+len(tags))
t := append(emp.tags, tags...)
func tagsToLabels(t1 []Tag, t2 []Tag) []event.Label {
l := make([]event.Label, 0, len(t1)+len(t2))

for i := range t1 {
l = append(l, event.String(t1[i].Key(), t1[i].Value()))
}

for i := range t {
l[i] = event.String(t[i].Key(), t[i].Value())
for i := range t2 {
l = append(l, event.String(t2[i].Key(), t2[i].Value()))
}

return l
Expand Down
99 changes: 99 additions & 0 deletions common/metrics/events_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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 (
"sync"
"time"
)

type (
eventsClient struct {
provider MetricProvider
serviceIdx ServiceIdx

scopes *sync.Map
}
)

var _ Client = (*eventsClient)(nil)

func NewEventsClient(provider MetricProvider, idx ServiceIdx) *eventsClient {
serviceTypeTagValue, _ := MetricsServiceIdxToServiceName(idx)

return &eventsClient{
provider: provider.WithTags(ServiceTypeTag(serviceTypeTagValue), NamespaceTag(namespaceAllValue)),
serviceIdx: idx,
scopes: new(sync.Map),
}
}

// IncCounter increments a counter metric
func (e eventsClient) IncCounter(scope int, counter int) {
e.Scope(scope).IncCounter(counter)
}

// AddCounter adds delta to the counter metric
func (e eventsClient) AddCounter(scope int, counter int, delta int64) {
e.Scope(scope).AddCounter(counter, delta)
}

// StartTimer starts a timer for the given
// metric name. Time will be recorded when stopwatch is stopped.
func (e eventsClient) StartTimer(scope int, timer int) Stopwatch {
return e.Scope(scope).StartTimer(timer)
}

// RecordTimer starts a timer for the given
// metric name
func (e eventsClient) RecordTimer(scope int, timer int, d time.Duration) {
e.Scope(scope).RecordTimer(timer, d)
}

// RecordDistribution records and emits a distribution (wrapper on top of timer) for the given
// metric name
func (e eventsClient) RecordDistribution(scope int, timer int, d int) {
e.Scope(scope).RecordDistribution(timer, d)
}

// UpdateGauge reports Gauge type absolute value metric
func (e eventsClient) UpdateGauge(scope int, gauge int, value float64) {
e.Scope(scope).UpdateGauge(gauge, value)
}

// Scope returns an internal scope that can be used to add additional
// information to metrics
func (e eventsClient) Scope(scope int, tags ...Tag) Scope {
s, ok := e.scopes.Load(scope)

if !ok {
if s, ok = e.scopes.Load(scope); !ok {
s = newEventsScope(e.provider.WithTags(tags...), e.serviceIdx, scope)
e.scopes.Store(scope, s.(Scope))
}
}

return s.(Scope)
}

0 comments on commit ebe2262

Please sign in to comment.