Skip to content

Commit 0032bd6

Browse files
Fix default merging of resource attributes from environment variable (open-telemetry#1785)
* updated controller to merge default resource with environment resource * updated TracerProvider to merge default resource with environment resource * Added Changelog entry * Added resource.Environment(), modified resource.Default() for environment vairable and WithResource() configuration for TracerProvider and Controller * Update CHANGELOG.md Co-authored-by: Anthony Mirabella <[email protected]> * Moved environment detector to defaultResource initialization, added test cases * Changes to default resource initialization * made changes to the test cases * added merging of resource with environment resource Co-authored-by: Anthony Mirabella <[email protected]>
1 parent 96c5e4b commit 0032bd6

File tree

6 files changed

+50
-13
lines changed

6 files changed

+50
-13
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1010

1111
### Added
1212

13+
- - Extract resource attributes from the `OTEL_RESOURCE_ATTRIBUTES` environment variable and merge them with the `resource.Default` resource as well as resources provided to the `TracerProvider` and metric `Controller`. (#1785)
1314
- Added Jaeger Environment variables: `OTEL_EXPORTER_JAEGER_AGENT_HOST`, `OTEL_EXPORTER_JAEGER_AGENT_PORT`
1415
These environment variables can be used to override Jaeger agent hostname and port (#1752)
1516
- The OTLP exporter now has two new convenience functions, `NewExportPipeline` and `InstallNewPipeline`, setup and install the exporter in tracing and metrics pipelines. (#1373)

sdk/metric/controller/basic/config.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ type Option interface {
6464
Apply(*Config)
6565
}
6666

67-
// WithResource sets the Resource configuration option of a Config.
67+
// WithResource sets the Resource configuration option of a Config by merging it
68+
// with the Resource configuration in the environment.
6869
func WithResource(r *resource.Resource) Option {
69-
return resourceOption{r}
70+
res := resource.Merge(resource.Environment(), r)
71+
return resourceOption{res}
7072
}
7173

7274
type resourceOption struct{ *resource.Resource }

sdk/metric/controller/basic/controller_test.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/stretchr/testify/require"
2525

2626
"go.opentelemetry.io/otel/attribute"
27+
ottest "go.opentelemetry.io/otel/internal/internaltest"
2728
"go.opentelemetry.io/otel/metric"
2829
export "go.opentelemetry.io/otel/sdk/export/metric"
2930
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
@@ -34,6 +35,8 @@ import (
3435
"go.opentelemetry.io/otel/sdk/resource"
3536
)
3637

38+
const envVar = "OTEL_RESOURCE_ATTRIBUTES"
39+
3740
func getMap(t *testing.T, cont *controller.Controller) map[string]float64 {
3841
out := processortest.NewOutput(attribute.DefaultEncoder())
3942

@@ -58,6 +61,12 @@ func checkTestContext(t *testing.T, ctx context.Context) {
5861
}
5962

6063
func TestControllerUsesResource(t *testing.T) {
64+
store, err := ottest.SetEnvVariables(map[string]string{
65+
envVar: "key=value,T=U",
66+
})
67+
require.NoError(t, err)
68+
defer func() { require.NoError(t, store.Restore()) }()
69+
6170
cases := []struct {
6271
name string
6372
options []controller.Option
@@ -66,23 +75,26 @@ func TestControllerUsesResource(t *testing.T) {
6675
{
6776
name: "explicitly empty resource",
6877
options: []controller.Option{controller.WithResource(resource.Empty())},
69-
wanted: ""},
78+
wanted: resource.Environment().Encoded(attribute.DefaultEncoder())},
7079
{
7180
name: "uses default if no resource option",
7281
options: nil,
7382
wanted: resource.Default().Encoded(attribute.DefaultEncoder())},
7483
{
7584
name: "explicit resource",
7685
options: []controller.Option{controller.WithResource(resource.NewWithAttributes(attribute.String("R", "S")))},
77-
wanted: "R=S"},
86+
wanted: "R=S,T=U,key=value"},
7887
{
7988
name: "last resource wins",
8089
options: []controller.Option{
8190
controller.WithResource(resource.Default()),
8291
controller.WithResource(resource.NewWithAttributes(attribute.String("R", "S"))),
8392
},
84-
wanted: "R=S",
85-
},
93+
wanted: "R=S,T=U,key=value"},
94+
{
95+
name: "overlapping attributes with environment resource",
96+
options: []controller.Option{controller.WithResource(resource.NewWithAttributes(attribute.String("T", "V")))},
97+
wanted: "T=V,key=value"},
8698
}
8799
for _, c := range cases {
88100
t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) {

sdk/resource/resource.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var (
4040
otel.Handle(err)
4141
}
4242
return r
43-
}(Detect(context.Background(), defaultServiceNameDetector{}, TelemetrySDK{}))
43+
}(Detect(context.Background(), defaultServiceNameDetector{}, FromEnv{}, TelemetrySDK{}))
4444
)
4545

4646
// NewWithAttributes creates a resource from attrs. If attrs contains
@@ -144,6 +144,17 @@ func Default() *Resource {
144144
return defaultResource
145145
}
146146

147+
// Environment returns an instance of Resource with attributes
148+
// extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable.
149+
func Environment() *Resource {
150+
detector := &FromEnv{}
151+
resource, err := detector.Detect(context.Background())
152+
if err == nil {
153+
otel.Handle(err)
154+
}
155+
return resource
156+
}
157+
147158
// Equivalent returns an object that can be compared for equality
148159
// between two resources. This value is suitable for use as a key in
149160
// a map.

sdk/trace/provider.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,7 @@ func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
262262
// resource.Default() Resource by default.
263263
func WithResource(r *resource.Resource) TracerProviderOption {
264264
return func(opts *TracerProviderConfig) {
265-
if r != nil {
266-
opts.resource = r
267-
}
265+
opts.resource = resource.Merge(resource.Environment(), r)
268266
}
269267
}
270268

sdk/trace/trace_test.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import (
4343
"go.opentelemetry.io/otel/sdk/resource"
4444
)
4545

46+
const envVar = "OTEL_RESOURCE_ATTRIBUTES"
47+
4648
type storingHandler struct {
4749
errs []error
4850
}
@@ -1203,6 +1205,12 @@ func TestWithSpanKind(t *testing.T) {
12031205
}
12041206

12051207
func TestWithResource(t *testing.T) {
1208+
store, err := ottest.SetEnvVariables(map[string]string{
1209+
envVar: "key=value,rk5=7",
1210+
})
1211+
require.NoError(t, err)
1212+
defer func() { require.NoError(t, store.Restore()) }()
1213+
12061214
cases := []struct {
12071215
name string
12081216
options []TracerProviderOption
@@ -1212,7 +1220,7 @@ func TestWithResource(t *testing.T) {
12121220
{
12131221
name: "explicitly empty resource",
12141222
options: []TracerProviderOption{WithResource(resource.Empty())},
1215-
want: resource.Empty(),
1223+
want: resource.Environment(),
12161224
},
12171225
{
12181226
name: "uses default if no resource option",
@@ -1222,14 +1230,19 @@ func TestWithResource(t *testing.T) {
12221230
{
12231231
name: "explicit resource",
12241232
options: []TracerProviderOption{WithResource(resource.NewWithAttributes(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)))},
1225-
want: resource.NewWithAttributes(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)),
1233+
want: resource.Merge(resource.Environment(), resource.NewWithAttributes(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5))),
12261234
},
12271235
{
12281236
name: "last resource wins",
12291237
options: []TracerProviderOption{
12301238
WithResource(resource.NewWithAttributes(attribute.String("rk1", "vk1"), attribute.Int64("rk2", 5))),
12311239
WithResource(resource.NewWithAttributes(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10)))},
1232-
want: resource.NewWithAttributes(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10)),
1240+
want: resource.Merge(resource.Environment(), resource.NewWithAttributes(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
1241+
},
1242+
{
1243+
name: "overlapping attributes with environment resource",
1244+
options: []TracerProviderOption{WithResource(resource.NewWithAttributes(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10)))},
1245+
want: resource.Merge(resource.Environment(), resource.NewWithAttributes(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10))),
12331246
},
12341247
}
12351248
for _, tc := range cases {

0 commit comments

Comments
 (0)