mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-01-14 02:33:21 +02:00
fix(metric, log): merge explicit resource with environment variables (#5773)
fixes #5764 --------- Co-authored-by: Damien Mathieu <42@dmathieu.com>
This commit is contained in:
parent
8dca9cc0fa
commit
9e1b015159
@ -11,6 +11,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
### Added
|
||||
|
||||
- Support `OTEL_EXPORTER_OTLP_LOGS_INSECURE` and `OTEL_EXPORTER_OTLP_INSECURE` environments in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#5739)
|
||||
- The `WithResource` option for `NewMeterProvider` now merges the provided resources with the ones from environment variables. (#5773)
|
||||
- The `WithResource` option for `NewLoggerProvider` now merges the provided resources with the ones from environment variables. (#5773)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -5,6 +5,7 @@ go 1.22
|
||||
require (
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-logr/stdr v1.2.2
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.opentelemetry.io/otel v1.29.0
|
||||
go.opentelemetry.io/otel/log v0.5.0
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/log/embedded"
|
||||
@ -196,7 +197,11 @@ func (fn loggerProviderOptionFunc) apply(c providerConfig) providerConfig {
|
||||
// go.opentelemetry.io/otel/sdk/resource package will be used.
|
||||
func WithResource(res *resource.Resource) LoggerProviderOption {
|
||||
return loggerProviderOptionFunc(func(cfg providerConfig) providerConfig {
|
||||
cfg.resource = res
|
||||
var err error
|
||||
cfg.resource, err = resource.Merge(resource.Environment(), res)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/go-logr/logr/testr"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -20,10 +21,13 @@ import (
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/log/noop"
|
||||
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||
"go.opentelemetry.io/otel/sdk/log/internal/x"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
)
|
||||
|
||||
const envVarResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
|
||||
type processor struct {
|
||||
Name string
|
||||
Err error
|
||||
@ -172,6 +176,65 @@ func TestNewLoggerProviderConfiguration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func mergeResource(t *testing.T, r1, r2 *resource.Resource) *resource.Resource {
|
||||
r, err := resource.Merge(r1, r2)
|
||||
assert.NoError(t, err)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestWithResource(t *testing.T) {
|
||||
store, err := ottest.SetEnvVariables(map[string]string{
|
||||
envVarResourceAttributes: "key=value,rk5=7",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, store.Restore()) }()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
options []LoggerProviderOption
|
||||
want *resource.Resource
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
name: "explicitly empty resource",
|
||||
options: []LoggerProviderOption{WithResource(resource.Empty())},
|
||||
want: resource.Environment(),
|
||||
},
|
||||
{
|
||||
name: "uses default if no resource option",
|
||||
options: []LoggerProviderOption{},
|
||||
want: resource.Default(),
|
||||
},
|
||||
{
|
||||
name: "explicit resource",
|
||||
options: []LoggerProviderOption{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)))},
|
||||
want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5))),
|
||||
},
|
||||
{
|
||||
name: "last resource wins",
|
||||
options: []LoggerProviderOption{
|
||||
WithResource(resource.NewSchemaless(attribute.String("rk1", "vk1"), attribute.Int64("rk2", 5))),
|
||||
WithResource(resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
|
||||
},
|
||||
want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
|
||||
},
|
||||
{
|
||||
name: "overlapping attributes with environment resource",
|
||||
options: []LoggerProviderOption{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10)))},
|
||||
want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10))),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := newProviderConfig(tc.options).resource
|
||||
if diff := cmp.Diff(got, tc.want); diff != "" {
|
||||
t.Errorf("WithResource:\n -got +want %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerProviderConcurrentSafe(t *testing.T) {
|
||||
const goRoutineN = 10
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
)
|
||||
|
||||
@ -103,7 +104,11 @@ func (o optionFunc) apply(conf config) config {
|
||||
// go.opentelemetry.io/otel/sdk/resource package will be used.
|
||||
func WithResource(res *resource.Resource) Option {
|
||||
return optionFunc(func(conf config) config {
|
||||
conf.res = res
|
||||
var err error
|
||||
conf.res, err = resource.Merge(resource.Environment(), res)
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
return conf
|
||||
})
|
||||
}
|
||||
|
@ -8,9 +8,12 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
)
|
||||
@ -25,6 +28,8 @@ type reader struct {
|
||||
shutdownFunc func(context.Context) error
|
||||
}
|
||||
|
||||
const envVarResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
|
||||
var _ Reader = (*reader)(nil)
|
||||
|
||||
func (r *reader) aggregation(kind InstrumentKind) Aggregation { // nolint:revive // import-shadow for method scoped by type.
|
||||
@ -108,10 +113,63 @@ func TestUnifyMultiError(t *testing.T) {
|
||||
assert.Equal(t, unify(funcs)(context.Background()), target)
|
||||
}
|
||||
|
||||
func mergeResource(t *testing.T, r1, r2 *resource.Resource) *resource.Resource {
|
||||
r, err := resource.Merge(r1, r2)
|
||||
assert.NoError(t, err)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestWithResource(t *testing.T) {
|
||||
res := resource.NewSchemaless()
|
||||
c := newConfig([]Option{WithResource(res)})
|
||||
assert.Same(t, res, c.res)
|
||||
store, err := ottest.SetEnvVariables(map[string]string{
|
||||
envVarResourceAttributes: "key=value,rk5=7",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, store.Restore()) }()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
options []Option
|
||||
want *resource.Resource
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
name: "explicitly empty resource",
|
||||
options: []Option{WithResource(resource.Empty())},
|
||||
want: resource.Environment(),
|
||||
},
|
||||
{
|
||||
name: "uses default if no resource option",
|
||||
options: []Option{},
|
||||
want: resource.Default(),
|
||||
},
|
||||
{
|
||||
name: "explicit resource",
|
||||
options: []Option{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)))},
|
||||
want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5))),
|
||||
},
|
||||
{
|
||||
name: "last resource wins",
|
||||
options: []Option{
|
||||
WithResource(resource.NewSchemaless(attribute.String("rk1", "vk1"), attribute.Int64("rk2", 5))),
|
||||
WithResource(resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
|
||||
},
|
||||
want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
|
||||
},
|
||||
{
|
||||
name: "overlapping attributes with environment resource",
|
||||
options: []Option{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10)))},
|
||||
want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10))),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := newConfig(tc.options).res
|
||||
if diff := cmp.Diff(got, tc.want); diff != "" {
|
||||
t.Errorf("WithResource:\n -got +want %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithReader(t *testing.T) {
|
||||
|
@ -5,6 +5,7 @@ go 1.22
|
||||
require (
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-logr/stdr v1.2.2
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.opentelemetry.io/otel v1.29.0
|
||||
go.opentelemetry.io/otel/metric v1.29.0
|
||||
|
Loading…
Reference in New Issue
Block a user