Skip to content

Commit

Permalink
Update span limits to comply with specification (open-telemetry#2637)
Browse files Browse the repository at this point in the history
* 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 <a9@aneurysm9.com>

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
  • Loading branch information
2 people authored and hanyuancheung committed Mar 5, 2022
1 parent aafe1c3 commit 8386f01
Show file tree
Hide file tree
Showing 12 changed files with 895 additions and 202 deletions.
23 changes: 21 additions & 2 deletions CHANGELOG.md
Expand Up @@ -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

Expand All @@ -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

Expand Down
61 changes: 56 additions & 5 deletions sdk/internal/env/env.go
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
102 changes: 99 additions & 3 deletions sdk/trace/benchmark_test.go
Expand Up @@ -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)
Expand Down
84 changes: 0 additions & 84 deletions sdk/trace/config.go

This file was deleted.

7 changes: 6 additions & 1 deletion sdk/trace/evictedqueue.go
Expand Up @@ -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]
Expand Down

0 comments on commit 8386f01

Please sign in to comment.