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

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

Merged
merged 5 commits into from
Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
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