Skip to content

Commit

Permalink
Add support for Resources in the SDK (#552)
Browse files Browse the repository at this point in the history
* Add support for Resources in the SDK

Add `Config` types for the push `Controller` and the `SDK`. Included
with this are helper functions to configure the `ErrorHandler` and
`Resource`.

Add a `Resource` to the Meter `Descriptor`. The choice to add the
`Resource` here (instead of say a `Record` or the `Instrument` itself)
was motivated by the definition of the `Descriptor` as the way to
uniquely describe a metric instrument.

Update the push `Controller` and default `SDK` to pass down their configured
`Resource` from instantiation to the metric instruments.

* Update New SDK constructor documentation

* Change NewDescriptor constructor to take opts

Add DescriptorConfig and DescriptorOption to configure the metric
Descriptor with the description, unit, keys, and resource.

Update all function calls to NewDescriptor to use new function
signature.

* Apply suggestions from code review

Co-Authored-By: Rahul Patel <[email protected]>

* Update and add copyright notices

* Update push controller creator func

Pass the configured ErrorHandler for the controller to the SDK.

* Update Resource integration with the SDK

Add back the Resource field to the Descriptor that was moved in the
last merge with master.

Add a resource.Provider interface.

Have the default SDK implement the new resource.Provider interface and
integrate the new interface into the newSync/newAsync workflows. Now, if
the SDK has a Resource defined it will be passed to all Descriptors
created for the instruments it creates.

* Remove nil check for metric SDK config

* Fix and add test for API Options

Add an `Equal` method to the Resource so it can be compared with
github.com/google/go-cmp/cmp.

Add additional test of the API Option unit tests to ensure WithResource
correctly sets a new resource.

* Move the resource.Provider interface to the API package

Move the interface to where it is used.

Fix spelling.

* Remove errant line

* Remove nil checks for the push controller config

* Fix check SDK implements Resourcer

* Apply suggestions from code review

Co-Authored-By: Rahul Patel <[email protected]>

Co-authored-by: Rahul Patel <[email protected]>
  • Loading branch information
MrAlias and rghetia authored Mar 20, 2020
1 parent a01f63b commit f7df68b
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 33 deletions.
22 changes: 22 additions & 0 deletions api/metric/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/unit"
"go.opentelemetry.io/otel/sdk/resource"
)

// Provider supports named Meter instances.
Expand All @@ -45,6 +46,8 @@ type Config struct {
// Keys are recommended keys determined in the handles
// obtained for the metric.
Keys []core.Key
// Resource describes the entity for which measurements are made.
Resource resource.Resource
}

// Option is an interface for applying metric options.
Expand Down Expand Up @@ -141,6 +144,12 @@ func (d Descriptor) NumberKind() core.NumberKind {
return d.numberKind
}

// Resource returns the Resource describing the entity for which the metric
// instrument measures.
func (d Descriptor) Resource() resource.Resource {
return d.config.Resource
}

