From 11a0a2ab4e8bb28c8dcf11b33e7d02fd99c61a19 Mon Sep 17 00:00:00 2001 From: Mohamed Elqdusy Date: Fri, 26 Jul 2019 19:56:43 +0200 Subject: [PATCH] Implement factory and config for sampling processor (head-based) (#186) * Implement factory and config for sampling processor (head-based) * Change Config name --- processor/tracesamplerprocessor/config.go | 29 ++++++++ .../tracesamplerprocessor/config_test.go | 73 +++++++++++++++++++ processor/tracesamplerprocessor/factory.go | 67 +++++++++++++++++ .../tracesamplerprocessor/factory_test.go | 45 ++++++++++++ .../testdata/config.yaml | 16 ++++ .../tracesamplerprocessor/testdata/empty.yaml | 15 ++++ .../tracesamplerprocessor.go | 17 +---- .../tracesamplerprocessor_test.go | 32 ++++---- 8 files changed, 264 insertions(+), 30 deletions(-) create mode 100644 processor/tracesamplerprocessor/config.go create mode 100644 processor/tracesamplerprocessor/config_test.go create mode 100644 processor/tracesamplerprocessor/factory.go create mode 100644 processor/tracesamplerprocessor/factory_test.go create mode 100644 processor/tracesamplerprocessor/testdata/config.yaml create mode 100644 processor/tracesamplerprocessor/testdata/empty.yaml diff --git a/processor/tracesamplerprocessor/config.go b/processor/tracesamplerprocessor/config.go new file mode 100644 index 00000000000..5976d91670f --- /dev/null +++ b/processor/tracesamplerprocessor/config.go @@ -0,0 +1,29 @@ +// Copyright 2019, 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 tracesamplerprocessor + +import "github.com/open-telemetry/opentelemetry-service/config/configmodels" + +// Config has the configuration guiding the trace sampler processor. +type Config struct { + configmodels.ProcessorSettings `mapstructure:",squash"` + // SamplingPercentage is the percentage rate at which traces are going to be sampled. Defaults to zero, i.e.: no sample. + // Values greater or equal 100 are treated as "sample all traces". + SamplingPercentage float32 `mapstructure:"sampling-percentage"` + // HashSeed allows one to configure the hashing seed. This is important in scenarios where multiple layers of collectors + // have different sampling rates: if they use the same seed all passing one layer may pass the other even if they have + // different sampling rates, configuring different seeds avoids that. + HashSeed uint32 `mapstructure:"hash-seed"` +} diff --git a/processor/tracesamplerprocessor/config_test.go b/processor/tracesamplerprocessor/config_test.go new file mode 100644 index 00000000000..06039dd850e --- /dev/null +++ b/processor/tracesamplerprocessor/config_test.go @@ -0,0 +1,73 @@ +// Copyright 2019, 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 tracesamplerprocessor + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/open-telemetry/opentelemetry-service/config" + "github.com/open-telemetry/opentelemetry-service/config/configmodels" + "github.com/open-telemetry/opentelemetry-service/processor" +) + +func TestLoadConfig(t *testing.T) { + receivers, processors, exporters, err := config.ExampleComponents() + assert.Nil(t, err) + + factory := &Factory{} + processors[typeStr] = factory + cfg, err := config.LoadConfigFile( + t, path.Join(".", "testdata", "config.yaml"), receivers, processors, exporters, + ) + + require.Nil(t, err) + require.NotNil(t, cfg) + + p0 := cfg.Processors["trace-sampler"] + assert.Equal(t, p0, + &Config{ + ProcessorSettings: configmodels.ProcessorSettings{ + TypeVal: "trace-sampler", + NameVal: "trace-sampler", + }, + SamplingPercentage: 15.3, + HashSeed: 22, + }) + +} + +func TestLoadConfigEmpty(t *testing.T) { + receivers, _, exporters, err := config.ExampleComponents() + processors, err := processor.Build(&Factory{}) + require.NotNil(t, processors) + require.NoError(t, err) + + config, err := config.LoadConfigFile( + t, + path.Join(".", "testdata", "empty.yaml"), + receivers, + processors, + exporters) + + require.Nil(t, err) + require.NotNil(t, config) + p0 := config.Processors["trace-sampler"] + factory := &Factory{} + assert.Equal(t, p0, factory.CreateDefaultConfig()) +} diff --git a/processor/tracesamplerprocessor/factory.go b/processor/tracesamplerprocessor/factory.go new file mode 100644 index 00000000000..49285d164a6 --- /dev/null +++ b/processor/tracesamplerprocessor/factory.go @@ -0,0 +1,67 @@ +// Copyright 2019, 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 tracesamplerprocessor + +import ( + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-service/config/configerror" + "github.com/open-telemetry/opentelemetry-service/config/configmodels" + "github.com/open-telemetry/opentelemetry-service/consumer" + "github.com/open-telemetry/opentelemetry-service/processor" +) + +const ( + // The value of "type" trace-samplers in configuration. + typeStr = "trace-sampler" +) + +// Factory is the factory for trace-sample processor. +type Factory struct { +} + +// Type gets the type of the config created by this factory. +func (f *Factory) Type() string { + return typeStr +} + +// CreateDefaultConfig creates the default configuration for processor. +func (f *Factory) CreateDefaultConfig() configmodels.Processor { + return &Config{ + ProcessorSettings: configmodels.ProcessorSettings{ + TypeVal: typeStr, + NameVal: typeStr, + }, + } +} + +// CreateTraceProcessor creates a trace processor based on this config. +func (f *Factory) CreateTraceProcessor( + logger *zap.Logger, + nextConsumer consumer.TraceConsumer, + cfg configmodels.Processor, +) (processor.TraceProcessor, error) { + oCfg := cfg.(*Config) + return NewTraceProcessor(nextConsumer, *oCfg) +} + +// CreateMetricsProcessor creates a metrics processor based on this config. +func (f *Factory) CreateMetricsProcessor( + logger *zap.Logger, + nextConsumer consumer.MetricsConsumer, + cfg configmodels.Processor, +) (processor.MetricsProcessor, error) { + return nil, configerror.ErrDataTypeIsNotSupported +} diff --git a/processor/tracesamplerprocessor/factory_test.go b/processor/tracesamplerprocessor/factory_test.go new file mode 100644 index 00000000000..8c5771e78fc --- /dev/null +++ b/processor/tracesamplerprocessor/factory_test.go @@ -0,0 +1,45 @@ +// Copyright 2019, 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 tracesamplerprocessor + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-service/exporter/exportertest" +) + +func TestCreateDefaultConfig(t *testing.T) { + factory := &Factory{} + + cfg := factory.CreateDefaultConfig() + assert.NotNil(t, cfg, "failed to create default config") +} + +func TestCreateProcessor(t *testing.T) { + factory := &Factory{} + + cfg := factory.CreateDefaultConfig() + + tp, err := factory.CreateTraceProcessor(zap.NewNop(), exportertest.NewNopTraceExporter(), cfg) + assert.NotNil(t, tp) + assert.NoError(t, err, "cannot create trace processor") + + mp, err := factory.CreateMetricsProcessor(zap.NewNop(), nil, cfg) + assert.Nil(t, mp) + assert.Error(t, err, "should not be able to create metric processor") +} diff --git a/processor/tracesamplerprocessor/testdata/config.yaml b/processor/tracesamplerprocessor/testdata/config.yaml new file mode 100644 index 00000000000..ba76d6f0a42 --- /dev/null +++ b/processor/tracesamplerprocessor/testdata/config.yaml @@ -0,0 +1,16 @@ +receivers: + examplereceiver: + +processors: + trace-sampler: + sampling-percentage: 15.3 + hash-seed: 22 + +exporters: + exampleexporter: + +pipelines: + traces: + receivers: [examplereceiver] + processors: [trace-sampler] + exporters: [exampleexporter] diff --git a/processor/tracesamplerprocessor/testdata/empty.yaml b/processor/tracesamplerprocessor/testdata/empty.yaml new file mode 100644 index 00000000000..e320a9a4fb2 --- /dev/null +++ b/processor/tracesamplerprocessor/testdata/empty.yaml @@ -0,0 +1,15 @@ +receivers: + examplereceiver: + +processors: + trace-sampler: + + +exporters: + exampleexporter: + +pipelines: + traces: + receivers: [examplereceiver] + processors: [trace-sampler] + exporters: [exampleexporter] diff --git a/processor/tracesamplerprocessor/tracesamplerprocessor.go b/processor/tracesamplerprocessor/tracesamplerprocessor.go index ef87f32bb94..670884f7d8e 100644 --- a/processor/tracesamplerprocessor/tracesamplerprocessor.go +++ b/processor/tracesamplerprocessor/tracesamplerprocessor.go @@ -38,19 +38,8 @@ const ( percentageScaleFactor = numHashBuckets / 100.0 ) -// TraceSamplerCfg has the configuration guiding the trace sampler processor. -type TraceSamplerCfg struct { - // SamplingPercentage is the percentage rate at which traces are going to be sampled. Defaults to zero, i.e.: no sample. - // Values greater or equal 100 are treated as "sample all traces". - SamplingPercentage float32 - // HashSeed allows one to configure the hashing seed. This is important in scenarios where multiple layers of collectors - // have different sampling rates: if they use the same seed all passing one layer may pass the other even if they have - // different sampling rates, configuring different seeds avoids that. - HashSeed uint32 -} - -// InitFromViper updates TraceSamplerCfg according to the viper configuration. -func (tsc *TraceSamplerCfg) InitFromViper(v *viper.Viper) (*TraceSamplerCfg, error) { +// InitFromViper updates TraceSampler config according to the viper configuration. +func (tsc *Config) InitFromViper(v *viper.Viper) (*Config, error) { if v == nil { return nil, errors.New("v is nil") } @@ -73,7 +62,7 @@ var _ processor.TraceProcessor = (*tracesamplerprocessor)(nil) // NewTraceProcessor returns a processor.TraceProcessor that will perform head sampling according to the given // configuration. -func NewTraceProcessor(nextConsumer consumer.TraceConsumer, cfg TraceSamplerCfg) (processor.TraceProcessor, error) { +func NewTraceProcessor(nextConsumer consumer.TraceConsumer, cfg Config) (processor.TraceProcessor, error) { if nextConsumer == nil { return nil, errors.New("nextConsumer is nil") } diff --git a/processor/tracesamplerprocessor/tracesamplerprocessor_test.go b/processor/tracesamplerprocessor/tracesamplerprocessor_test.go index c25b7ca1ac9..190ee80281b 100644 --- a/processor/tracesamplerprocessor/tracesamplerprocessor_test.go +++ b/processor/tracesamplerprocessor/tracesamplerprocessor_test.go @@ -33,7 +33,7 @@ import ( tracetranslator "github.com/open-telemetry/opentelemetry-service/translator/trace" ) -func TestTraceSamplerCfg_InitFromViper(t *testing.T) { +func TestConfig_InitFromViper(t *testing.T) { type fields struct { SamplingPercentage float32 } @@ -41,7 +41,7 @@ func TestTraceSamplerCfg_InitFromViper(t *testing.T) { name string fields fields genViperFn func() *viper.Viper - want *TraceSamplerCfg + want *Config wantErr bool }{ { @@ -76,7 +76,7 @@ func TestTraceSamplerCfg_InitFromViper(t *testing.T) { v.Set(samplingPercentageCfgTag, 5) return v }, - want: &TraceSamplerCfg{ + want: &Config{ SamplingPercentage: 5.0, }, }, @@ -88,7 +88,7 @@ func TestTraceSamplerCfg_InitFromViper(t *testing.T) { v.Set(hashSeedCfgTag, 1234) return v }, - want: &TraceSamplerCfg{ + want: &Config{ SamplingPercentage: 0.03, HashSeed: 1234, }, @@ -96,16 +96,16 @@ func TestTraceSamplerCfg_InitFromViper(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tsc := &TraceSamplerCfg{ + tsc := &Config{ SamplingPercentage: tt.fields.SamplingPercentage, } got, err := tsc.InitFromViper(tt.genViperFn()) if (err != nil) != tt.wantErr { - t.Errorf("TraceSamplerCfg.InitFromViper() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Config.InitFromViper() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("TraceSamplerCfg.InitFromViper() = %v, want %v", got, tt.want) + t.Errorf("Config.InitFromViper() = %v, want %v", got, tt.want) } }) } @@ -115,7 +115,7 @@ func TestNewTraceProcessor(t *testing.T) { tests := []struct { name string nextConsumer consumer.TraceConsumer - cfg TraceSamplerCfg + cfg Config want processor.TraceProcessor wantErr bool }{ @@ -126,7 +126,7 @@ func TestNewTraceProcessor(t *testing.T) { { name: "happy_path", nextConsumer: &exportertest.SinkTraceExporter{}, - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 15.5, }, want: &tracesamplerprocessor{ @@ -136,7 +136,7 @@ func TestNewTraceProcessor(t *testing.T) { { name: "happy_path_hash_seed", nextConsumer: &exportertest.SinkTraceExporter{}, - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 13.33, HashSeed: 4321, }, @@ -169,14 +169,14 @@ func TestNewTraceProcessor(t *testing.T) { func Test_tracesamplerprocessor_SamplingPercentageRange(t *testing.T) { tests := []struct { name string - cfg TraceSamplerCfg + cfg Config numBatches int numTracesPerBatch int acceptableDelta float64 }{ { name: "random_sampling_tiny", - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 0.03, }, numBatches: 1e5, @@ -185,7 +185,7 @@ func Test_tracesamplerprocessor_SamplingPercentageRange(t *testing.T) { }, { name: "random_sampling_small", - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 5, }, numBatches: 1e5, @@ -194,7 +194,7 @@ func Test_tracesamplerprocessor_SamplingPercentageRange(t *testing.T) { }, { name: "random_sampling_medium", - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 50.0, }, numBatches: 1e5, @@ -203,7 +203,7 @@ func Test_tracesamplerprocessor_SamplingPercentageRange(t *testing.T) { }, { name: "random_sampling_high", - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 90.0, }, numBatches: 1e5, @@ -212,7 +212,7 @@ func Test_tracesamplerprocessor_SamplingPercentageRange(t *testing.T) { }, { name: "random_sampling_all", - cfg: TraceSamplerCfg{ + cfg: Config{ SamplingPercentage: 100.0, }, numBatches: 1e5,