diff --git a/CHANGELOG.md b/CHANGELOG.md index e9dd18503..2ed27c3e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm (#720) - The `OTEL_TRACES_SAMPLER` environment variable is now honored instead of only defaulting to an always-on sampler. (#724) +- Set span limits to the Splunk defaults (the link count is limited to 1000, + the attribute value length is limited to 12000, and all other limts are set + to be unlimited) if they are not set by the user with environment variables. + (#723) ### Fixed diff --git a/distro/config.go b/distro/config.go index a784c953e..7f3f8ec12 100644 --- a/distro/config.go +++ b/distro/config.go @@ -46,9 +46,6 @@ const ( // Logging level to set when using the default logger. otelLogLevelKey = "OTEL_LOG_LEVEL" - // FIXME: support OTEL_SPAN_LINK_COUNT_LIMIT - // FIXME: support OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT - // splunkMetricsEndpointKey defines the endpoint Splunk specific metrics // are sent. This is not currently supported. splunkMetricsEndpointKey = "SPLUNK_METRICS_ENDPOINT" @@ -74,6 +71,7 @@ type exporterConfig struct { type config struct { Logger logr.Logger Propagator propagation.TextMapPropagator + SpanLimits *trace.SpanLimits ExportConfig *exporterConfig TraceExporterFunc traceExporterFunc @@ -82,7 +80,8 @@ type config struct { // newConfig returns a validated config with Splunk defaults. func newConfig(opts ...Option) *config { c := &config{ - Logger: logger(zapConfig(envOr(otelLogLevelKey, defaultLogLevel))), + Logger: logger(zapConfig(envOr(otelLogLevelKey, defaultLogLevel))), + SpanLimits: newSpanLimits(), ExportConfig: &exporterConfig{ AccessToken: envOr(accessTokenKey, defaultAccessToken), }, diff --git a/distro/limits.go b/distro/limits.go new file mode 100644 index 000000000..31fc2cb6d --- /dev/null +++ b/distro/limits.go @@ -0,0 +1,76 @@ +// Copyright Splunk Inc. +// +// 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 distro + +import ( + "os" + + "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + attributeValueLengthKey = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT" + spanAttributeValueLengthKey = "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" + spanAttributeValueLengthDefault = 12000 + + attributeCountKey = "OTEL_ATTRIBUTE_COUNT_LIMIT" + spanAttributeCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" + spanAttributeCountDefault = -1 // Unlimited. + + spanEventCountKey = "OTEL_SPAN_EVENT_COUNT_LIMIT" + spanEventCountDefault = -1 // Unlimited. + + spanEventAttributeCountKey = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT" + spanEventAttributeCountDefault = -1 // Unlimited. + + spanLinkCountKey = "OTEL_SPAN_LINK_COUNT_LIMIT" + spanLinkCountDefault = 1000 + + spanLinkAttributeCountKey = "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT" + spanLinkAttributeCountDefault = -1 // Unlimited. +) + +// newSpanLimits returns new span limits that use Splunk defaults (the link +// count is limited to 1000, the attribute value length is limited to 12000, +// and all other limts are set to be unlimited) or the corresponding OTel +// environment variable value if it is set. +func newSpanLimits() *trace.SpanLimits { + // Use trace.NewSpanLimits here to ensure any future additions are not set + // to zero, which would happen if we delared with &trace.SpanLimits{...}. + limits := trace.NewSpanLimits() + + // limits will use OTel defaults or the applicable environment variable if + // they are set. The Splunk defaults need to be applied only if the + // environment variables were unset. + limits.AttributeValueLengthLimit = limitValue(limits.AttributeValueLengthLimit, spanAttributeValueLengthDefault, attributeValueLengthKey, spanAttributeValueLengthKey) + limits.AttributeCountLimit = limitValue(limits.AttributeCountLimit, spanAttributeCountDefault, attributeCountKey, spanAttributeCountKey) + limits.EventCountLimit = limitValue(limits.EventCountLimit, spanEventCountDefault, spanEventCountKey) + limits.LinkCountLimit = limitValue(limits.LinkCountLimit, spanLinkCountDefault, spanLinkCountKey) + limits.AttributePerEventCountLimit = limitValue(limits.AttributePerEventCountLimit, spanEventAttributeCountDefault, spanEventAttributeCountKey) + limits.AttributePerLinkCountLimit = limitValue(limits.AttributePerLinkCountLimit, spanLinkAttributeCountDefault, spanLinkAttributeCountKey) + + return &limits +} + +// limitValue returns the current limit value if it was set because one of +// envs is defined, otherwise it returns the limit zero value. +func limitValue(current, zero int, envs ...string) int { + for _, env := range envs { + if _, ok := os.LookupEnv(env); ok { + return current + } + } + return zero +} diff --git a/distro/limits_test.go b/distro/limits_test.go new file mode 100644 index 000000000..819df2f81 --- /dev/null +++ b/distro/limits_test.go @@ -0,0 +1,111 @@ +// Copyright Splunk Inc. +// +// 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 distro + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/sdk/trace" +) + +func expectedSL(aLen, aN, eN, lN, aPerE, aPerL int) *trace.SpanLimits { + return &trace.SpanLimits{ + AttributeValueLengthLimit: aLen, + AttributeCountLimit: aN, + EventCountLimit: eN, + LinkCountLimit: lN, + AttributePerEventCountLimit: aPerE, + AttributePerLinkCountLimit: aPerL, + } +} + +func TestNewSpanLimits(t *testing.T) { + tests := []struct { + name string + envs map[string]string + want *trace.SpanLimits + }{ + { + name: "defaults", + want: expectedSL(12000, -1, -1, 1000, -1, -1), + }, + { + name: attributeValueLengthKey, + envs: map[string]string{ + attributeValueLengthKey: "10", + }, + want: expectedSL(10, -1, -1, 1000, -1, -1), + }, + { + name: spanAttributeValueLengthKey, + envs: map[string]string{ + spanAttributeValueLengthKey: "10", + }, + want: expectedSL(10, -1, -1, 1000, -1, -1), + }, + { + name: attributeCountKey, + envs: map[string]string{ + attributeCountKey: "10", + }, + want: expectedSL(12000, 10, -1, 1000, -1, -1), + }, + { + name: spanAttributeCountKey, + envs: map[string]string{ + spanAttributeCountKey: "10", + }, + want: expectedSL(12000, 10, -1, 1000, -1, -1), + }, + { + name: spanEventCountKey, + envs: map[string]string{ + spanEventCountKey: "10", + }, + want: expectedSL(12000, -1, 10, 1000, -1, -1), + }, + { + name: spanEventAttributeCountKey, + envs: map[string]string{ + spanEventAttributeCountKey: "10", + }, + want: expectedSL(12000, -1, -1, 1000, 10, -1), + }, + { + name: spanLinkCountKey, + envs: map[string]string{ + spanLinkCountKey: "10", + }, + want: expectedSL(12000, -1, -1, 10, -1, -1), + }, + { + name: spanLinkAttributeCountKey, + envs: map[string]string{ + spanLinkAttributeCountKey: "10", + }, + want: expectedSL(12000, -1, -1, 1000, -1, 10), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for key, val := range test.envs { + t.Cleanup(Setenv(key, val)) + } + assert.Equal(t, test.want, newSpanLimits()) + }) + } +} diff --git a/distro/otel.go b/distro/otel.go index 98e04e027..76692aa23 100644 --- a/distro/otel.go +++ b/distro/otel.go @@ -104,6 +104,7 @@ func Run(opts ...Option) (SDK, error) { o := []trace.TracerProviderOption{ trace.WithResource(res), + trace.WithRawSpanLimits(*c.SpanLimits), trace.WithSpanProcessor(trace.NewBatchSpanProcessor(exp)), } if _, ok := os.LookupEnv(tracesSamplerKey); !ok {