// Meter is an interface to the metrics portion of the OpenTelemetry SDK.
type Meter interface {
// Labels returns a reference to a set of labels that cannot
Expand Down Expand Up @@ -212,3 +221,16 @@ type keysOption []core.Key
func (k keysOption) Apply(config *Config) {
config.Keys = append(config.Keys, k...)
}

// WithResource applies provided Resource.
//
// This will override any existing Resource.
func WithResource(r resource.Resource) Option {
return resourceOption(r)
}

type resourceOption resource.Resource

func (r resourceOption) Apply(config *Config) {
config.Resource = resource.Resource(r)
}
67 changes: 43 additions & 24 deletions api/metric/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/unit"
mockTest "go.opentelemetry.io/otel/internal/metric"
"go.opentelemetry.io/otel/sdk/resource"

"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
Expand All @@ -35,19 +36,21 @@ var Must = metric.Must

func TestOptions(t *testing.T) {
type testcase struct {
name string
opts []metric.Option
keys []core.Key
desc string
unit unit.Unit
name string
opts []metric.Option
keys []core.Key
desc string
unit unit.Unit
resource resource.Resource
}
testcases := []testcase{
{
name: "no opts",
opts: nil,
keys: nil,
desc: "",
unit: "",
name: "no opts",
opts: nil,
keys: nil,
desc: "",
unit: "",
resource: resource.Resource{},
},
{
name: "keys keys keys",
Expand All @@ -61,46 +64,61 @@ func TestOptions(t *testing.T) {
key.New("bar"), key.New("bar2"),
key.New("baz"), key.New("baz2"),
},
desc: "",
unit: "",
desc: "",
unit: "",
resource: resource.Resource{},
},
{
name: "description",
opts: []metric.Option{
metric.WithDescription("stuff"),
},
keys: nil,
desc: "stuff",
unit: "",
keys: nil,
desc: "stuff",
unit: "",
resource: resource.Resource{},
},
{
name: "description override",
opts: []metric.Option{
metric.WithDescription("stuff"),
metric.WithDescription("things"),
},
keys: nil,
desc: "things",
unit: "",
keys: nil,
desc: "things",
unit: "",
resource: resource.Resource{},
},
{
name: "unit",
opts: []metric.Option{
metric.WithUnit("s"),
},
keys: nil,
desc: "",
unit: "s",
keys: nil,
desc: "",
unit: "s",
resource: resource.Resource{},
},
{
name: "unit override",
opts: []metric.Option{
metric.WithUnit("s"),
metric.WithUnit("h"),
},
keys: nil,
desc: "",
unit: "h",
keys: nil,
desc: "",
unit: "h",
resource: resource.Resource{},
},
{
name: "resource override",
opts: []metric.Option{
metric.WithResource(*resource.New(key.New("name").String("test-name"))),
},
keys: nil,
desc: "",
unit: "",
resource: *resource.New(key.New("name").String("test-name")),
},
}
for idx, tt := range testcases {
Expand All @@ -109,6 +127,7 @@ func TestOptions(t *testing.T) {
Description: tt.desc,
Unit: tt.unit,
Keys: tt.keys,
Resource: tt.resource,
}); diff != "" {
t.Errorf("Compare options: -got +want %s", diff)
}
Expand Down
26 changes: 26 additions & 0 deletions api/metric/sdkhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"

"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/sdk/resource"
)

// MeterImpl is a convenient interface for SDK and test
Expand Down Expand Up @@ -133,6 +134,29 @@ func Configure(opts []Option) Config {
return config
}

// Resourcer is implemented by any value that has a Resource method,
// which returns the Resource associated with the value.
// The Resource method is used to set the Resource for Descriptors of new
// metric instruments.
type Resourcer interface {
Resource() resource.Resource
}

// insertResource inserts a WithResource option at the beginning of opts
// using the resource defined by impl if impl implements Resourcer.
//
// If opts contains a WithResource option already, that Option will take
// precedence and overwrite the Resource set from impl.
//
// The returned []Option may uses the same underlying array as opts.
func insertResource(impl MeterImpl, opts []Option) []Option {
if r, ok := impl.(Resourcer); ok {
// default to the impl resource and override if passed in opts.
return append([]Option{WithResource(r.Resource())}, opts...)
}
return opts
}

// WrapMeterImpl constructs a `Meter` implementation from a
// `MeterImpl` implementation.
func WrapMeterImpl(impl MeterImpl) Meter {
Expand All @@ -159,6 +183,7 @@ func (m *wrappedMeterImpl) RecordBatch(ctx context.Context, ls LabelSet, ms ...M
}

func (m *wrappedMeterImpl) newSync(name string, metricKind Kind, numberKind core.NumberKind, opts []Option) (SyncImpl, error) {
opts = insertResource(m.impl, opts)
return m.impl.NewSyncInstrument(NewDescriptor(name, metricKind, numberKind, opts...))
}

Expand Down Expand Up @@ -219,6 +244,7 @@ func WrapFloat64MeasureInstrument(syncInst SyncImpl, err error) (Float64Measure,
}

func (m *wrappedMeterImpl) newAsync(name string, mkind Kind, nkind core.NumberKind, opts []Option, callback func(func(core.Number, LabelSet))) (AsyncImpl, error) {
opts = insertResource(m.impl, opts)
return m.impl.NewAsyncInstrument(
NewDescriptor(name, mkind, nkind, opts...),
callback)
Expand Down
2 changes: 1 addition & 1 deletion sdk/export/metric/metric.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2019, OpenTelemetry Authors
// Copyright 2020, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
44 changes: 44 additions & 0 deletions sdk/metric/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package metric

import "go.opentelemetry.io/otel/sdk/resource"

// Config contains configuration for an SDK.
type Config struct {
// ErrorHandler is the function called when the SDK encounters an error.
//
// This option can be overridden after instantiation of the SDK
// with the `SetErrorHandler` method.
ErrorHandler ErrorHandler

// Resource is the OpenTelemetry resource associated with all Meters
// created by the SDK.
Resource resource.Resource
}

// Option is the interface that applies the value to a configuration option.
type Option interface {
// Apply sets the Option value of a Config.
Apply(*Config)
}

// WithErrorHandler sets the ErrorHandler configuration option of a Config.
func WithErrorHandler(fn ErrorHandler) Option {
return errorHandlerOption(fn)
}

type errorHandlerOption ErrorHandler

func (o errorHandlerOption) Apply(config *Config) {
config.ErrorHandler = ErrorHandler(o)
}

// WithResource sets the Resource configuration option of a Config.
func WithResource(r resource.Resource) Option {
return resourceOption(r)
}

type resourceOption resource.Resource

func (o resourceOption) Apply(config *Config) {
config.Resource = resource.Resource(o)
}
47 changes: 47 additions & 0 deletions sdk/metric/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package metric

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/sdk/resource"
)

func TestWithErrorHandler(t *testing.T) {
errH, reg := func() (ErrorHandler, *error) {
e := fmt.Errorf("default invalid")
reg := &e
return func(err error) {
*reg = err
}, reg
}()

c := &Config{}
WithErrorHandler(errH).Apply(c)
err1 := fmt.Errorf("error 1")
c.ErrorHandler(err1)
assert.EqualError(t, *reg, err1.Error())

// Ensure overwriting works.
c = &Config{ErrorHandler: DefaultErrorHandler}
WithErrorHandler(errH).Apply(c)
err2 := fmt.Errorf("error 2")
c.ErrorHandler(err2)
assert.EqualError(t, *reg, err2.Error())
}

func TestWithResource(t *testing.T) {
r := resource.New(core.Key("A").String("a"))

c := &Config{}
WithResource(*r).Apply(c)
assert.Equal(t, *r, c.Resource)

// Ensure overwriting works.
c = &Config{Resource: resource.Resource{}}
WithResource(*r).Apply(c)
assert.Equal(t, *r, c.Resource)
}
47 changes: 47 additions & 0 deletions sdk/metric/controller/push/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package push

import (
sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
)

// Config contains configuration for a push Controller.
type Config struct {
// ErrorHandler is the function called when the Controller encounters an error.
//
// This option can be overridden after instantiation of the Controller
// with the `SetErrorHandler` method.
ErrorHandler sdk.ErrorHandler

// Resource is the OpenTelemetry resource associated with all Meters
// created by the Controller.
Resource resource.Resource
}

// Option is the interface that applies the value to a configuration option.
type Option interface {
// Apply sets the Option value of a Config.
Apply(*Config)
}

// WithErrorHandler sets the ErrorHandler configuration option of a Config.
func WithErrorHandler(fn sdk.ErrorHandler) Option {
return errorHandlerOption(fn)
}

type errorHandlerOption sdk.ErrorHandler

func (o errorHandlerOption) Apply(config *Config) {
config.ErrorHandler = sdk.ErrorHandler(o)
}

// WithResource sets the Resource configuration option of a Config.
func WithResource(r resource.Resource) Option {
return resourceOption(r)
}

type resourceOption resource.Resource

func (o resourceOption) Apply(config *Config) {
config.Resource = resource.Resource(o)
}
Loading

0 comments on commit f7df68b

Please sign in to comment.