From 8386f01edddbabd0514dea81f58a9577c561e213 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Thu, 3 Mar 2022 07:56:07 -0800 Subject: [PATCH] Update span limits to comply with specification (#2637) * PoC for span limit refactor * Rename config.go to span_limits.go * Add unit tests for truncateAttr * Add unit tests for non-string attrs * Add span limit benchmark tests * Fix lint * Isolate span limit tests * Clean span limits test * Test limits on exported spans * Remove duplicate test code * Fix lint * Add WithRawSpanLimits option * Add test for raw and orig span limits opts * Add changes to changelog * Add tests for span resource disabled * Test unlimited instead of default limit * Update docs * Add fix to changelog * Fix option docs * Do no mutate attribute * Fix truncateAttr comment * Remake NewSpanLimits to be newEnvSpanLimits Update and unify documentation accordingly. * Update truncateAttr string slice update comment * Update CHANGELOG.md Co-authored-by: Anthony Mirabella Co-authored-by: Anthony Mirabella --- CHANGELOG.md | 23 ++- sdk/internal/env/env.go | 61 +++++++- sdk/trace/benchmark_test.go | 102 +++++++++++- sdk/trace/config.go | 84 ---------- sdk/trace/evictedqueue.go | 7 +- sdk/trace/provider.go | 66 ++++++-- sdk/trace/provider_test.go | 61 -------- sdk/trace/span.go | 116 ++++++++++---- sdk/trace/span_limits.go | 126 +++++++++++++++ sdk/trace/span_limits_test.go | 283 ++++++++++++++++++++++++++++++++++ sdk/trace/span_test.go | 145 +++++++++++++++++ sdk/trace/trace_test.go | 23 ++- 12 files changed, 895 insertions(+), 202 deletions(-) delete mode 100644 sdk/trace/config.go create mode 100644 sdk/trace/span_limits.go create mode 100644 sdk/trace/span_limits_test.go create mode 100644 sdk/trace/span_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b90c3a4b167..d7d51e21893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,15 +14,26 @@ This update is a breaking change of the unstable Metrics API. Code instrumented ### Added +- Log the Exporters configuration in the TracerProviders message. (#2578) - Added support to configure the span limits with environment variables. - The following environment variables are used. (#2606) + The following environment variables are used. (#2606, #2637) + - `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` - `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` - `OTEL_SPAN_EVENT_COUNT_LIMIT` + - `OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT` - `OTEL_SPAN_LINK_COUNT_LIMIT` + - `OTEL_LINK_ATTRIBUTE_COUNT_LIMIT` If the provided environment variables are invalid (negative), the default values would be used. - Rename the `gc` runtime name to `go` (#2560) -- Log the Exporters configuration in the TracerProviders message. (#2578) +- Add span attribute value length limit. + The new `AttributeValueLengthLimit` field is added to the `"go.opentelemetry.io/otel/sdk/trace".SpanLimits` type to configure this limit for a `TracerProvider`. + The default limit for this resource is "unlimited". (#2637) +- Add the `WithRawSpanLimits` option to `go.opentelemetry.io/otel/sdk/trace`. + This option replaces the `WithSpanLimits` option. + Zero or negative values will not be changed to the default value like `WithSpanLimits` does. + Setting a limit to zero will effectively disable the related resource it limits and setting to a negative value will mean that resource is unlimited. + Consequentially, limits should be constructed using `NewSpanLimits` and updated accordingly. (#2637) ### Changed @@ -37,6 +48,14 @@ This update is a breaking change of the unstable Metrics API. Code instrumented - Remove the OTLP trace exporter limit of SpanEvents when exporting. (#2616) - Use port `4318` instead of `4317` for default for the `otlpmetrichttp` and `otlptracehttp` client. (#2614, #2625) +- Unlimited span limits are now supported (negative values). (#2636, #2637) + +### Deprecated + +- Deprecated `"go.opentelemetry.io/otel/sdk/trace".WithSpanLimits`. + Use `WithRawSpanLimits` instead. + That option allows setting unlimited and zero limits, this option does not. + This option will be kept until the next major version incremented release. (#2637) ## [1.4.1] - 2022-02-16 diff --git a/sdk/internal/env/env.go b/sdk/internal/env/env.go index df7a05626b3..dbc8f512492 100644 --- a/sdk/internal/env/env.go +++ b/sdk/internal/env/env.go @@ -41,20 +41,29 @@ const ( // i.e. 512 BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE" - // SpanAttributesCountKey + // SpanAttributeValueLengthKey + // Maximum allowed attribute value size. + SpanAttributeValueLengthKey = "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" + + // SpanAttributeCountKey // Maximum allowed span attribute count - // Default: 128 - SpanAttributesCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" + SpanAttributeCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" // SpanEventCountKey // Maximum allowed span event count - // Default: 128 SpanEventCountKey = "OTEL_SPAN_EVENT_COUNT_LIMIT" + // SpanEventAttributeCountKey + // Maximum allowed attribute per span event count. + SpanEventAttributeCountKey = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT" + // SpanLinkCountKey // Maximum allowed span link count - // Default: 128 SpanLinkCountKey = "OTEL_SPAN_LINK_COUNT_LIMIT" + + // SpanLinkAttributeCountKey + // Maximum allowed attribute per span link count + SpanLinkAttributeCountKey = "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT" ) // IntEnvOr returns the int value of the environment variable with name key if @@ -101,3 +110,45 @@ func BatchSpanProcessorMaxQueueSize(defaultValue int) int { func BatchSpanProcessorMaxExportBatchSize(defaultValue int) int { return IntEnvOr(BatchSpanProcessorMaxExportBatchSizeKey, defaultValue) } + +// SpanAttributeValueLength returns the environment variable value for the +// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists, otherwise +// defaultValue is returned. +func SpanAttributeValueLength(defaultValue int) int { + return IntEnvOr(SpanAttributeValueLengthKey, defaultValue) +} + +// SpanAttributeCount returns the environment variable value for the +// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanAttributeCount(defaultValue int) int { + return IntEnvOr(SpanAttributeCountKey, defaultValue) +} + +// SpanEventCount returns the environment variable value for the +// OTEL_SPAN_EVENT_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanEventCount(defaultValue int) int { + return IntEnvOr(SpanEventCountKey, defaultValue) +} + +// SpanEventAttributeCount returns the environment variable value for the +// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue +// is returned. +func SpanEventAttributeCount(defaultValue int) int { + return IntEnvOr(SpanEventAttributeCountKey, defaultValue) +} + +// SpanLinkCount returns the environment variable value for the +// OTEL_SPAN_LINK_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanLinkCount(defaultValue int) int { + return IntEnvOr(SpanLinkCountKey, defaultValue) +} + +// SpanLinkAttributeCount returns the environment variable value for the +// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanLinkAttributeCount(defaultValue int) int { + return IntEnvOr(SpanLinkAttributeCountKey, defaultValue) +} diff --git a/sdk/trace/benchmark_test.go b/sdk/trace/benchmark_test.go index 81a06e2f8f9..b7dde8d96b0 100644 --- a/sdk/trace/benchmark_test.go +++ b/sdk/trace/benchmark_test.go @@ -25,10 +25,106 @@ import ( "go.opentelemetry.io/otel/trace" ) +func benchmarkSpanLimits(b *testing.B, limits sdktrace.SpanLimits) { + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanLimits(limits)) + tracer := tp.Tracer(b.Name()) + ctx := context.Background() + + const count = 8 + + attrs := []attribute.KeyValue{ + attribute.Bool("bool", true), + attribute.BoolSlice("boolSlice", []bool{true, false}), + attribute.Int("int", 42), + attribute.IntSlice("intSlice", []int{42, -1}), + attribute.Int64("int64", 42), + attribute.Int64Slice("int64Slice", []int64{42, -1}), + attribute.Float64("float64", 42), + attribute.Float64Slice("float64Slice", []float64{42, -1}), + attribute.String("string", "value"), + attribute.StringSlice("stringSlice", []string{"value", "value-1"}), + } + + links := make([]trace.Link, count) + for i := range links { + links[i] = trace.Link{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: [16]byte{0x01}, + SpanID: [8]byte{0x01}, + }), + Attributes: attrs, + } + } + + events := make([]struct { + name string + attr []attribute.KeyValue + }, count) + for i := range events { + events[i] = struct { + name string + attr []attribute.KeyValue + }{ + name: fmt.Sprintf("event-%d", i), + attr: attrs, + } + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, span := tracer.Start(ctx, "span-name", trace.WithLinks(links...)) + span.SetAttributes(attrs...) + for _, e := range events { + span.AddEvent(e.name, trace.WithAttributes(e.attr...)) + } + span.End() + } +} + +func BenchmarkSpanLimits(b *testing.B) { + b.Run("AttributeValueLengthLimit", func(b *testing.B) { + limits := sdktrace.NewSpanLimits() + limits.AttributeValueLengthLimit = 2 + benchmarkSpanLimits(b, limits) + }) + + b.Run("AttributeCountLimit", func(b *testing.B) { + limits := sdktrace.NewSpanLimits() + limits.AttributeCountLimit = 1 + benchmarkSpanLimits(b, limits) + }) + + b.Run("EventCountLimit", func(b *testing.B) { + limits := sdktrace.NewSpanLimits() + limits.EventCountLimit = 1 + benchmarkSpanLimits(b, limits) + }) + + b.Run("LinkCountLimit", func(b *testing.B) { + limits := sdktrace.NewSpanLimits() + limits.LinkCountLimit = 1 + benchmarkSpanLimits(b, limits) + }) + + b.Run("AttributePerEventCountLimit", func(b *testing.B) { + limits := sdktrace.NewSpanLimits() + limits.AttributePerEventCountLimit = 1 + benchmarkSpanLimits(b, limits) + }) + + b.Run("AttributePerLinkCountLimit", func(b *testing.B) { + limits := sdktrace.NewSpanLimits() + limits.AttributePerLinkCountLimit = 1 + benchmarkSpanLimits(b, limits) + }) +} + func BenchmarkSpanSetAttributesOverCapacity(b *testing.B) { - tp := sdktrace.NewTracerProvider( - sdktrace.WithSpanLimits(sdktrace.SpanLimits{AttributeCountLimit: 1}), - ) + limits := sdktrace.NewSpanLimits() + limits.AttributeCountLimit = 1 + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanLimits(limits)) tracer := tp.Tracer("BenchmarkSpanSetAttributesOverCapacity") ctx := context.Background() attrs := make([]attribute.KeyValue, 128) diff --git a/sdk/trace/config.go b/sdk/trace/config.go deleted file mode 100644 index efea7b302f9..00000000000 --- a/sdk/trace/config.go +++ /dev/null @@ -1,84 +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 trace // import "go.opentelemetry.io/otel/sdk/trace" -import "go.opentelemetry.io/otel/sdk/internal/env" - -// SpanLimits represents the limits of a span. -type SpanLimits struct { - // AttributeCountLimit is the maximum allowed span attribute count. - AttributeCountLimit int - - // EventCountLimit is the maximum allowed span event count. - EventCountLimit int - - // LinkCountLimit is the maximum allowed span link count. - LinkCountLimit int - - // AttributePerEventCountLimit is the maximum allowed attribute per span event count. - AttributePerEventCountLimit int - - // AttributePerLinkCountLimit is the maximum allowed attribute per span link count. - AttributePerLinkCountLimit int -} - -func (sl *SpanLimits) ensureDefault() { - if sl.EventCountLimit <= 0 { - sl.EventCountLimit = DefaultEventCountLimit - } - if sl.AttributeCountLimit <= 0 { - sl.AttributeCountLimit = DefaultAttributeCountLimit - } - if sl.LinkCountLimit <= 0 { - sl.LinkCountLimit = DefaultLinkCountLimit - } - if sl.AttributePerEventCountLimit <= 0 { - sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit - } - if sl.AttributePerLinkCountLimit <= 0 { - sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit - } -} - -func (sl *SpanLimits) parsePotentialEnvConfigs() { - sl.AttributeCountLimit = env.IntEnvOr(env.SpanAttributesCountKey, sl.AttributeCountLimit) - sl.LinkCountLimit = env.IntEnvOr(env.SpanLinkCountKey, sl.LinkCountLimit) - sl.EventCountLimit = env.IntEnvOr(env.SpanEventCountKey, sl.EventCountLimit) -} - -const ( - // DefaultAttributeCountLimit is the default maximum allowed span attribute count. - // If not specified via WithSpanLimits, will try to retrieve the value from - // environment variable `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT`. - // If Invalid value (negative or zero) is provided, the default value 128 will be used. - DefaultAttributeCountLimit = 128 - - // DefaultEventCountLimit is the default maximum allowed span event count. - // If not specified via WithSpanLimits, will try to retrieve the value from - // environment variable `OTEL_SPAN_EVENT_COUNT_LIMIT`. - // If Invalid value (negative or zero) is provided, the default value 128 will be used. - DefaultEventCountLimit = 128 - - // DefaultLinkCountLimit is the default maximum allowed span link count. - // If the value is not specified via WithSpanLimits, will try to retrieve the value from - // environment variable `OTEL_SPAN_LINK_COUNT_LIMIT`. - // If Invalid value (negative or zero) is provided, the default value 128 will be used. - DefaultLinkCountLimit = 128 - - // DefaultAttributePerEventCountLimit is the default maximum allowed attribute per span event count. - DefaultAttributePerEventCountLimit = 128 - - // DefaultAttributePerLinkCountLimit is the default maximum allowed attribute per span link count. - DefaultAttributePerLinkCountLimit = 128 -) diff --git a/sdk/trace/evictedqueue.go b/sdk/trace/evictedqueue.go index 8e89e19d4b9..d1c86e59b22 100644 --- a/sdk/trace/evictedqueue.go +++ b/sdk/trace/evictedqueue.go @@ -29,7 +29,12 @@ func newEvictedQueue(capacity int) evictedQueue { // add adds value to the evictedQueue eq. If eq is at capacity, the oldest // queued value will be discarded and the drop count incremented. func (eq *evictedQueue) add(value interface{}) { - if len(eq.queue) == eq.capacity { + if eq.capacity == 0 { + eq.droppedCount++ + return + } + + if eq.capacity > 0 && len(eq.queue) == eq.capacity { // Drop first-in while avoiding allocating more capacity to eq.queue. copy(eq.queue[:eq.capacity-1], eq.queue[1:]) eq.queue = eq.queue[:eq.capacity-1] diff --git a/sdk/trace/provider.go b/sdk/trace/provider.go index 2de79f03397..0643d1a1611 100644 --- a/sdk/trace/provider.go +++ b/sdk/trace/provider.go @@ -96,9 +96,10 @@ var _ trace.TracerProvider = &TracerProvider{} // The passed opts are used to override these default values and configure the // returned TracerProvider appropriately. func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { - o := tracerProviderConfig{} + o := tracerProviderConfig{ + spanLimits: NewSpanLimits(), + } - o.spanLimits.parsePotentialEnvConfigs() for _, opt := range opts { o = opt.apply(o) } @@ -345,20 +346,68 @@ func WithSampler(s Sampler) TracerProviderOption { }) } -// WithSpanLimits returns a TracerProviderOption that will configure the -// SpanLimits sl as a TracerProvider's SpanLimits. The configured SpanLimits -// are used used by the Tracers the TracerProvider and the Spans they create -// to limit tracing resources used. +// WithSpanLimits returns a TracerProviderOption that configures a +// TracerProvider to use the SpanLimits sl. These SpanLimits bound any Span +// created by a Tracer from the TracerProvider. +// +// If any field of sl is zero or negative it will be replaced with the default +// value for that field. // -// If this option is not used, the TracerProvider will use the default -// SpanLimits. +// If this or WithRawSpanLimits are not provided, the TracerProvider will use +// the limits defined by environment variables, or the defaults if unset. +// Refer to the NewSpanLimits documentation for information about this +// relationship. +// +// Deprecated: Use WithRawSpanLimits instead which allows setting unlimited +// and zero limits. This option will be kept until the next major version +// incremented release. func WithSpanLimits(sl SpanLimits) TracerProviderOption { + if sl.AttributeValueLengthLimit <= 0 { + sl.AttributeValueLengthLimit = DefaultAttributeValueLengthLimit + } + if sl.AttributeCountLimit <= 0 { + sl.AttributeCountLimit = DefaultAttributeCountLimit + } + if sl.EventCountLimit <= 0 { + sl.EventCountLimit = DefaultEventCountLimit + } + if sl.AttributePerEventCountLimit <= 0 { + sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit + } + if sl.LinkCountLimit <= 0 { + sl.LinkCountLimit = DefaultLinkCountLimit + } + if sl.AttributePerLinkCountLimit <= 0 { + sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit + } return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { cfg.spanLimits = sl return cfg }) } +// WithRawSpanLimits returns a TracerProviderOption that configures a +// TracerProvider to use these limits. These limits bound any Span created by +// a Tracer from the TracerProvider. +// +// The limits will be used as-is. Zero or negative values will not be changed +// to the default value like WithSpanLimits does. Setting a limit to zero will +// effectively disable the related resource it limits and setting to a +// negative value will mean that resource is unlimited. Consequentially, this +// means that the zero-value SpanLimits will disable all span resources. +// Because of this, limits should be constructed using NewSpanLimits and +// updated accordingly. +// +// If this or WithSpanLimits are not provided, the TracerProvider will use the +// limits defined by environment variables, or the defaults if unset. Refer to +// the NewSpanLimits documentation for information about this relationship. +func WithRawSpanLimits(limits SpanLimits) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + cfg.spanLimits = limits + return cfg + }) +} + // ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid. func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig { if cfg.sampler == nil { @@ -367,7 +416,6 @@ func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderCon if cfg.idGenerator == nil { cfg.idGenerator = defaultIDGenerator() } - cfg.spanLimits.ensureDefault() if cfg.resource == nil { cfg.resource = resource.Default() } diff --git a/sdk/trace/provider_test.go b/sdk/trace/provider_test.go index 0633889bdc2..e2fce31d7f7 100644 --- a/sdk/trace/provider_test.go +++ b/sdk/trace/provider_test.go @@ -17,14 +17,8 @@ package trace import ( "context" "errors" - "os" "testing" - "github.com/stretchr/testify/require" - - ottest "go.opentelemetry.io/otel/internal/internaltest" - "go.opentelemetry.io/otel/sdk/internal/env" - "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel/trace" @@ -100,58 +94,3 @@ func TestSchemaURL(t *testing.T) { tracerStruct := tracerIface.(*tracer) assert.EqualValues(t, schemaURL, tracerStruct.instrumentationLibrary.SchemaURL) } - -func TestNewTraceProviderWithoutSpanLimitConfiguration(t *testing.T) { - envStore := ottest.NewEnvStore() - defer func() { - require.NoError(t, envStore.Restore()) - }() - envStore.Record(env.SpanAttributesCountKey) - envStore.Record(env.SpanEventCountKey) - envStore.Record(env.SpanLinkCountKey) - require.NoError(t, os.Setenv(env.SpanEventCountKey, "111")) - require.NoError(t, os.Setenv(env.SpanAttributesCountKey, "222")) - require.NoError(t, os.Setenv(env.SpanLinkCountKey, "333")) - tp := NewTracerProvider() - assert.Equal(t, 111, tp.spanLimits.EventCountLimit) - assert.Equal(t, 222, tp.spanLimits.AttributeCountLimit) - assert.Equal(t, 333, tp.spanLimits.LinkCountLimit) -} - -func TestNewTraceProviderWithSpanLimitConfigurationFromOptsAndEnvironmentVariable(t *testing.T) { - envStore := ottest.NewEnvStore() - defer func() { - require.NoError(t, envStore.Restore()) - }() - envStore.Record(env.SpanAttributesCountKey) - envStore.Record(env.SpanEventCountKey) - envStore.Record(env.SpanLinkCountKey) - require.NoError(t, os.Setenv(env.SpanEventCountKey, "111")) - require.NoError(t, os.Setenv(env.SpanAttributesCountKey, "222")) - require.NoError(t, os.Setenv(env.SpanLinkCountKey, "333")) - tp := NewTracerProvider(WithSpanLimits(SpanLimits{ - EventCountLimit: 1, - AttributeCountLimit: 2, - LinkCountLimit: 3, - })) - assert.Equal(t, 1, tp.spanLimits.EventCountLimit) - assert.Equal(t, 2, tp.spanLimits.AttributeCountLimit) - assert.Equal(t, 3, tp.spanLimits.LinkCountLimit) -} - -func TestNewTraceProviderWithInvalidSpanLimitConfigurationFromEnvironmentVariable(t *testing.T) { - envStore := ottest.NewEnvStore() - defer func() { - require.NoError(t, envStore.Restore()) - }() - envStore.Record(env.SpanAttributesCountKey) - envStore.Record(env.SpanEventCountKey) - envStore.Record(env.SpanLinkCountKey) - require.NoError(t, os.Setenv(env.SpanEventCountKey, "-111")) - require.NoError(t, os.Setenv(env.SpanAttributesCountKey, "-222")) - require.NoError(t, os.Setenv(env.SpanLinkCountKey, "-333")) - tp := NewTracerProvider() - assert.Equal(t, 128, tp.spanLimits.EventCountLimit) - assert.Equal(t, 128, tp.spanLimits.AttributeCountLimit) - assert.Equal(t, 128, tp.spanLimits.LinkCountLimit) -} diff --git a/sdk/trace/span.go b/sdk/trace/span.go index 779cde691dd..f2dda294580 100644 --- a/sdk/trace/span.go +++ b/sdk/trace/span.go @@ -212,10 +212,17 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) { s.mu.Lock() defer s.mu.Unlock() + limit := s.tracer.provider.spanLimits.AttributeCountLimit + if limit == 0 { + // No attributes allowed. + s.droppedAttributes += len(attributes) + return + } + // If adding these attributes could exceed the capacity of s perform a // de-duplication and truncation while adding to avoid over allocation. - if len(s.attributes)+len(attributes) > s.tracer.provider.spanLimits.AttributeCountLimit { - s.addOverCapAttrs(attributes) + if limit > 0 && len(s.attributes)+len(attributes) > limit { + s.addOverCapAttrs(limit, attributes) return } @@ -227,21 +234,25 @@ func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) { s.droppedAttributes++ continue } + a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a) s.attributes = append(s.attributes, a) } } // addOverCapAttrs adds the attributes attrs to the span s while // de-duplicating the attributes of s and attrs and dropping attributes that -// exceed the capacity of s. +// exceed the limit. // // This method assumes s.mu.Lock is held by the caller. // // This method should only be called when there is a possibility that adding -// attrs to s will exceed the capacity of s. Otherwise, attrs should be added -// to s without checking for duplicates and all retrieval methods of the -// attributes for s will de-duplicate as needed. -func (s *recordingSpan) addOverCapAttrs(attrs []attribute.KeyValue) { +// attrs to s will exceed the limit. Otherwise, attrs should be added to s +// without checking for duplicates and all retrieval methods of the attributes +// for s will de-duplicate as needed. +// +// This method assumes limit is a value > 0. The argument should be validated +// by the caller. +func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) { // In order to not allocate more capacity to s.attributes than needed, // prune and truncate this addition of attributes while adding. @@ -265,17 +276,58 @@ func (s *recordingSpan) addOverCapAttrs(attrs []attribute.KeyValue) { continue } - if len(s.attributes) >= s.tracer.provider.spanLimits.AttributeCountLimit { + if len(s.attributes) >= limit { // Do not just drop all of the remaining attributes, make sure // updates are checked and performed. s.droppedAttributes++ } else { + a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a) s.attributes = append(s.attributes, a) exists[a.Key] = len(s.attributes) - 1 } } } +// truncateAttr returns a truncated version of attr. Only string and string +// slice attribute values are truncated. String values are truncated to at +// most a length of limit. Each string slice value is truncated in this fasion +// (the slice length itself is unaffected). +// +// No truncation is perfromed for a negative limit. +func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue { + if limit < 0 { + return attr + } + switch attr.Value.Type() { + case attribute.STRING: + if v := attr.Value.AsString(); len(v) > limit { + return attr.Key.String(v[:limit]) + } + case attribute.STRINGSLICE: + // Do no mutate the original, make a copy. + trucated := attr.Key.StringSlice(attr.Value.AsStringSlice()) + // Do not do this. + // + // v := trucated.Value.AsStringSlice() + // cp := make([]string, len(v)) + // /* Copy and truncate values to cp ... */ + // trucated.Value = attribute.StringSliceValue(cp) + // + // Copying the []string and then assigning it back as a new value with + // attribute.StringSliceValue will copy the data twice. Instead, we + // already made a copy above that only this function owns, update the + // underlying slice data of our copy. + v := trucated.Value.AsStringSlice() + for i := range v { + if len(v[i]) > limit { + v[i] = v[i][:limit] + } + } + return trucated + } + return attr +} + // End ends the span. This method does nothing if the span is already ended or // is not being recorded. // @@ -396,22 +448,23 @@ func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) { func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) { c := trace.NewEventConfig(o...) + e := Event{Name: name, Attributes: c.Attributes(), Time: c.Timestamp()} - // Discard over limited attributes - attributes := c.Attributes() - var discarded int - if len(attributes) > s.tracer.provider.spanLimits.AttributePerEventCountLimit { - discarded = len(attributes) - s.tracer.provider.spanLimits.AttributePerEventCountLimit - attributes = attributes[:s.tracer.provider.spanLimits.AttributePerEventCountLimit] + // Discard attributes over limit. + limit := s.tracer.provider.spanLimits.AttributePerEventCountLimit + if limit == 0 { + // Drop all attributes. + e.DroppedAttributeCount = len(e.Attributes) + e.Attributes = nil + } else if limit > 0 && len(e.Attributes) > limit { + // Drop over capacity. + e.DroppedAttributeCount = len(e.Attributes) - limit + e.Attributes = e.Attributes[:limit] } + s.mu.Lock() - defer s.mu.Unlock() - s.events.add(Event{ - Name: name, - Attributes: attributes, - DroppedAttributeCount: discarded, - Time: c.Timestamp(), - }) + s.events.add(e) + s.mu.Unlock() } // SetName sets the name of this span. If this span is not being recorded than @@ -551,18 +604,23 @@ func (s *recordingSpan) addLink(link trace.Link) { if !s.IsRecording() || !link.SpanContext.IsValid() { return } - s.mu.Lock() - defer s.mu.Unlock() - var droppedAttributeCount int + l := Link{SpanContext: link.SpanContext, Attributes: link.Attributes} - // Discard over limited attributes - if len(link.Attributes) > s.tracer.provider.spanLimits.AttributePerLinkCountLimit { - droppedAttributeCount = len(link.Attributes) - s.tracer.provider.spanLimits.AttributePerLinkCountLimit - link.Attributes = link.Attributes[:s.tracer.provider.spanLimits.AttributePerLinkCountLimit] + // Discard attributes over limit. + limit := s.tracer.provider.spanLimits.AttributePerLinkCountLimit + if limit == 0 { + // Drop all attributes. + l.DroppedAttributeCount = len(l.Attributes) + l.Attributes = nil + } else if limit > 0 && len(l.Attributes) > limit { + l.DroppedAttributeCount = len(l.Attributes) - limit + l.Attributes = l.Attributes[:limit] } - s.links.add(Link{link.SpanContext, link.Attributes, droppedAttributeCount}) + s.mu.Lock() + s.links.add(l) + s.mu.Unlock() } // DroppedAttributes returns the number of attributes dropped by the span diff --git a/sdk/trace/span_limits.go b/sdk/trace/span_limits.go new file mode 100644 index 00000000000..b2c47584ae5 --- /dev/null +++ b/sdk/trace/span_limits.go @@ -0,0 +1,126 @@ +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import "go.opentelemetry.io/otel/sdk/internal/env" + +const ( + // DefaultAttributeValueLengthLimit is the default maximum allowed + // attribute value length, unlimited. + DefaultAttributeValueLengthLimit = -1 + + // DefaultAttributeCountLimit is the default maximum number of attributes + // a span can have. + DefaultAttributeCountLimit = 128 + + // DefaultEventCountLimit is the default maximum number of events a span + // can have. + DefaultEventCountLimit = 128 + + // DefaultLinkCountLimit is the default maximum number of links a span can + // have. + DefaultLinkCountLimit = 128 + + // DefaultAttributePerEventCountLimit is the default maximum number of + // attributes a span event can have. + DefaultAttributePerEventCountLimit = 128 + + // DefaultAttributePerLinkCountLimit is the default maximum number of + // attributes a span link can have. + DefaultAttributePerLinkCountLimit = 128 +) + +// SpanLimits represents the limits of a span. +type SpanLimits struct { + // AttributeValueLengthLimit is the maximum allowed attribute value length. + // + // This limit only applies to string and string slice attribute values. + // Any string longer than this value will be truncated to this length. + // + // Setting this to a negative value means no limit is applied. + AttributeValueLengthLimit int + + // AttributeCountLimit is the maximum allowed span attribute count. Any + // attribute added to a span once this limit is reached will be dropped. + // + // Setting this to zero means no attributes will be recorded. + // + // Setting this to a negative value means no limit is applied. + AttributeCountLimit int + + // EventCountLimit is the maximum allowed span event count. Any event + // added to a span once this limit is reached means it will be added but + // the oldest event will be dropped. + // + // Setting this to zero means no events we be recorded. + // + // Setting this to a negative value means no limit is applied. + EventCountLimit int + + // LinkCountLimit is the maximum allowed span link count. Any link added + // to a span once this limit is reached means it will be added but the + // oldest link will be dropped. + // + // Setting this to zero means no links we be recorded. + // + // Setting this to a negative value means no limit is applied. + LinkCountLimit int + + // AttributePerEventCountLimit is the maximum number of attributes allowed + // per span event. Any attribute added after this limit reached will be + // dropped. + // + // Setting this to zero means no attributes will be recorded for events. + // + // Setting this to a negative value means no limit is applied. + AttributePerEventCountLimit int + + // AttributePerLinkCountLimit is the maximum number of attributes allowed + // per span link. Any attribute added after this limit reached will be + // dropped. + // + // Setting this to zero means no attributes will be recorded for links. + // + // Setting this to a negative value means no limit is applied. + AttributePerLinkCountLimit int +} + +// NewSpanLimits returns a SpanLimits with all limits set to the value their +// corresponding environment variable holds, or the default if unset. +// +// • AttributeValueLengthLimit: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT +// (default: unlimited) +// +// • AttributeCountLimit: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT (default: 128) +// +// • EventCountLimit: OTEL_SPAN_EVENT_COUNT_LIMIT (default: 128) +// +// • AttributePerEventCountLimit: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT (default: +// 128) +// +// • LinkCountLimit: OTEL_SPAN_LINK_COUNT_LIMIT (default: 128) +// +// • AttributePerLinkCountLimit: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT (default: +// 128) +func NewSpanLimits() SpanLimits { + return SpanLimits{ + AttributeValueLengthLimit: env.SpanAttributeValueLength(DefaultAttributeValueLengthLimit), + AttributeCountLimit: env.SpanAttributeCount(DefaultAttributeCountLimit), + EventCountLimit: env.SpanEventCount(DefaultEventCountLimit), + LinkCountLimit: env.SpanLinkCount(DefaultLinkCountLimit), + AttributePerEventCountLimit: env.SpanEventAttributeCount(DefaultAttributePerEventCountLimit), + AttributePerLinkCountLimit: env.SpanLinkAttributeCount(DefaultAttributePerLinkCountLimit), + } +} diff --git a/sdk/trace/span_limits_test.go b/sdk/trace/span_limits_test.go new file mode 100644 index 00000000000..91199516c56 --- /dev/null +++ b/sdk/trace/span_limits_test.go @@ -0,0 +1,283 @@ +// 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 trace + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/attribute" + ottest "go.opentelemetry.io/otel/internal/internaltest" + "go.opentelemetry.io/otel/sdk/internal/env" + "go.opentelemetry.io/otel/trace" +) + +func TestSettingSpanLimits(t *testing.T) { + envLimits := func(val string) map[string]string { + return map[string]string{ + env.SpanAttributeValueLengthKey: val, + env.SpanEventCountKey: val, + env.SpanAttributeCountKey: val, + env.SpanLinkCountKey: val, + env.SpanEventAttributeCountKey: val, + env.SpanLinkAttributeCountKey: val, + } + } + + limits := func(n int) *SpanLimits { + lims := NewSpanLimits() + lims.AttributeValueLengthLimit = n + lims.AttributeCountLimit = n + lims.EventCountLimit = n + lims.LinkCountLimit = n + lims.AttributePerEventCountLimit = n + lims.AttributePerLinkCountLimit = n + return &lims + } + + tests := []struct { + name string + env map[string]string + opt *SpanLimits + rawOpt *SpanLimits + want SpanLimits + }{ + { + name: "defaults", + want: NewSpanLimits(), + }, + { + name: "env", + env: envLimits("42"), + want: *(limits(42)), + }, + { + name: "opt", + opt: limits(42), + want: *(limits(42)), + }, + { + name: "raw-opt", + rawOpt: limits(42), + want: *(limits(42)), + }, + { + name: "opt-override", + env: envLimits("-2"), + // Option take priority. + opt: limits(43), + want: *(limits(43)), + }, + { + name: "raw-opt-override", + env: envLimits("-2"), + // Option take priority. + rawOpt: limits(43), + want: *(limits(43)), + }, + { + name: "last-opt-wins", + opt: limits(-2), + rawOpt: limits(-3), + want: *(limits(-3)), + }, + { + name: "env(unlimited)", + // OTel spec says negative SpanLinkAttributeCountKey is invalid, + // but since we will revert to the default (unlimited) which uses + // negative values to signal this than this value is expected to + // pass through. + env: envLimits("-1"), + want: *(limits(-1)), + }, + { + name: "opt(unlimited)", + // Corrects to defaults. + opt: limits(-1), + want: NewSpanLimits(), + }, + { + name: "raw-opt(unlimited)", + rawOpt: limits(-1), + want: *(limits(-1)), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.env != nil { + es := ottest.NewEnvStore() + t.Cleanup(func() { require.NoError(t, es.Restore()) }) + for k, v := range test.env { + es.Record(k) + require.NoError(t, os.Setenv(k, v)) + } + } + + var opts []TracerProviderOption + if test.opt != nil { + opts = append(opts, WithSpanLimits(*test.opt)) + } + if test.rawOpt != nil { + opts = append(opts, WithRawSpanLimits(*test.rawOpt)) + } + + assert.Equal(t, test.want, NewTracerProvider(opts...).spanLimits) + }) + } +} + +type recorder []ReadOnlySpan + +func (r *recorder) OnStart(context.Context, ReadWriteSpan) {} +func (r *recorder) OnEnd(s ReadOnlySpan) { *r = append(*r, s) } +func (r *recorder) ForceFlush(context.Context) error { return nil } +func (r *recorder) Shutdown(context.Context) error { return nil } + +func testSpanLimits(t *testing.T, limits SpanLimits) ReadOnlySpan { + rec := new(recorder) + tp := NewTracerProvider(WithRawSpanLimits(limits), WithSpanProcessor(rec)) + tracer := tp.Tracer("testSpanLimits") + + ctx := context.Background() + a := []attribute.KeyValue{attribute.Bool("one", true), attribute.Bool("two", true)} + l := trace.Link{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: [16]byte{0x01}, + SpanID: [8]byte{0x01}, + }), + Attributes: a, + } + _, span := tracer.Start(ctx, "span-name", trace.WithLinks(l, l)) + span.SetAttributes( + attribute.String("string", "abc"), + attribute.StringSlice("stringSlice", []string{"abc", "def"}), + ) + span.AddEvent("event 1", trace.WithAttributes(a...)) + span.AddEvent("event 2", trace.WithAttributes(a...)) + span.End() + require.NoError(t, tp.Shutdown(ctx)) + + require.Len(t, *rec, 1, "exported spans") + return (*rec)[0] +} + +func TestSpanLimits(t *testing.T) { + t.Run("AttributeValueLengthLimit", func(t *testing.T) { + limits := NewSpanLimits() + // Unlimited. + limits.AttributeValueLengthLimit = -1 + attrs := testSpanLimits(t, limits).Attributes() + assert.Contains(t, attrs, attribute.String("string", "abc")) + assert.Contains(t, attrs, attribute.StringSlice("stringSlice", []string{"abc", "def"})) + + limits.AttributeValueLengthLimit = 2 + attrs = testSpanLimits(t, limits).Attributes() + // Ensure string and string slice attributes are truncated. + assert.Contains(t, attrs, attribute.String("string", "ab")) + assert.Contains(t, attrs, attribute.StringSlice("stringSlice", []string{"ab", "de"})) + + limits.AttributeValueLengthLimit = 0 + attrs = testSpanLimits(t, limits).Attributes() + assert.Contains(t, attrs, attribute.String("string", "")) + assert.Contains(t, attrs, attribute.StringSlice("stringSlice", []string{"", ""})) + }) + + t.Run("AttributeCountLimit", func(t *testing.T) { + limits := NewSpanLimits() + // Unlimited. + limits.AttributeCountLimit = -1 + assert.Len(t, testSpanLimits(t, limits).Attributes(), 2) + + limits.AttributeCountLimit = 1 + assert.Len(t, testSpanLimits(t, limits).Attributes(), 1) + + // Ensure this can be disabled. + limits.AttributeCountLimit = 0 + assert.Len(t, testSpanLimits(t, limits).Attributes(), 0) + }) + + t.Run("EventCountLimit", func(t *testing.T) { + limits := NewSpanLimits() + // Unlimited. + limits.EventCountLimit = -1 + assert.Len(t, testSpanLimits(t, limits).Events(), 2) + + limits.EventCountLimit = 1 + assert.Len(t, testSpanLimits(t, limits).Events(), 1) + + // Ensure this can be disabled. + limits.EventCountLimit = 0 + assert.Len(t, testSpanLimits(t, limits).Events(), 0) + }) + + t.Run("AttributePerEventCountLimit", func(t *testing.T) { + limits := NewSpanLimits() + // Unlimited. + limits.AttributePerEventCountLimit = -1 + for _, e := range testSpanLimits(t, limits).Events() { + assert.Len(t, e.Attributes, 2) + } + + limits.AttributePerEventCountLimit = 1 + for _, e := range testSpanLimits(t, limits).Events() { + assert.Len(t, e.Attributes, 1) + } + + // Ensure this can be disabled. + limits.AttributePerEventCountLimit = 0 + for _, e := range testSpanLimits(t, limits).Events() { + assert.Len(t, e.Attributes, 0) + } + }) + + t.Run("LinkCountLimit", func(t *testing.T) { + limits := NewSpanLimits() + // Unlimited. + limits.LinkCountLimit = -1 + assert.Len(t, testSpanLimits(t, limits).Links(), 2) + + limits.LinkCountLimit = 1 + assert.Len(t, testSpanLimits(t, limits).Links(), 1) + + // Ensure this can be disabled. + limits.LinkCountLimit = 0 + assert.Len(t, testSpanLimits(t, limits).Links(), 0) + }) + + t.Run("AttributePerLinkCountLimit", func(t *testing.T) { + limits := NewSpanLimits() + // Unlimited. + limits.AttributePerLinkCountLimit = -1 + for _, l := range testSpanLimits(t, limits).Links() { + assert.Len(t, l.Attributes, 2) + } + + limits.AttributePerLinkCountLimit = 1 + for _, l := range testSpanLimits(t, limits).Links() { + assert.Len(t, l.Attributes, 1) + } + + // Ensure this can be disabled. + limits.AttributePerLinkCountLimit = 0 + for _, l := range testSpanLimits(t, limits).Links() { + assert.Len(t, l.Attributes, 0) + } + }) +} diff --git a/sdk/trace/span_test.go b/sdk/trace/span_test.go new file mode 100644 index 00000000000..2c7992e457e --- /dev/null +++ b/sdk/trace/span_test.go @@ -0,0 +1,145 @@ +// 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 trace + +import ( + "bytes" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/attribute" +) + +func TestTruncateAttr(t *testing.T) { + const key = "key" + + strAttr := attribute.String(key, "value") + strSliceAttr := attribute.StringSlice(key, []string{"value-0", "value-1"}) + + tests := []struct { + limit int + attr, want attribute.KeyValue + }{ + { + limit: -1, + attr: strAttr, + want: strAttr, + }, + { + limit: -1, + attr: strSliceAttr, + want: strSliceAttr, + }, + { + limit: 0, + attr: attribute.Bool(key, true), + want: attribute.Bool(key, true), + }, + { + limit: 0, + attr: attribute.BoolSlice(key, []bool{true, false}), + want: attribute.BoolSlice(key, []bool{true, false}), + }, + { + limit: 0, + attr: attribute.Int(key, 42), + want: attribute.Int(key, 42), + }, + { + limit: 0, + attr: attribute.IntSlice(key, []int{42, -1}), + want: attribute.IntSlice(key, []int{42, -1}), + }, + { + limit: 0, + attr: attribute.Int64(key, 42), + want: attribute.Int64(key, 42), + }, + { + limit: 0, + attr: attribute.Int64Slice(key, []int64{42, -1}), + want: attribute.Int64Slice(key, []int64{42, -1}), + }, + { + limit: 0, + attr: attribute.Float64(key, 42), + want: attribute.Float64(key, 42), + }, + { + limit: 0, + attr: attribute.Float64Slice(key, []float64{42, -1}), + want: attribute.Float64Slice(key, []float64{42, -1}), + }, + { + limit: 0, + attr: strAttr, + want: attribute.String(key, ""), + }, + { + limit: 0, + attr: strSliceAttr, + want: attribute.StringSlice(key, []string{"", ""}), + }, + { + limit: 0, + attr: attribute.Stringer(key, bytes.NewBufferString("value")), + want: attribute.String(key, ""), + }, + { + limit: 1, + attr: strAttr, + want: attribute.String(key, "v"), + }, + { + limit: 1, + attr: strSliceAttr, + want: attribute.StringSlice(key, []string{"v", "v"}), + }, + { + limit: 5, + attr: strAttr, + want: strAttr, + }, + { + limit: 7, + attr: strSliceAttr, + want: strSliceAttr, + }, + { + limit: 6, + attr: attribute.StringSlice(key, []string{"value", "value-1"}), + want: attribute.StringSlice(key, []string{"value", "value-"}), + }, + { + limit: 128, + attr: strAttr, + want: strAttr, + }, + { + limit: 128, + attr: strSliceAttr, + want: strSliceAttr, + }, + } + + for _, test := range tests { + name := fmt.Sprintf("%s->%s(limit:%d)", test.attr.Key, test.attr.Value.Emit(), test.limit) + t.Run(name, func(t *testing.T) { + assert.Equal(t, test.want, truncateAttr(test.limit, test.attr)) + }) + } +} diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go index 5ba0015584a..fe401d06535 100644 --- a/sdk/trace/trace_test.go +++ b/sdk/trace/trace_test.go @@ -604,10 +604,9 @@ func TestSpanSetAttributes(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { te := NewTestExporter() - tp := NewTracerProvider( - WithSyncer(te), - WithSpanLimits(SpanLimits{AttributeCountLimit: capacity}), - ) + sl := NewSpanLimits() + sl.AttributeCountLimit = capacity + tp := NewTracerProvider(WithSyncer(te), WithSpanLimits(sl)) _, span := tp.Tracer(instName).Start(context.Background(), spanName) for _, a := range test.input { span.SetAttributes(a...) @@ -677,7 +676,9 @@ func TestEvents(t *testing.T) { func TestEventsOverLimit(t *testing.T) { te := NewTestExporter() - tp := NewTracerProvider(WithSpanLimits(SpanLimits{EventCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty())) + sl := NewSpanLimits() + sl.EventCountLimit = 2 + tp := NewTracerProvider(WithSpanLimits(sl), WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "EventsOverLimit") k1v1 := attribute.String("key1", "value1") @@ -779,7 +780,9 @@ func TestLinksOverLimit(t *testing.T) { sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) sc3 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) - tp := NewTracerProvider(WithSpanLimits(SpanLimits{LinkCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty())) + sl := NewSpanLimits() + sl.LinkCountLimit = 2 + tp := NewTracerProvider(WithSpanLimits(sl), WithSyncer(te), WithResource(resource.Empty())) span := startSpan(tp, "LinksOverLimit", trace.WithLinks( @@ -1637,8 +1640,10 @@ func TestReadWriteSpan(t *testing.T) { func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) { te := NewTestExporter() + sl := NewSpanLimits() + sl.AttributePerEventCountLimit = 2 tp := NewTracerProvider( - WithSpanLimits(SpanLimits{AttributePerEventCountLimit: 2}), + WithSpanLimits(sl), WithSyncer(te), WithResource(resource.Empty()), ) @@ -1701,8 +1706,10 @@ func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) { func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) { te := NewTestExporter() + sl := NewSpanLimits() + sl.AttributePerLinkCountLimit = 1 tp := NewTracerProvider( - WithSpanLimits(SpanLimits{AttributePerLinkCountLimit: 1}), + WithSpanLimits(sl), WithSyncer(te), WithResource(resource.Empty()), )