1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-02-05 13:15:41 +02:00

Removed different types of Detectors for Resources. (#1810)

* Removed different types of Detectors for Resources.

This change simplifies different types of collectors into one list. The
order of this list determines how they are applied.  Defaults are
applied when the user does not supply any detectors.  To achieve
default behavior and additional behavior a DefaultDetectors struct has been
created

* missed prometheus test.

* Changed behavior around WithDetectors(nil)

Added examples of use of WithDetectors.

* Added NoOp example

* Changed test to reflect acutal default case

This changes because WithDetector() no longer is the same as not using WithDetector()

* Unexports the noOp detector

* Updated changelog

* Fixes to spelling mistakes.

* Added NewEmptyResouce and unexported builtin detectors

* Fix for prometheus example

* Resource has two Rs

I need to get a new R key it seems.

Co-authored-by: Sam Xie <xsambundy@gmail.com>

* Replaced NewEmptyResource() with New()

* Fix test function name

* Comment fixups.

* Apply suggestions from code review

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>

Co-authored-by: Sam Xie <xsambundy@gmail.com>
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
Aaron Clawson 2021-04-29 13:12:48 -05:00 committed by GitHub
parent f92a6d8361
commit 7674eebf56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 224 additions and 259 deletions

View File

@ -26,6 +26,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Make `NewSplitDriver` from `go.opentelemetry.io/otel/exporters/otlp` take variadic arguments instead of a `SplitConfig` item.
`NewSplitDriver` now automatically implements an internal `noopDriver` for `SplitConfig` fields that are not initialized. (#1798)
- `resource.New()` now creates a Resource without builtin detectors. Previous behavior is now achieved by using `WithBuiltinDetectors` Option. (#1810)
- Move the `Event` type from the `go.opentelemetry.io/otel` package to the `go.opentelemetry.io/otel/sdk/trace` package. (#1846)
- BatchSpanProcessor now report export failures when calling `ForceFlush()` method. (#1860)
@ -33,6 +34,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Removed
- Remove `resource.WithoutBuiltin()`. Use `resource.New()`. (#1810)
- Unexported types `resource.FromEnv`, `resource.Host`, and `resource.TelemetrySDK`, Use the corresponding `With*()` to use individually. (#1810)
### Fixed
- Only report errors from the `"go.opentelemetry.io/otel/sdk/resource".Environment` function when they are not `nil`. (#1850, #1851)

View File

@ -40,7 +40,6 @@ func ExampleNewExportPipeline() {
// Create a resource, with builtin attributes plus R=V.
res, err := resource.New(
context.Background(),
resource.WithoutBuiltin(), // Test-only!
resource.WithAttributes(attribute.String("R", "V")),
)
if err != nil {

View File

@ -26,19 +26,19 @@ import (
)
type (
// TelemetrySDK is a Detector that provides information about
// telemetrySDK is a Detector that provides information about
// the OpenTelemetry SDK used. This Detector is included as a
// builtin. If these resource attributes are not wanted, use
// the WithTelemetrySDK(nil) or WithoutBuiltin() options to
// explicitly disable them.
TelemetrySDK struct{}
telemetrySDK struct{}
// Host is a Detector that provides information about the host
// host is a Detector that provides information about the host
// being run on. This Detector is included as a builtin. If
// these resource attributes are not wanted, use the
// WithHost(nil) or WithoutBuiltin() options to explicitly
// disable them.
Host struct{}
host struct{}
stringDetector struct {
K attribute.Key
@ -49,14 +49,14 @@ type (
)
var (
_ Detector = TelemetrySDK{}
_ Detector = Host{}
_ Detector = telemetrySDK{}
_ Detector = host{}
_ Detector = stringDetector{}
_ Detector = defaultServiceNameDetector{}
)
// Detect returns a *Resource that describes the OpenTelemetry SDK used.
func (TelemetrySDK) Detect(context.Context) (*Resource, error) {
func (telemetrySDK) Detect(context.Context) (*Resource, error) {
return NewWithAttributes(
semconv.TelemetrySDKNameKey.String("opentelemetry"),
semconv.TelemetrySDKLanguageKey.String("go"),
@ -65,7 +65,7 @@ func (TelemetrySDK) Detect(context.Context) (*Resource, error) {
}
// Detect returns a *Resource that describes the host being run on.
func (Host) Detect(ctx context.Context) (*Resource, error) {
func (host) Detect(ctx context.Context) (*Resource, error) {
return StringDetector(semconv.HostNameKey, os.Hostname).Detect(ctx)
}

View File

@ -61,7 +61,6 @@ func TestStringDetectorErrors(t *testing.T) {
for _, test := range tests {
res, err := resource.New(
context.Background(),
resource.WithoutBuiltin(),
resource.WithAttributes(attribute.String("A", "B")),
resource.WithDetectors(test.s),
)

View File

@ -24,18 +24,6 @@ import (
type config struct {
// detectors that will be evaluated.
detectors []Detector
// telemetrySDK is used to specify non-default
// `telemetry.sdk.*` attributes.
telemetrySDK Detector
// HostResource is used to specify non-default `host.*`
// attributes.
host Detector
// FromEnv is used to specify non-default OTEL_RESOURCE_ATTRIBUTES
// attributes.
fromEnv Detector
}
// Option is the interface that applies a configuration option.
@ -81,85 +69,24 @@ func (o detectorsOption) Apply(cfg *config) {
cfg.detectors = append(cfg.detectors, o.detectors...)
}
// WithTelemetrySDK overrides the builtin `telemetry.sdk.*`
// attributes. Use nil to disable these attributes entirely.
func WithTelemetrySDK(d Detector) Option {
return telemetrySDKOption{Detector: d}
// WithBuiltinDetectors adds the built detectors to the configured resource.
func WithBuiltinDetectors() Option {
return WithDetectors(telemetrySDK{},
host{},
fromEnv{})
}
type telemetrySDKOption struct {
option
Detector
// WithFromEnv adds attributes from environment variables to the configured resource.
func WithFromEnv() Option {
return WithDetectors(fromEnv{})
}
// Apply implements Option.
func (o telemetrySDKOption) Apply(cfg *config) {
cfg.telemetrySDK = o.Detector
// WithHost adds attributes from the host to the configured resource.
func WithHost() Option {
return WithDetectors(host{})
}
// WithHost overrides the builtin `host.*` attributes. Use nil to
// disable these attributes entirely.
func WithHost(d Detector) Option {
return hostOption{Detector: d}
}
type hostOption struct {
option
Detector
}
// Apply implements Option.
func (o hostOption) Apply(cfg *config) {
cfg.host = o.Detector
}
// WithFromEnv overrides the builtin detector for
// OTEL_RESOURCE_ATTRIBUTES. Use nil to disable environment checking.
func WithFromEnv(d Detector) Option {
return fromEnvOption{Detector: d}
}
type fromEnvOption struct {
option
Detector
}
// Apply implements Option.
func (o fromEnvOption) Apply(cfg *config) {
cfg.fromEnv = o.Detector
}
// WithoutBuiltin disables all the builtin detectors, including the
// telemetry.sdk.*, host.*, and the environment detector.
func WithoutBuiltin() Option {
return noBuiltinOption{}
}
type noBuiltinOption struct {
option
}
// Apply implements Option.
func (o noBuiltinOption) Apply(cfg *config) {
cfg.host = nil
cfg.telemetrySDK = nil
cfg.fromEnv = nil
}
// New returns a Resource combined from the provided attributes,
// user-provided detectors and builtin detectors.
func New(ctx context.Context, opts ...Option) (*Resource, error) {
cfg := config{
telemetrySDK: TelemetrySDK{},
host: Host{},
fromEnv: FromEnv{},
}
for _, opt := range opts {
opt.Apply(&cfg)
}
detectors := append(
[]Detector{cfg.telemetrySDK, cfg.host, cfg.fromEnv},
cfg.detectors...,
)
return Detect(ctx, detectors...)
// WithTelemetrySDK adds TelemetrySDK version info to the configured resource.
func WithTelemetrySDK() Option {
return WithDetectors(telemetrySDK{})
}

View File

@ -1,139 +0,0 @@
// Copyright The 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 resource_test
import (
"context"
"fmt"
"os"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/sdk/resource"
)
const envVar = "OTEL_RESOURCE_ATTRIBUTES"
func TestDefaultConfig(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: "",
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
res, err := resource.New(ctx)
require.NoError(t, err)
require.EqualValues(t, map[string]string{
"host.name": hostname(),
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
}, toMap(res))
}
func TestDefaultConfigNoHost(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: "",
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
res, err := resource.New(ctx, resource.WithHost(nil))
require.NoError(t, err)
require.EqualValues(t, map[string]string{
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
}, toMap(res))
}
func TestDefaultConfigNoEnv(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: "from=here",
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
res, err := resource.New(ctx, resource.WithFromEnv(nil))
require.NoError(t, err)
require.EqualValues(t, map[string]string{
"host.name": hostname(),
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
}, toMap(res))
}
func TestDefaultConfigWithEnv(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: "key=value,other=attr",
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
res, err := resource.New(ctx)
require.NoError(t, err)
require.EqualValues(t, map[string]string{
"key": "value",
"other": "attr",
"host.name": hostname(),
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
}, toMap(res))
}
func TestWithoutBuiltin(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: "key=value,other=attr",
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
res, err := resource.New(
ctx,
resource.WithoutBuiltin(),
resource.WithAttributes(attribute.String("hello", "collector")),
)
require.NoError(t, err)
require.EqualValues(t, map[string]string{
"hello": "collector",
}, toMap(res))
}
func toMap(res *resource.Resource) map[string]string {
m := map[string]string{}
for _, attr := range res.Attributes() {
m[string(attr.Key)] = attr.Value.Emit()
}
return m
}
func hostname() string {
hn, err := os.Hostname()
if err != nil {
return fmt.Sprintf("hostname(%s)", err)
}
return hn
}

View File

@ -31,18 +31,18 @@ var (
errMissingValue = fmt.Errorf("%w: missing value", ErrPartialResource)
)
// FromEnv is a Detector that implements the Detector and collects
// fromEnv is a Detector that implements the Detector and collects
// resources from environment. This Detector is included as a
// builtin. If these resource attributes are not wanted, use the
// WithFromEnv(nil) or WithoutBuiltin() options to explicitly disable
// them.
type FromEnv struct{}
type fromEnv struct{}
// compile time assertion that FromEnv implements Detector interface
var _ Detector = FromEnv{}
var _ Detector = fromEnv{}
// Detect collects resources from environment
func (FromEnv) Detect(context.Context) (*Resource, error) {
func (fromEnv) Detect(context.Context) (*Resource, error) {
attrs := strings.TrimSpace(os.Getenv(envVar))
if attrs == "" {

View File

@ -33,7 +33,7 @@ func TestDetectOnePair(t *testing.T) {
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
detector := &FromEnv{}
detector := &fromEnv{}
res, err := detector.Detect(context.Background())
require.NoError(t, err)
assert.Equal(t, NewWithAttributes(attribute.String("key", "value")), res)
@ -47,7 +47,7 @@ func TestDetectMultiPairs(t *testing.T) {
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
detector := &FromEnv{}
detector := &fromEnv{}
res, err := detector.Detect(context.Background())
require.NoError(t, err)
assert.Equal(t, res, NewWithAttributes(
@ -65,7 +65,7 @@ func TestEmpty(t *testing.T) {
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
detector := &FromEnv{}
detector := &fromEnv{}
res, err := detector.Detect(context.Background())
require.NoError(t, err)
assert.Equal(t, Empty(), res)
@ -78,7 +78,7 @@ func TestMissingKeyError(t *testing.T) {
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
detector := &FromEnv{}
detector := &fromEnv{}
res, err := detector.Detect(context.Background())
assert.Error(t, err)
assert.Equal(t, err, fmt.Errorf("%w: %v", errMissingValue, "[key]"))

View File

@ -38,7 +38,6 @@ func TestWithOSType(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithOSType(),
)

View File

@ -148,7 +148,6 @@ func testWithProcessPID(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessPID(),
)
@ -162,7 +161,6 @@ func testWithProcessExecutableName(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessExecutableName(),
)
@ -176,7 +174,6 @@ func testWithProcessExecutablePath(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessExecutablePath(),
)
@ -190,7 +187,6 @@ func testWithProcessCommandArgs(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessCommandArgs(),
)
@ -204,7 +200,6 @@ func testWithProcessOwner(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessOwner(),
)
@ -218,7 +213,6 @@ func testWithProcessRuntimeName(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessRuntimeName(),
)
@ -232,7 +226,6 @@ func testWithProcessRuntimeVersion(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessRuntimeVersion(),
)
@ -246,7 +239,6 @@ func testWithProcessRuntimeDescription(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessRuntimeDescription(),
)
@ -260,7 +252,6 @@ func testWithProcess(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcess(),
)
@ -281,7 +272,6 @@ func testWithProcessExecutablePathError(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessExecutablePath(),
)
@ -293,7 +283,6 @@ func testWithProcessOwnerError(t *testing.T) {
ctx := context.Background()
res, err := resource.New(ctx,
resource.WithoutBuiltin(),
resource.WithProcessOwner(),
)

View File

@ -40,9 +40,19 @@ var (
otel.Handle(err)
}
return r
}(Detect(context.Background(), defaultServiceNameDetector{}, FromEnv{}, TelemetrySDK{}))
}(Detect(context.Background(), defaultServiceNameDetector{}, fromEnv{}, telemetrySDK{}))
)
// New returns a Resource combined from the user-provided detectors.
func New(ctx context.Context, opts ...Option) (*Resource, error) {
cfg := config{}
for _, opt := range opts {
opt.Apply(&cfg)
}
return Detect(ctx, cfg.detectors...)
}
// NewWithAttributes creates a resource from attrs. If attrs contains
// duplicate keys, the last value will be used. If attrs contains any invalid
// items those items will be dropped.
@ -147,7 +157,7 @@ func Default() *Resource {
// Environment returns an instance of Resource with attributes
// extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable.
func Environment() *Resource {
detector := &FromEnv{}
detector := &fromEnv{}
resource, err := detector.Detect(context.Background())
if err != nil {
otel.Handle(err)

View File

@ -15,8 +15,10 @@
package resource_test
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"testing"
@ -25,6 +27,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/semconv"
)
@ -38,7 +41,7 @@ var (
kv42 = attribute.String("k4", "")
)
func TestNew(t *testing.T) {
func TestNewWithAttributes(t *testing.T) {
cases := []struct {
name string
in []attribute.KeyValue
@ -252,6 +255,8 @@ func TestString(t *testing.T) {
}
}
const envVar = "OTEL_RESOURCE_ATTRIBUTES"
func TestMarshalJSON(t *testing.T) {
r := resource.NewWithAttributes(attribute.Int64("A", 1), attribute.String("C", "D"))
data, err := json.Marshal(r)
@ -260,3 +265,175 @@ func TestMarshalJSON(t *testing.T) {
`[{"Key":"A","Value":{"Type":"INT64","Value":1}},{"Key":"C","Value":{"Type":"STRING","Value":"D"}}]`,
string(data))
}
func TestNew(t *testing.T) {
tc := []struct {
name string
envars string
detectors []resource.Detector
options []resource.Option
resourceValues map[string]string
}{
{
name: "No Options returns empty resrouce",
envars: "key=value,other=attr",
options: nil,
resourceValues: map[string]string{},
},
{
name: "Nil Detectors works",
envars: "key=value,other=attr",
options: []resource.Option{
resource.WithDetectors(),
},
resourceValues: map[string]string{},
},
{
name: "Only Host",
envars: "from=here",
options: []resource.Option{
resource.WithHost(),
},
resourceValues: map[string]string{
"host.name": hostname(),
},
},
{
name: "Only Env",
envars: "key=value,other=attr",
options: []resource.Option{
resource.WithFromEnv(),
},
resourceValues: map[string]string{
"key": "value",
"other": "attr",
},
},
{
name: "Only TelemetrySDK",
envars: "",
options: []resource.Option{
resource.WithTelemetrySDK(),
},
resourceValues: map[string]string{
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
},
},
{
name: "WithAttributes",
envars: "key=value,other=attr",
options: []resource.Option{
resource.WithAttributes(attribute.String("A", "B")),
},
resourceValues: map[string]string{
"A": "B",
},
},
{
name: "Builtins",
envars: "key=value,other=attr",
options: []resource.Option{
resource.WithBuiltinDetectors(),
},
resourceValues: map[string]string{
"host.name": hostname(),
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
"key": "value",
"other": "attr",
},
},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: tt.envars,
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
res, err := resource.New(ctx, tt.options...)
require.NoError(t, err)
require.EqualValues(t, tt.resourceValues, toMap(res))
})
}
}
func TestNewWithBuiltinDetectors(t *testing.T) {
tc := []struct {
name string
envars string
detectors []resource.Detector
options []resource.Option
resourceValues map[string]string
}{
{
name: "No Options returns builtin",
envars: "key=value,other=attr",
options: nil,
resourceValues: map[string]string{
"host.name": hostname(),
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
"key": "value",
"other": "attr",
},
},
{
name: "WithAttributes",
envars: "key=value,other=attr",
options: []resource.Option{
resource.WithAttributes(attribute.String("A", "B")),
},
resourceValues: map[string]string{
"host.name": hostname(),
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.language": "go",
"telemetry.sdk.version": otel.Version(),
"key": "value",
"other": "attr",
"A": "B",
},
},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
store, err := ottest.SetEnvVariables(map[string]string{
envVar: tt.envars,
})
require.NoError(t, err)
defer func() { require.NoError(t, store.Restore()) }()
ctx := context.Background()
options := append([]resource.Option{resource.WithBuiltinDetectors()}, tt.options...)
res, err := resource.New(ctx, options...)
require.NoError(t, err)
require.EqualValues(t, tt.resourceValues, toMap(res))
})
}
}
func toMap(res *resource.Resource) map[string]string {
m := map[string]string{}
for _, attr := range res.Attributes() {
m[string(attr.Key)] = attr.Value.Emit()
}
return m
}
func hostname() string {
hn, err := os.Hostname()
if err != nil {
return fmt.Sprintf("hostname(%s)", err)
}
return hn
}