Skip to content

Commit

Permalink
Create resource.Default() with required attributes/default values (#1507
Browse files Browse the repository at this point in the history
)

* Create resource.Default() with required attributes/default values

Add metric controller and tracer provider tests for resources

* Updated CHANGELOG

* PR comments

+ some small CHANGELOG PR addition, rewording
+ change default resource servicename to `unknown_service:go` (this
matches the Java codebase which uses `unknown_service:java`)
  • Loading branch information
evantorrie authored Feb 15, 2021
1 parent 76f9342 commit 8fae0a6
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 45 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- Added `resource.Default()` for use with meter and tracer providers. (#1507)

## [0.17.0] - 2020-02-12

### Changed

- Rename project default branch from `master` to `main`.
- Rename project default branch from `master` to `main`. (#1505)
- Reverse order in which `Resource` attributes are merged, per change in spec. (#1501)
- Add tooling to maintain "replace" directives in go.mod files automatically. (#1528)
- Create new modules: otel/metric, otel/trace, otel/oteltest, otel/sdk/export/metric, otel/sdk/metric (#1528)
Expand Down
1 change: 1 addition & 0 deletions exporters/metric/prometheus/prometheus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func TestPrometheusStatefulness(t *testing.T) {
exporter, err := prometheus.NewExportPipeline(
prometheus.Config{},
controller.WithCollectPeriod(0),
controller.WithResource(resource.Empty()),
)
require.NoError(t, err)

Expand Down
4 changes: 4 additions & 0 deletions sdk/metric/controller/basic/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
export "go.opentelemetry.io/otel/sdk/export/metric"
sdk "go.opentelemetry.io/otel/sdk/metric"
controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time"
"go.opentelemetry.io/otel/sdk/resource"
)

// DefaultPeriod is used for:
Expand Down Expand Up @@ -85,6 +86,9 @@ func New(checkpointer export.Checkpointer, opts ...Option) *Controller {
for _, opt := range opts {
opt.Apply(c)
}
if c.Resource == nil {
c.Resource = resource.Default()
}

impl := sdk.NewAccumulator(
checkpointer,
Expand Down
59 changes: 59 additions & 0 deletions sdk/metric/controller/basic/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package basic_test
import (
"context"
"errors"
"fmt"
"testing"
"time"

Expand All @@ -30,6 +31,7 @@ import (
"go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/resource"
)

func getMap(t *testing.T, cont *controller.Controller) map[string]float64 {
Expand All @@ -55,13 +57,66 @@ func checkTestContext(t *testing.T, ctx context.Context) {
require.Equal(t, "B", ctx.Value(testContextKey("A")))
}

func TestControllerUsesResource(t *testing.T) {
cases := []struct {
name string
options []controller.Option
wanted string
}{
{
name: "explicitly empty resource",
options: []controller.Option{controller.WithResource(resource.Empty())},
wanted: ""},
{
name: "uses default if no resource option",
options: nil,
wanted: resource.Default().Encoded(label.DefaultEncoder())},
{
name: "explicit resource",
options: []controller.Option{controller.WithResource(resource.NewWithAttributes(label.String("R", "S")))},
wanted: "R=S"},
{
name: "last resource wins",
options: []controller.Option{
controller.WithResource(resource.Default()),
controller.WithResource(resource.NewWithAttributes(label.String("R", "S"))),
},
wanted: "R=S",
},
}
for _, c := range cases {
t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) {
cont := controller.New(
processor.New(
processortest.AggregatorSelector(),
export.CumulativeExportKindSelector(),
),
c.options...,
)
prov := cont.MeterProvider()

ctr := metric.Must(prov.Meter("named")).NewFloat64Counter("calls.sum")
ctr.Add(context.Background(), 1.)

// Collect once
require.NoError(t, cont.Collect(context.Background()))

expect := map[string]float64{
"calls.sum//" + c.wanted: 1.,
}
require.EqualValues(t, expect, getMap(t, cont))
})
}
}

func TestStartNoExporter(t *testing.T) {
cont := controller.New(
processor.New(
processortest.AggregatorSelector(),
export.CumulativeExportKindSelector(),
),
controller.WithCollectPeriod(time.Second),
controller.WithResource(resource.Empty()),
)
mock := controllertest.NewMockClock()
cont.SetClock(mock)
Expand Down Expand Up @@ -132,6 +187,7 @@ func TestObserverCanceled(t *testing.T) {
),
controller.WithCollectPeriod(0),
controller.WithCollectTimeout(time.Millisecond),
controller.WithResource(resource.Empty()),
)

prov := cont.MeterProvider()
Expand Down Expand Up @@ -163,6 +219,7 @@ func TestObserverContext(t *testing.T) {
export.CumulativeExportKindSelector(),
),
controller.WithCollectTimeout(0),
controller.WithResource(resource.Empty()),
)

prov := cont.MeterProvider()
Expand Down Expand Up @@ -228,6 +285,7 @@ func TestExportTimeout(t *testing.T) {
controller.WithCollectPeriod(time.Second),
controller.WithPushTimeout(time.Millisecond),
controller.WithPusher(exporter),
controller.WithResource(resource.Empty()),
)
mock := controllertest.NewMockClock()
cont.SetClock(mock)
Expand Down Expand Up @@ -283,6 +341,7 @@ func TestCollectAfterStopThenStartAgain(t *testing.T) {
),
controller.WithCollectPeriod(time.Second),
controller.WithPusher(exp),
controller.WithResource(resource.Empty()),
)
mock := controllertest.NewMockClock()
cont.SetClock(mock)
Expand Down
3 changes: 3 additions & 0 deletions sdk/metric/controller/basic/pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/resource"
)

func TestPullNoCollect(t *testing.T) {
Expand All @@ -39,6 +40,7 @@ func TestPullNoCollect(t *testing.T) {
processor.WithMemory(true),
),
controller.WithCollectPeriod(0),
controller.WithResource(resource.Empty()),
)

ctx := context.Background()
Expand Down Expand Up @@ -74,6 +76,7 @@ func TestPullWithCollect(t *testing.T) {
processor.WithMemory(true),
),
controller.WithCollectPeriod(time.Second),
controller.WithResource(resource.Empty()),
)
mock := controllertest.NewMockClock()
puller.SetClock(mock)
Expand Down
18 changes: 18 additions & 0 deletions sdk/resource/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/label"
Expand All @@ -43,12 +44,15 @@ type (
K label.Key
F func() (string, error)
}

defaultServiceNameDetector struct{}
)

var (
_ Detector = TelemetrySDK{}
_ Detector = Host{}
_ Detector = stringDetector{}
_ Detector = defaultServiceNameDetector{}
)

// Detect returns a *Resource that describes the OpenTelemetry SDK used.
Expand Down Expand Up @@ -79,3 +83,17 @@ func (sd stringDetector) Detect(ctx context.Context) (*Resource, error) {
}
return NewWithAttributes(sd.K.String(value)), nil
}

// Detect implements Detector
func (defaultServiceNameDetector) Detect(ctx context.Context) (*Resource, error) {
return StringDetector(
semconv.ServiceNameKey,
func() (string, error) {
executable, err := os.Executable()
if err != nil {
return "unknown_service:go", nil
}
return "unknown_service:" + filepath.Base(executable), nil
},
).Detect(ctx)
}
21 changes: 20 additions & 1 deletion sdk/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
package resource // import "go.opentelemetry.io/otel/sdk/resource"

import (
"context"

"go.opentelemetry.io/otel"

"go.opentelemetry.io/otel/label"
)

Expand All @@ -29,7 +33,16 @@ type Resource struct {
labels label.Set
}

var emptyResource Resource
var (
emptyResource Resource

defaultResource *Resource = func(r *Resource, err error) *Resource {
if err != nil {
otel.Handle(err)
}
return r
}(Detect(context.Background(), defaultServiceNameDetector{}, TelemetrySDK{}))
)

// NewWithAttributes creates a resource from a set of attributes. If there are
// duplicate keys present in the list of attributes, then the last
Expand Down Expand Up @@ -113,6 +126,12 @@ func Empty() *Resource {
return &emptyResource
}

// Default returns an instance of Resource with a default
// "service.name" and OpenTelemetrySDK attributes
func Default() *Resource {
return defaultResource
}

// Equivalent returns an object that can be compared for equality
// between two resources. This value is suitable for use as a key in
// a map.
Expand Down
18 changes: 18 additions & 0 deletions sdk/resource/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ package resource_test
import (
"encoding/json"
"fmt"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/label"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/semconv"
)

var (
Expand Down Expand Up @@ -162,6 +165,21 @@ func TestMerge(t *testing.T) {
}
}

func TestDefault(t *testing.T) {
res := resource.Default()
require.False(t, res.Equal(resource.Empty()))
require.True(t, res.LabelSet().HasValue(semconv.ServiceNameKey))

serviceName, _ := res.LabelSet().Value(semconv.ServiceNameKey)
require.True(t, strings.HasPrefix(serviceName.AsString(), "unknown_service:"))
require.Greaterf(t, len(serviceName.AsString()), len("unknown_service:"),
"default service.name should include executable name")

require.Contains(t, res.Attributes(), semconv.TelemetrySDKLanguageGo)
require.Contains(t, res.Attributes(), semconv.TelemetrySDKVersionKey.String(otel.Version()))
require.Contains(t, res.Attributes(), semconv.TelemetrySDKNameKey.String("opentelemetry"))
}

func TestString(t *testing.T) {
for _, test := range []struct {
kvs []label.KeyValue
Expand Down
5 changes: 3 additions & 2 deletions sdk/trace/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ func (p *TracerProvider) ApplyConfig(cfg Config) {
if cfg.MaxLinksPerSpan > 0 {
c.MaxLinksPerSpan = cfg.MaxLinksPerSpan
}
if cfg.Resource != nil {
c.Resource = cfg.Resource
c.Resource = cfg.Resource
if c.Resource == nil {
c.Resource = resource.Default()
}
p.config.Store(&c)
}
Expand Down
Loading

0 comments on commit 8fae0a6

Please sign in to comment.