Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metrics proposal for Go #2

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions api/metric/additive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package metric

type Float64AdditiveHandle struct {
Handle
}

func NewFloat64Additive(name string, mos ...Option) *Float64AdditiveHandle {
g := &Float64AdditiveHandle{}
registerMetric(name, Additive, mos, &g.Handle)
return g
}
28 changes: 28 additions & 0 deletions api/metric/aggregation/descriptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package aggregation

//go:generate stringer -type=Operator
type Operator int

const (
NONE Operator = iota
SUM
COUNT
MIN
MAX

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why min/max? Where is this used?

LAST_VALUE
DISTRIBUTION

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a distribution if a histogram is used user requires to also configure the buckets. If a summary is used user requires to configure the percentiles. So I cannot see how this is used.

)
29 changes: 29 additions & 0 deletions api/metric/aggregation/operator_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 42 additions & 18 deletions api/metric/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,51 @@ import (
"context"

"go.opentelemetry.io/api/core"
"go.opentelemetry.io/api/metric/aggregation"
"go.opentelemetry.io/api/registry"
"go.opentelemetry.io/api/unit"
)

type MetricType int
type Type int

//go:generate stringer -type=Type
const (
Invalid MetricType = iota
Gauge // Supports Set()
Cumulative // Supports Inc()
Invalid Type = iota
Gauge // Supports Set()
Cumulative // Supports Inc(): only positive values

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cumulative also needs a set, because the classic example for this is CPU usage. If you want to report a CPU usage it is always going up (unless you do a CumulativeGauge that remembers the previous value and Inc delta).

Additive // Supports Add(): positive or negative

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additive and Gauge can be unified.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They could be. I was hoping to make there be a 1:1 mapping between measured value and the interpretation. If we decide to keep a Stats API (i.e., reject RFC 0003), then I feel strongly that the Metrics API should be layered on top of the Stats API, so there should be a 1:1 mapping from a metrics update into a lower-level stats measurement. If we combine Additive and Gauge, for example, then I need to know the kind of operation in order to interpret a statistic. By allowing the Additive and Gauge to be separate metric types, I can layer Metrics on top of Stats, because each Measure has only one kind. I.e., there's not a mixture of Set() and Add() calls in the same stream of measurements.

Measure // Supports Record()
)

type Meter interface {
// TODO more Metric types
GetFloat64Gauge(ctx context.Context, gauge *Float64GaugeHandle, labels ...core.KeyValue) Float64Gauge
GetFloat64Gauge(context.Context, *Float64GaugeHandle, ...core.KeyValue) Float64Gauge

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a context in this case?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows context tags to be recorded, in case the SDK wants to do that. In addition, it allows us to associate the current active span with the measurement, which means we can reflect this metric update in the recorded span data, for example.

GetFloat64Cumulative(context.Context, *Float64CumulativeHandle, ...core.KeyValue) Float64Cumulative
GetFloat64Additive(context.Context, *Float64AdditiveHandle, ...core.KeyValue) Float64Additive
GetFloat64Measure(context.Context, *Float64MeasureHandle, ...core.KeyValue) Float64Measure
}

type Float64Gauge interface {
Set(ctx context.Context, value float64, labels ...core.KeyValue)
}

type Float64Cumulative interface {
Inc(ctx context.Context, value float64, labels ...core.KeyValue)
}

type Float64Additive interface {
Add(ctx context.Context, value float64, labels ...core.KeyValue)
}

type Float64Measure interface {
Record(ctx context.Context, value float64, labels ...core.KeyValue)
}

type Handle struct {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a Handle? why does this exist?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A handle is a common base type for any of the metrics. In this proposal, the only API difference between the metric types is the verb they use, the interpretation is left to the SDK. So the Handle is a thing that describes the metric independently of its verb or metric type. The properties of a metric are: variable (name), type (gauge, additive, cumulative, or measure), keys (recommended), and aggregation (recommended).

Variable registry.Variable

Type MetricType
Keys []core.Key
Type Type
Keys []core.Key
Aggregation aggregation.Operator
}

