Skip to content

Commit

Permalink
[processor/tailsampling] Adds BooleanAttribute PolicyType (#18764)
Browse files Browse the repository at this point in the history
  • Loading branch information
petercline authored Feb 27, 2023
1 parent 57cfee7 commit 94e4025
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 1 deletion.
16 changes: 16 additions & 0 deletions .chloggen/boolean-attribute.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: processor/tailsampling

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: adds support for a BooleanAttribute PolicyType

# One or more tracking issues related to the change
issues: [17545]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: enables use of boolean attrbiutes in defining tail sampling policies
6 changes: 6 additions & 0 deletions processor/tailsamplingprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Multiple policies exist today and it is straight forward to add more. These incl
- `trace_state`: Sample based on [TraceState](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracestate) value matches
- `rate_limiting`: Sample based on rate
- `span_count`: Sample based on the minimum number of spans within a batch. If all traces within the batch have less number of spans than the threshold, the batch will not be sampled.
- `boolean_attribute`: Sample based on boolean attribute (resource and record).
- `and`: Sample based on multiple policies, creates an AND policy
- `composite`: Sample based on a combination of above samplers, with ordering and rate allocation per sampler. Rate allocation allocates certain percentages of spans per policy order.
For example if we have set max_total_spans_per_second as 100 then we can set rate_allocation as follows
Expand Down Expand Up @@ -108,6 +109,11 @@ processors:
type: trace_state,
trace_state: { key: key3, values: [value1, value2] }
},
{
name: test-policy-12,
type: boolean_attribute,
boolean_attrbiute: {key: key4, value: true}
},
{
name: and-policy-1,
type: and,
Expand Down
17 changes: 16 additions & 1 deletion processor/tailsamplingprocessor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const (
Probabilistic PolicyType = "probabilistic"
// StatusCode sample traces that have a given status code.
StatusCode PolicyType = "status_code"
// StringAttribute sample traces that a attribute, of type string, matching
// StringAttribute sample traces that an attribute, of type string, matching
// one of the listed values.
StringAttribute PolicyType = "string_attribute"
// RateLimiting allows all traces until the specified limits are satisfied.
Expand All @@ -46,6 +46,9 @@ const (
SpanCount PolicyType = "span_count"
// TraceState sample traces with specified values by the given key
TraceState PolicyType = "trace_state"
// BooleanAttribute sample traces having an attribute, of type bool, that matches
// the specified boolean value [true|false].
BooleanAttribute PolicyType = "boolean_attribute"
)

// sharedPolicyCfg holds the common configuration to all policies that are used in derivative policy configurations
Expand All @@ -71,6 +74,8 @@ type sharedPolicyCfg struct {
SpanCountCfg SpanCountCfg `mapstructure:"span_count"`
// Configs for defining trace_state policy
TraceStateCfg TraceStateCfg `mapstructure:"trace_state"`
// Configs for boolean attribute filter sampling policy evaluator.
BooleanAttributeCfg BooleanAttributeCfg `mapstructure:"boolean_attribute"`
}

// CompositeSubPolicyCfg holds the common configuration to all policies under composite policy.
Expand Down Expand Up @@ -192,6 +197,16 @@ type SpanCountCfg struct {
MinSpans int32 `mapstructure:"min_spans"`
}

// BooleanAttributeCfg holds the configurable settings to create a boolean attribute filter
// sampling policy evaluator.
type BooleanAttributeCfg struct {
// Tag that the filter is going to be matching against.
Key string `mapstructure:"key"`
// Value indicate the bool value, either true or false to use when matching against attribute values.
// BooleanAttribute Policy will apply exact value match on Value
Value bool `mapstructure:"value"`
}

// Config holds the configuration for tail-based sampling.
type Config struct {
// DecisionWait is the desired wait time from the arrival of the first span of
Expand Down
7 changes: 7 additions & 0 deletions processor/tailsamplingprocessor/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ func TestLoadConfig(t *testing.T) {
TraceStateCfg: TraceStateCfg{Key: "key3", Values: []string{"value1", "value2"}},
},
},
{
sharedPolicyCfg: sharedPolicyCfg{
Name: "test-policy-10",
Type: BooleanAttribute,
BooleanAttributeCfg: BooleanAttributeCfg{Key: "key4", Value: true},
},
},
{
sharedPolicyCfg: sharedPolicyCfg{
Name: "and-policy-1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 sampling // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/internal/sampling"

import (
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
"go.uber.org/zap"
)

type booleanAttributeFilter struct {
key string
value bool
logger *zap.Logger
}

var _ PolicyEvaluator = (*booleanAttributeFilter)(nil)

// NewBooleanAttributeFilter creates a policy evaluator that samples all traces with
// the given attribute that match the supplied boolean value.
func NewBooleanAttributeFilter(logger *zap.Logger, key string, value bool) PolicyEvaluator {
return &booleanAttributeFilter{
key: key,
value: value,
logger: logger,
}
}

// Evaluate looks at the trace data and returns a corresponding SamplingDecision.
func (baf *booleanAttributeFilter) Evaluate(_ pcommon.TraceID, trace *TraceData) (Decision, error) {
trace.Lock()
batches := trace.ReceivedBatches
trace.Unlock()

return hasResourceOrSpanWithCondition(
batches,
func(resource pcommon.Resource) bool {
if v, ok := resource.Attributes().Get(baf.key); ok {
value := v.Bool()
return value == baf.value
}
return false
},
func(span ptrace.Span) bool {
if v, ok := span.Attributes().Get(baf.key); ok {
value := v.Bool()
return value == baf.value
}
return false
}), nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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 sampling

import (
"testing"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
"go.uber.org/zap"
)

func TestBooleanTagFilter(t *testing.T) {

var empty = map[string]interface{}{}
filter := NewBooleanAttributeFilter(zap.NewNop(), "example", true)

resAttr := map[string]interface{}{}
resAttr["example"] = 8

cases := []struct {
Desc string
Trace *TraceData
Decision Decision
}{
{
Desc: "non-matching span attribute",
Trace: newTraceBoolAttrs(empty, "non_matching", true),
Decision: NotSampled,
},
{
Desc: "span attribute with unwanted boolean value",
Trace: newTraceBoolAttrs(empty, "example", false),
Decision: NotSampled,
},
{
Desc: "span attribute with wanted boolean value",
Trace: newTraceBoolAttrs(empty, "example", true),
Decision: Sampled,
},
}

for _, c := range cases {
t.Run(c.Desc, func(t *testing.T) {
u, _ := uuid.NewRandom()
decision, err := filter.Evaluate(pcommon.TraceID(u), c.Trace)
assert.NoError(t, err)
assert.Equal(t, decision, c.Decision)
})
}
}

func newTraceBoolAttrs(nodeAttrs map[string]interface{}, spanAttrKey string, spanAttrValue bool) *TraceData {
traces := ptrace.NewTraces()
rs := traces.ResourceSpans().AppendEmpty()
//nolint:errcheck
rs.Resource().Attributes().FromRaw(nodeAttrs)
ils := rs.ScopeSpans().AppendEmpty()
span := ils.Spans().AppendEmpty()
span.SetTraceID([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})
span.SetSpanID([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
span.Attributes().PutBool(spanAttrKey, spanAttrValue)
return &TraceData{
ReceivedBatches: traces,
}
}
3 changes: 3 additions & 0 deletions processor/tailsamplingprocessor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ func getSharedPolicyEvaluator(logger *zap.Logger, cfg *sharedPolicyCfg) (samplin
case TraceState:
tsfCfg := cfg.TraceStateCfg
return sampling.NewTraceStateFilter(logger, tsfCfg.Key, tsfCfg.Values), nil
case BooleanAttribute:
bafCfg := cfg.BooleanAttributeCfg
return sampling.NewBooleanAttributeFilter(logger, bafCfg.Key, bafCfg.Value), nil
default:
return nil, fmt.Errorf("unknown sampling policy type %s", cfg.Type)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ tail_sampling:
type: trace_state,
trace_state: { key: key3, values: [ value1, value2 ] }
},
{
name: test-policy-10,
type: boolean_attribute,
boolean_attribute: { key: key4, value: true }
},
{
name: and-policy-1,
type: and,
Expand Down

0 comments on commit 94e4025

Please sign in to comment.