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

Golang metrics prototype #100

Merged
merged 36 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c888fc1
initial metrics work
Sep 19, 2019
9bd13f8
rename cumulative to counter
krnowak Sep 23, 2019
12ce9d3
rename bidirectional to nonmonotonic
krnowak Sep 23, 2019
074ff1f
rename unidirectional to monotonic
krnowak Sep 23, 2019
671b9fe
rename nonnegative to signed
krnowak Sep 23, 2019
6926a5f
rename instrument to descriptor
krnowak Sep 23, 2019
58885b5
change some interfaces to match the spec
krnowak Oct 1, 2019
9680cd2
keep integral measurement separate from floating ones
krnowak Oct 1, 2019
e50df6e
remove duplicated measurement type
krnowak Oct 1, 2019
6b8298c
add checking for options
krnowak Oct 1, 2019
f2a4abe
reorder some fields and functions
krnowak Oct 1, 2019
32e3b6a
license
krnowak Sep 25, 2019
3f934ff
rework measurement values
krnowak Sep 28, 2019
6e1139f
make measurement value a tagged union
krnowak Oct 1, 2019
508422c
simplify to one kind of metrics
krnowak Oct 1, 2019
2b4ff72
add observers
krnowak Oct 1, 2019
cb43448
rename a function
krnowak Oct 1, 2019
cb91aeb
drop disabled field from descriptor
krnowak Oct 1, 2019
d933a2d
add back typed API for metrics
krnowak Oct 3, 2019
e7188d8
make metric options type safe
krnowak Oct 3, 2019
b519b22
merge alternatives into a single bool
krnowak Oct 3, 2019
89f13f0
make value kind name less stuttery
krnowak Oct 4, 2019
f405bf4
fix observation callback prototype
krnowak Oct 4, 2019
760906f
drop context parameter from NewHandle
krnowak Oct 4, 2019
1c8e7e7
drop useless parameter names
krnowak Oct 4, 2019
bfca093
make descriptor an opaque struct
krnowak Oct 4, 2019
200178e
use a store helper
krnowak Oct 4, 2019
ba4bb01
handle comment fixes
krnowak Oct 4, 2019
2eb049a
reword Alternate comment
krnowak Oct 4, 2019
7805077
drop the "any value" metrics
krnowak Oct 5, 2019
1eaac50
make measurement value simpler
krnowak Oct 5, 2019
cf4b0b0
document value stuff
krnowak Oct 5, 2019
b7fd1b5
add tests for values
krnowak Oct 5, 2019
94ce64a
docs
krnowak Oct 7, 2019
9b9c274
do not panic if there is no span ID in the event
krnowak Oct 7, 2019
c84ca30
Merge branch 'master' into jmacd/metrics_proposal2
rghetia Oct 8, 2019
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
71 changes: 57 additions & 14 deletions api/metric/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ import (
type Kind int

const (
Invalid Kind = iota
CounterKind // Supports Add()
GaugeKind // Supports Set()
MeasureKind // Supports Record()
// Invalid describes an invalid metric.
Invalid Kind = iota
// CounterKind describes a metric that supports Add().
CounterKind
// GaugeKind describes a metric that supports Set().
GaugeKind
// MeasureKind describes a metric that supports Record().
MeasureKind
// ObserverKind describes a metric that reports measurement on
// demand.
ObserverKind
)

Expand All @@ -41,13 +47,13 @@ type Handle interface {
RecordOne(ctx context.Context, value MeasurementValue)
}

// LabelSet represents a []core.KeyValue for use as pre-defined labels
// in the metrics API.
//
// TODO this belongs outside the metrics API, in some sense, but that
// might create a dependency. Putting this here means we can't re-use
// might create a dependency. Putting this here means we can't re-use
// a LabelSet between metrics and tracing, even when they are the same
// SDK.

// LabelSet represents a []core.KeyValue for use as pre-defined labels
// in the metrics API.
type LabelSet interface {
Meter() Meter
}
Expand All @@ -66,21 +72,44 @@ type Meter interface {
// cannot be read by the application.
DefineLabels(context.Context, ...core.KeyValue) LabelSet

// RecordBatch atomically records a batch of measurements.
RecordBatch(context.Context, LabelSet, ...Measurement)

// NewHandle creates a Handle that contains the passed
// key-value pairs. This should not be used directly - prefer
// using GetHandle function of a metric.
NewHandle(*Descriptor, LabelSet) Handle
// DeleteHandle destroys the Handle and does a cleanup of the
// underlying resources.
DeleteHandle(Handle)

// RecordBatch atomically records a batch of measurements..
RecordBatch(context.Context, LabelSet, ...Measurement)

// RegisterObserver registers the observer with callback
// returning a measurement. When and how often the callback
// will be called is defined by SDK. This should not be used
// directly - prefer either RegisterInt64Observer or
// RegisterFloat64Observer, depending on the type of the
// observer to be registered.
RegisterObserver(Observer, ObserverCallback)
// UnregisterObserver removes the observer from registered
// observers. This should not be used directly - prefer either
// UnregisterInt64Observer or UnregisterFloat64Observer,
// depending on the type of the observer to be registered.
UnregisterObserver(Observer)
}

// DescriptorID is a unique identifier of a metric.
type DescriptorID uint64

// ValueKind describes the data type of the measurement value the
// metric generates.
type ValueKind int8

const (
// Int64ValueKind means that the metric generates values of
// type int64.
Int64ValueKind ValueKind = iota
// Float64ValueKind means that the metric generates values of
// type float64.
Float64ValueKind
)

Expand Down Expand Up @@ -157,10 +186,13 @@ type Measurement struct {
// Option supports specifying the various metric options.
type Option func(*Descriptor)

// OptionApplier is an interface for applying metric options that are
// valid for all the kinds of metrics.
type OptionApplier interface {
CounterOptionApplier
GaugeOptionApplier
MeasureOptionApplier
// ApplyOption is used to make some changes in the Descriptor.
ApplyOption(*Descriptor)
}

Expand Down Expand Up @@ -204,8 +236,8 @@ func WithUnit(unit unit.Unit) OptionApplier {
}
}

// WithKeys applies required label keys. Multiple `WithKeys`
// options accumulate.
// WithKeys applies required label keys. Multiple `WithKeys` options
// accumulate.
func WithKeys(keys ...core.Key) OptionApplier {
return optionWrapper{
F: func(d *Descriptor) {
Expand All @@ -214,7 +246,8 @@ func WithKeys(keys ...core.Key) OptionApplier {
}
}

// WithNonMonotonic sets whether a counter is permitted to go up AND down.
// WithNonMonotonic sets whether a counter is permitted to go up AND
// down.
func WithNonMonotonic(nm bool) CounterOptionApplier {
return counterOptionWrapper{
F: func(d *Descriptor) {
Expand Down Expand Up @@ -259,6 +292,9 @@ type Int64ObservationCallback func(LabelSet, int64)
// for the registered int64 observers.
type Int64ObserverCallback func(Meter, Int64Observer, Int64ObservationCallback)

// RegisterInt64Observer is a convenience wrapper around
// Meter.RegisterObserver that provides a type-safe callback for
// Int64Observer.
func RegisterInt64Observer(meter Meter, observer Int64Observer, callback Int64ObserverCallback) {
cb := func(m Meter, o Observer, ocb ObservationCallback) {
iocb := func(l LabelSet, i int64) {
Expand All @@ -269,6 +305,8 @@ func RegisterInt64Observer(meter Meter, observer Int64Observer, callback Int64Ob
meter.RegisterObserver(observer.Observer, cb)
}

// UnregisterInt64Observer is a convenience wrapper around
// Meter.UnregisterObserver for Int64Observer.
func UnregisterInt64Observer(meter Meter, observer Int64Observer) {
meter.UnregisterObserver(observer.Observer)
}
Expand All @@ -281,6 +319,9 @@ type Float64ObservationCallback func(LabelSet, float64)
// call for the registered float64 observers.
type Float64ObserverCallback func(Meter, Float64Observer, Float64ObservationCallback)

// RegisterFloat64Observer is a convenience wrapper around
// Meter.RegisterObserver that provides a type-safe callback for
// Float64Observer.
func RegisterFloat64Observer(meter Meter, observer Float64Observer, callback Float64ObserverCallback) {
cb := func(m Meter, o Observer, ocb ObservationCallback) {
focb := func(l LabelSet, f float64) {
Expand All @@ -291,6 +332,8 @@ func RegisterFloat64Observer(meter Meter, observer Float64Observer, callback Flo
meter.RegisterObserver(observer.Observer, cb)
}

// UnregisterFloat64Observer is a convenience wrapper around
// Meter.UnregisterObserver for Float64Observer.
func UnregisterFloat64Observer(meter Meter, observer Float64Observer) {
meter.UnregisterObserver(observer.Observer)
}
3 changes: 3 additions & 0 deletions api/metric/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ var (

// TODO: Maybe unexport that and document very _very_ clearly, that
// you can still get a descriptor with NewInt64Counter(…).Descriptor

// CommonMetric holds a descriptor. It is used mostly to implement the
// common parts for every metric kind.
type CommonMetric struct {
*Descriptor
}
Expand Down
28 changes: 28 additions & 0 deletions api/metric/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,31 @@ import (
"context"
)

// Float64Counter is a metric that accumulates float64 values.
type Float64Counter struct {
CommonMetric
}

// Int64Counter is a metric that accumulates int64 values.
type Int64Counter struct {
CommonMetric
}

// Float64CounterHandle is a handle for Float64Counter.
type Float64CounterHandle struct {
Handle
}

// Int64CounterHandle is a handle for Int64Counter.
type Int64CounterHandle struct {
Handle
}

// CounterOptionApplier is an interface for applying metric options
// that are valid only for counter metrics.
type CounterOptionApplier interface {
// ApplyCounterOption is used to make some counter-specific
// changes in the Descriptor.
ApplyCounterOption(*Descriptor)
}

Expand All @@ -56,46 +64,66 @@ func newCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) C
return m
}

// NewFloat64Counter creates a new counter for float64.
func NewFloat64Counter(name string, mos ...CounterOptionApplier) (c Float64Counter) {
c.CommonMetric = newCounter(name, Float64ValueKind, mos...)
return
}

// NewInt64Counter creates a new counter for int64.
func NewInt64Counter(name string, mos ...CounterOptionApplier) (c Int64Counter) {
c.CommonMetric = newCounter(name, Int64ValueKind, mos...)
return
}

// GetHandle creates a handle for this counter. The labels should
// contain the keys and values specified in the counter with the
// WithKeys option.
func (c *Float64Counter) GetHandle(labels LabelSet) (h Float64CounterHandle) {
h.Handle = c.getHandle(labels)
return
}

// GetHandle creates a handle for this counter. The labels should
// contain the keys and values specified in the counter with the
// WithKeys option.
func (c *Int64Counter) GetHandle(labels LabelSet) (h Int64CounterHandle) {
h.Handle = c.getHandle(labels)
return
}

// Measurement creates a Measurement object to use with batch
// recording.
func (c *Float64Counter) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}

// Measurement creates a Measurement object to use with batch
// recording.
func (c *Int64Counter) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}

// Add adds the value to the counter's sum. The labels should contain
// the keys and values specified in the counter with the WithKeys
// option.
func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) {
c.recordOne(ctx, NewFloat64MeasurementValue(value), labels)
}

// Add adds the value to the counter's sum. The labels should contain
// the keys and values specified in the counter with the WithKeys
// option.
func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) {
c.recordOne(ctx, NewInt64MeasurementValue(value), labels)
}

// Add adds the value to the counter's sum.
func (h *Float64CounterHandle) Add(ctx context.Context, value float64) {
h.RecordOne(ctx, NewFloat64MeasurementValue(value))
}

// Add adds the value to the counter's sum.
func (h *Int64CounterHandle) Add(ctx context.Context, value int64) {
h.RecordOne(ctx, NewInt64MeasurementValue(value))
}
64 changes: 64 additions & 0 deletions api/metric/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,68 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// metric package provides an API for reporting diagnostic
// measurements using three basic kinds of instruments (or four, if
// calling one special case a separate one).
//
// The three basic kinds are:
//
// - counters
// - gauges
// - measures
//
// There is also a special case of a gauge instrument called
// observer. It will be discussed later.
//
// All instruments report either float64 or int64 values.
//
// The primary object that handles metrics is Meter. The
// implementation of the Meter is provided by SDK. Normally, the Meter
// is used directly only for the LabelSet generation, batch recording
// and the handle destruction.
//
// LabelSet is a set of keys and values that are in a suitable,
// optimized form to be used by Meter.
//
// Counters are instruments that are reporting a quantity or a sum. An
// example could be bank account balance or bytes downloaded. Counters
// can be created with either NewFloat64Counter or
// NewInt64Counter. Counters expect non-negative values by default to
// be reported. This can be changed with the WithNonMonotonic option
// passed to the New*Counter function - this allows reporting negative
// values. To report the new value, use an Add function.
//
// Gauges are instruments that are reporting a current state of a
// value. An example could be voltage or temperature. Gauges can be
// created with either NewFloat64Gauge or NewInt64Gauge. Gauges by
// default have no limitations about reported values - they can be
// less or greater than the last reported value. This can be changed
// with the WithMonotonic option passed to the New*Gauge function -
// this permits the reported values only to go up. To report a new
// value, use the Set function.
//
// Measures are instruments that are reporting values that are
// recorded separately to figure out some statistical properties from
// those values (like average). An example could be temperature over
// time or lines of code in the project over time. Measures can be
// created with either NewFloat64Measure or NewInt64Measure. Measures
// by default take only non-negative values. This can be changed with
// the WithSigned option passed to the New*Measure function - this
// allows reporting negative values too. To report a new value, use
// the Record function.
//
// A special case of a gauge is observer. It has the same role as
// gauge, but reports values in a different way. The observer can be
// created with NewFloat64Observer or NewInt64Observer and then
// registered within a Meter with either RegisterInt64Observer or
// RegisterFloat64Observer functions. These take a callback for
// reporting the values. The callback will be called by the Meter when
// it deems it necessary.
//
// All the basic kinds of instruments (so, not observers) also support
// creating handles for a potentially more efficient reporting. The
// handles have the same function names as the instruments (so counter
// handle has Add, gauge handle has Set and measure handle has
// Record). Handles can be created with the GetHandle function of the
// respective instrument.
package metric // import "go.opentelemetry.io/api/metric"
Loading