type Option func(*Handle, *[]registry.Option)
Expand All @@ -62,20 +81,25 @@ func WithUnit(unit unit.Unit) Option {
}
}

// WithKeys applies the provided dimension keys.
// WithKeys applies recommended dimension keys. Multiple `WithKeys`
// options accumulate. The keys specified in this way are taken as
// the recommended aggregation keys for Gauge, Cumulative, and
// Additive metrics. For Measure metrics, the keys recommended here
// are taken as the default for aggregations.
func WithKeys(keys ...core.Key) Option {
return func(m *Handle, _ *[]registry.Option) {
m.Keys = keys
m.Keys = append(m.Keys, keys...)
}
}

func (mtype MetricType) String() string {
switch mtype {
case Gauge:
return "gauge"
case Cumulative:
return "cumulative"
default:
return "unknown"
// WithAggregation applies a user-recommended aggregation to this
// metric, useful particularly for Measure type metrics.
func WithAggregation(aggr aggregation.Operator) Option {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not compatible with OpenCensus stats which is a primary requirement for this, because we need to not allow the instrumentation plugin to define the aggregation.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a definition. This is a recommendation. The SDK is not bound to follow this advice. If this value is not set, the default aggregation for MeasureMetrics is None. I like this because the application programmer can suggest None as a good aggregation for measure metrics, but could also suggest Distribution.

return func(m *Handle, _ *[]registry.Option) {
m.Aggregation = aggr
}
}

func (h Handle) Defined() bool {
return h.Variable.Defined()
}
2 changes: 1 addition & 1 deletion api/metric/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"go.opentelemetry.io/api/registry"
)

func registerMetric(name string, mtype MetricType, opts []Option, metric *Handle) {
func registerMetric(name string, mtype Type, opts []Option, metric *Handle) {
var varOpts []registry.Option

for _, opt := range opts {
Expand Down
25 changes: 25 additions & 0 deletions api/metric/cumulative.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package metric

type Float64CumulativeHandle struct {
Handle
}

func NewFloat64Cumulative(name string, mos ...Option) *Float64CumulativeHandle {
g := &Float64CumulativeHandle{}
registerMetric(name, Cumulative, mos, &g.Handle)
return g
}
25 changes: 25 additions & 0 deletions api/metric/measure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package metric

type Float64MeasureHandle struct {
Handle
}

func NewFloat64Measure(name string, mos ...Option) *Float64MeasureHandle {
g := &Float64MeasureHandle{}
registerMetric(name, Measure, mos, &g.Handle)
return g
}
24 changes: 24 additions & 0 deletions api/metric/noop_meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,34 @@ type noopMetric struct{}
var _ Meter = noopMeter{}

var _ Float64Gauge = noopMetric{}
var _ Float64Cumulative = noopMetric{}
var _ Float64Additive = noopMetric{}
var _ Float64Measure = noopMetric{}

func (noopMeter) GetFloat64Gauge(ctx context.Context, gauge *Float64GaugeHandle, labels ...core.KeyValue) Float64Gauge {
return noopMetric{}
}

func (noopMeter) GetFloat64Cumulative(ctx context.Context, gauge *Float64CumulativeHandle, labels ...core.KeyValue) Float64Cumulative {
return noopMetric{}
}

func (noopMeter) GetFloat64Additive(ctx context.Context, gauge *Float64AdditiveHandle, labels ...core.KeyValue) Float64Additive {
return noopMetric{}
}

func (noopMeter) GetFloat64Measure(ctx context.Context, gauge *Float64MeasureHandle, labels ...core.KeyValue) Float64Measure {
return noopMetric{}
}

func (noopMetric) Set(ctx context.Context, value float64, labels ...core.KeyValue) {
}

func (noopMetric) Inc(ctx context.Context, value float64, labels ...core.KeyValue) {
}

func (noopMetric) Add(ctx context.Context, value float64, labels ...core.KeyValue) {
}

func (noopMetric) Record(ctx context.Context, value float64, labels ...core.KeyValue) {
}
27 changes: 27 additions & 0 deletions api/metric/type_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading