diff --git a/pkg/client/client.go b/pkg/client/client.go index 26eafe2d6..d9e58b1d6 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -350,9 +350,16 @@ func (o *OptimizelyClient) getFeatureVariable(featureKey, variableKey string, us variable := featureDecisionContext.Variable if featureDecision.Variation != nil { - if v, ok := featureDecision.Variation.Variables[variable.ID]; ok && featureDecision.Variation.FeatureEnabled { - return v.Value, variable.Type, &featureDecision, nil + if featureDecision.Variation.FeatureEnabled { + if v, ok := featureDecision.Variation.Variables[variable.ID]; ok { + o.logger.Debug(fmt.Sprintf(logging.VariableValueForFeatureFlag.String(), v.Value, variable.Key, featureKey)) + return v.Value, variable.Type, &featureDecision, nil + } + } else { + o.logger.Debug(fmt.Sprintf(logging.FeatureNotEnabledForUserReturningDefault.String(), featureKey, userContext.ID, variable.DefaultValue)) } + } else { + o.logger.Debug(fmt.Sprintf(logging.ReturningDefaultValue.String(), userContext.ID, variableKey, featureKey)) } return variable.DefaultValue, variable.Type, &featureDecision, nil @@ -411,6 +418,13 @@ func (o *OptimizelyClient) GetAllFeatureVariablesWithDecision(featureKey string, if featureDecision.Variation != nil { enabled = featureDecision.Variation.FeatureEnabled + if enabled { + o.logger.Debug(fmt.Sprintf(logging.FeatureEnabledForUser.String(), featureKey, userContext.ID)) + } else { + o.logger.Debug(fmt.Sprintf(logging.FeatureNotEnabledForUser.String(), featureKey, userContext.ID)) + } + } else { + o.logger.Debug(fmt.Sprintf(logging.ReturningAllDefaultValue.String(), userContext.ID, featureKey)) } feature := decisionContext.Feature @@ -427,6 +441,7 @@ func (o *OptimizelyClient) GetAllFeatureVariablesWithDecision(featureKey string, if enabled { if variable, ok := featureDecision.Variation.Variables[v.ID]; ok { val = variable.Value + o.logger.Debug(fmt.Sprintf(logging.VariableValueForFeatureFlag.String(), val, v.Key, featureKey)) } } diff --git a/pkg/decision/bucketer/experiment_bucketer.go b/pkg/decision/bucketer/experiment_bucketer.go index 2a380efa0..12d363d35 100644 --- a/pkg/decision/bucketer/experiment_bucketer.go +++ b/pkg/decision/bucketer/experiment_bucketer.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019, Optimizely, Inc. and contributors * + * Copyright 2019-2020, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -18,6 +18,8 @@ package bucketer import ( + "fmt" + "github.com/optimizely/go-sdk/pkg/decision/reasons" "github.com/optimizely/go-sdk/pkg/entities" "github.com/optimizely/go-sdk/pkg/logging" @@ -31,28 +33,34 @@ type ExperimentBucketer interface { // MurmurhashExperimentBucketer buckets the user using the mmh3 algorightm type MurmurhashExperimentBucketer struct { bucketer Bucketer + logger logging.OptimizelyLogProducer } // NewMurmurhashExperimentBucketer returns a new instance of the murmurhash experiment bucketer func NewMurmurhashExperimentBucketer(logger logging.OptimizelyLogProducer, hashSeed uint32) *MurmurhashExperimentBucketer { return &MurmurhashExperimentBucketer{ - bucketer: MurmurhashBucketer{hashSeed: hashSeed, logger:logger}, + bucketer: MurmurhashBucketer{hashSeed: hashSeed, logger: logger}, + logger: logger, } } // Bucket buckets the user into the given experiment func (b MurmurhashExperimentBucketer) Bucket(bucketingID string, experiment entities.Experiment, group entities.Group) (*entities.Variation, reasons.Reason, error) { if experiment.GroupID != "" && group.Policy == "random" { - bucketKey := bucketingID + group.ID - bucketedExperimentID := b.bucketer.BucketToEntity(bucketKey, group.TrafficAllocation) - if bucketedExperimentID == "" || bucketedExperimentID != experiment.ID { - // User is not bucketed into provided experiment in mutex group + if bucketedExperimentID := b.bucketer.BucketToEntity(bucketingID, group.ID, group.TrafficAllocation); bucketedExperimentID != "" { + if bucketedExperimentID != experiment.ID { + // User is not bucketed into provided experiment in mutex group + b.logger.Debug(fmt.Sprintf(logging.UserNotBucketedIntoExperimentInGroup.String(), bucketingID, experiment.Key, group.ID)) + return nil, reasons.NotBucketedIntoVariation, nil + } + b.logger.Debug(fmt.Sprintf(logging.UserBucketedIntoExperimentInGroup.String(), bucketingID, experiment.Key, group.ID)) + } else { + b.logger.Debug(fmt.Sprintf(logging.UserNotBucketedIntoAnyExperimentInGroup.String(), bucketingID, group.ID)) return nil, reasons.NotBucketedIntoVariation, nil } } - bucketKey := bucketingID + experiment.ID - bucketedVariationID := b.bucketer.BucketToEntity(bucketKey, experiment.TrafficAllocation) + bucketedVariationID := b.bucketer.BucketToEntity(bucketingID, experiment.ID, experiment.TrafficAllocation) if bucketedVariationID == "" { // User is not bucketed into a variation in the experiment, return nil variation return nil, reasons.NotBucketedIntoVariation, nil diff --git a/pkg/decision/bucketer/experiment_bucketer_test.go b/pkg/decision/bucketer/experiment_bucketer_test.go index 2d3ebec8c..8e8d83865 100644 --- a/pkg/decision/bucketer/experiment_bucketer_test.go +++ b/pkg/decision/bucketer/experiment_bucketer_test.go @@ -1,26 +1,66 @@ +/**************************************************************************** + * Copyright 2019-2020, Optimizely, Inc. and contributors * + * * + * 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 bucketer // package bucketer import ( - "github.com/optimizely/go-sdk/pkg/logging" + "fmt" "testing" "github.com/optimizely/go-sdk/pkg/decision/reasons" + "github.com/optimizely/go-sdk/pkg/logging" "github.com/optimizely/go-sdk/pkg/entities" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) +type MockLogger struct { + mock.Mock +} + +func (m *MockLogger) Debug(message string) { + m.Called(message) +} + +func (m *MockLogger) Info(message string) { + m.Called(message) +} + +func (m *MockLogger) Warning(message string) { + m.Called(message) +} + +func (m *MockLogger) Error(message string, err interface{}) { + m.Called(message, err) +} + func TestBucketExclusionGroups(t *testing.T) { + mockLogger := MockLogger{} experiment1 := entities.Experiment{ ID: "1886780721", Key: "experiment_1", Variations: map[string]entities.Variation{ - "22222": entities.Variation{ID: "22222", Key: "exp_1_var_1"}, - "22223": entities.Variation{ID: "22223", Key: "exp_1_var_2"}, + "22222": {ID: "22222", Key: "exp_1_var_1"}, + "22223": {ID: "22223", Key: "exp_1_var_2"}, }, TrafficAllocation: []entities.Range{ - entities.Range{EntityID: "22222", EndOfRange: 4999}, - entities.Range{EntityID: "22223", EndOfRange: 10000}, + {EntityID: "22222", EndOfRange: 4999}, + {EntityID: "22223", EndOfRange: 10000}, }, GroupID: "1886780722", } @@ -28,12 +68,12 @@ func TestBucketExclusionGroups(t *testing.T) { ID: "1886780723", Key: "experiment_2", Variations: map[string]entities.Variation{ - "22224": entities.Variation{ID: "22224", Key: "exp_2_var_1"}, - "22225": entities.Variation{ID: "22225", Key: "exp_2_var_2"}, + "22224": {ID: "22224", Key: "exp_2_var_1"}, + "22225": {ID: "22225", Key: "exp_2_var_2"}, }, TrafficAllocation: []entities.Range{ - entities.Range{EntityID: "22224", EndOfRange: 4999}, - entities.Range{EntityID: "22225", EndOfRange: 10000}, + {EntityID: "22224", EndOfRange: 4999}, + {EntityID: "22225", EndOfRange: 10000}, }, GroupID: "1886780722", } @@ -42,13 +82,18 @@ func TestBucketExclusionGroups(t *testing.T) { ID: "1886780722", Policy: "random", TrafficAllocation: []entities.Range{ - entities.Range{EntityID: "1886780721", EndOfRange: 2500}, - entities.Range{EntityID: "1886780723", EndOfRange: 5000}, + {EntityID: "1886780721", EndOfRange: 2500}, + {EntityID: "1886780723", EndOfRange: 5000}, }, } - bucketer := NewMurmurhashExperimentBucketer(logging.GetLogger("","TestBucketExclusionGroups" ), DefaultHashSeed) + bucketer := NewMurmurhashExperimentBucketer(&mockLogger, DefaultHashSeed) // ppid2 + 1886780722 (groupId) will generate bucket value of 2434 which maps to experiment 1 + mockLogger.On("Debug", fmt.Sprintf(logging.UserAssignedToBucketValue.String(), 2434, "ppid2")) + mockLogger.On("Debug", fmt.Sprintf(logging.UserBucketedIntoExperimentInGroup.String(), "ppid2", "experiment_1", "1886780722")) + mockLogger.On("Debug", fmt.Sprintf(logging.UserAssignedToBucketValue.String(), 4299, "ppid2")) + mockLogger.On("Debug", fmt.Sprintf(logging.UserNotBucketedIntoExperimentInGroup.String(), "ppid2", "experiment_2", "1886780722")) + bucketedVariation, reason, _ := bucketer.Bucket("ppid2", experiment1, exclusionGroup) assert.Equal(t, experiment1.Variations["22222"], *bucketedVariation) assert.Equal(t, reasons.BucketedIntoVariation, reason) diff --git a/pkg/decision/bucketer/murmurhashbucketer.go b/pkg/decision/bucketer/murmurhashbucketer.go index a9eb25ef2..508c3464c 100644 --- a/pkg/decision/bucketer/murmurhashbucketer.go +++ b/pkg/decision/bucketer/murmurhashbucketer.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019, Optimizely, Inc. and contributors * + * Copyright 2019-2020, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -36,7 +36,7 @@ const maxTrafficValue = 10000 // Bucketer is used to generate bucket value using bucketing key type Bucketer interface { Generate(bucketingKey string) int - BucketToEntity(bucketKey string, trafficAllocations []entities.Range) (entityID string) + BucketToEntity(bucketingID, entityID string, trafficAllocations []entities.Range) string } // MurmurhashBucketer generates the bucketing value using the mmh3 algorightm @@ -49,7 +49,7 @@ type MurmurhashBucketer struct { func NewMurmurhashBucketer(logger logging.OptimizelyLogProducer, hashSeed uint32) *MurmurhashBucketer { return &MurmurhashBucketer{ hashSeed: hashSeed, - logger: logger, + logger: logger, } } @@ -64,10 +64,10 @@ func (b MurmurhashBucketer) Generate(bucketingKey string) int { return int(ratio * maxTrafficValue) } -// BucketToEntity buckets into a traffic against given bucketKey -func (b MurmurhashBucketer) BucketToEntity(bucketKey string, trafficAllocations []entities.Range) (entityID string) { - bucketValue := b.Generate(bucketKey) - +// BucketToEntity buckets into a traffic against given bucketingId and entityID +func (b MurmurhashBucketer) BucketToEntity(bucketingID, entityID string, trafficAllocations []entities.Range) string { + bucketValue := b.Generate(bucketingID + entityID) + b.logger.Debug(fmt.Sprintf(logging.UserAssignedToBucketValue.String(), bucketValue, bucketingID)) var currentEndOfRange int for _, trafficAllocationRange := range trafficAllocations { currentEndOfRange = trafficAllocationRange.EndOfRange diff --git a/pkg/decision/bucketer/murmurhashbucketer_test.go b/pkg/decision/bucketer/murmurhashbucketer_test.go index 3db47c552..d0542d511 100644 --- a/pkg/decision/bucketer/murmurhashbucketer_test.go +++ b/pkg/decision/bucketer/murmurhashbucketer_test.go @@ -1,10 +1,28 @@ +/**************************************************************************** + * Copyright 2019-2020, Optimizely, Inc. and contributors * + * * + * 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 bucketer // package bucketer import ( "fmt" - "github.com/optimizely/go-sdk/pkg/logging" "testing" + "github.com/optimizely/go-sdk/pkg/logging" + "github.com/optimizely/go-sdk/pkg/entities" "github.com/stretchr/testify/assert" ) @@ -16,13 +34,11 @@ func TestBucketToEntity(t *testing.T) { experimentID2 := "1886780722" // bucket value 5254 - bucketingKey1 := fmt.Sprintf("%s%s", "ppid1", experimentID) + bucketingID1 := "ppid1" // bucket value 4299 - bucketingKey2 := fmt.Sprintf("%s%s", "ppid2", experimentID) + bucketingID2 := "ppid2" // bucket value 2434 - bucketingKey3 := fmt.Sprintf("%s%s", "ppid2", experimentID2) - // bucket value 5439 - bucketingKey4 := fmt.Sprintf("%s%s", "ppid3", experimentID) + bucketingID3 := "ppid3" variation1 := "1234567123" variation2 := "5949300123" @@ -41,14 +57,14 @@ func TestBucketToEntity(t *testing.T) { }, } - assert.Equal(t, variation2, bucketer.BucketToEntity(bucketingKey1, trafficAlloc)) - assert.Equal(t, variation1, bucketer.BucketToEntity(bucketingKey2, trafficAlloc)) + assert.Equal(t, variation2, bucketer.BucketToEntity(bucketingID1, experimentID, trafficAlloc)) + assert.Equal(t, variation1, bucketer.BucketToEntity(bucketingID2, experimentID, trafficAlloc)) // bucket to empty variation range - assert.Equal(t, "", bucketer.BucketToEntity(bucketingKey3, trafficAlloc)) + assert.Equal(t, "", bucketer.BucketToEntity(bucketingID2, experimentID2, trafficAlloc)) // bucket outside of range (not in experiment) - assert.Equal(t, "", bucketer.BucketToEntity(bucketingKey4, trafficAlloc)) + assert.Equal(t, "", bucketer.BucketToEntity(bucketingID3, experimentID, trafficAlloc)) } func TestGenerateBucketValue(t *testing.T) { diff --git a/pkg/decision/evaluator/condition_test.go b/pkg/decision/evaluator/condition_test.go index 0cce66ccf..8b5e6c59b 100644 --- a/pkg/decision/evaluator/condition_test.go +++ b/pkg/decision/evaluator/condition_test.go @@ -136,7 +136,7 @@ func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorWithUnknownTyp s.mockLogger.AssertExpectations(s.T()) } -func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemver() { +func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemver() { conditionEvaluator := CustomAttributeConditionEvaluator{} condition := entities.Condition{ Match: "semver_ge", @@ -154,10 +154,10 @@ func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemver() condTreeParams := entities.NewTreeParameters(&user, map[string]entities.Audience{}) result, _ := conditionEvaluator.Evaluate(condition, condTreeParams) - s.Equal( result, true) + s.Equal(result, true) } -func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemverBeta() { +func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemverBeta() { conditionEvaluator := CustomAttributeConditionEvaluator{} condition := entities.Condition{ Match: "semver_ge", @@ -178,7 +178,7 @@ func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemverBe s.Equal(true, result) } -func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemverInvalid() { +func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorForGeSemverInvalid() { conditionEvaluator := CustomAttributeConditionEvaluator{} condition := entities.Condition{ Match: "semver_ge", diff --git a/pkg/decision/evaluator/matchers/lt_test.go b/pkg/decision/evaluator/matchers/lt_test.go index e3a76d12a..50496b73a 100644 --- a/pkg/decision/evaluator/matchers/lt_test.go +++ b/pkg/decision/evaluator/matchers/lt_test.go @@ -50,7 +50,6 @@ func (s *LtTestSuite) TestLtMatcherInt() { "int_42": 41, }, } - result, err := s.matcher(condition, user, s.mockLogger) s.NoError(err) s.True(result) @@ -125,7 +124,6 @@ func (s *LtTestSuite) TestLtMatcherFloat() { "float_4_2": 4, }, } - result, err := s.matcher(condition, user, s.mockLogger) s.NoError(err) s.True(result) @@ -136,7 +134,6 @@ func (s *LtTestSuite) TestLtMatcherFloat() { "float_4_2": 4.19999, }, } - result, err = s.matcher(condition, user, s.mockLogger) s.NoError(err) s.True(result) diff --git a/pkg/decision/experiment_bucketer_service.go b/pkg/decision/experiment_bucketer_service.go index a06d71514..8c923afa3 100644 --- a/pkg/decision/experiment_bucketer_service.go +++ b/pkg/decision/experiment_bucketer_service.go @@ -80,6 +80,11 @@ func (s ExperimentBucketerService) GetDecision(decisionContext ExperimentDecisio } // @TODO: handle error from bucketer variation, reason, _ := s.bucketer.Bucket(bucketingID, *experiment, group) + if variation != nil { + s.logger.Debug(fmt.Sprintf(logging.UserBucketedIntoVariationInExperiment.String(), userContext.ID, variation.Key, experiment.Key)) + } else { + s.logger.Debug(fmt.Sprintf(logging.UserNotBucketedIntoVariation.String(), userContext.ID)) + } experimentDecision.Reason = reason experimentDecision.Variation = variation return experimentDecision, nil diff --git a/pkg/decision/experiment_bucketer_service_test.go b/pkg/decision/experiment_bucketer_service_test.go index 88383ac80..286103dd9 100644 --- a/pkg/decision/experiment_bucketer_service_test.go +++ b/pkg/decision/experiment_bucketer_service_test.go @@ -88,6 +88,8 @@ func (s *ExperimentBucketerTestSuite) TestGetDecisionNoTargeting() { } s.mockBucketer.On("Bucket", testUserContext.ID, testExp1111, entities.Group{}).Return(&testExp1111Var2222, reasons.BucketedIntoVariation, nil) s.mockLogger.On("Debug", fmt.Sprintf(logging.ExperimentAudiencesEvaluatedTo.String(), "test_experiment_1111", true)) + s.mockLogger.On("Debug", fmt.Sprintf(logging.UserBucketedIntoVariationInExperiment.String(), "test_user_1", "2222", "test_experiment_1111")) + experimentBucketerService := ExperimentBucketerService{ bucketer: s.mockBucketer, logger: s.mockLogger, @@ -121,6 +123,7 @@ func (s *ExperimentBucketerTestSuite) TestGetDecisionWithTargetingPasses() { s.mockConfig.On("GetAudienceMap").Return(map[string]entities.Audience{}) s.mockLogger.On("Debug", fmt.Sprintf(logging.EvaluatingAudiencesForExperiment.String(), "test_targeted_experiment_1116")) s.mockLogger.On("Debug", fmt.Sprintf(logging.ExperimentAudiencesEvaluatedTo.String(), "test_targeted_experiment_1116", true)) + s.mockLogger.On("Debug", fmt.Sprintf(logging.UserBucketedIntoVariationInExperiment.String(), "test_user_1", "2228", "test_targeted_experiment_1116")) testDecisionContext := ExperimentDecisionContext{ Experiment: &testTargetedExp1116, diff --git a/pkg/decision/feature_experiment_service.go b/pkg/decision/feature_experiment_service.go index 8890c10e7..d4f9c35e5 100644 --- a/pkg/decision/feature_experiment_service.go +++ b/pkg/decision/feature_experiment_service.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019, Optimizely, Inc. and contributors * + * Copyright 2019-2020, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -18,8 +18,6 @@ package decision import ( - "fmt" - "github.com/optimizely/go-sdk/pkg/entities" "github.com/optimizely/go-sdk/pkg/logging" ) @@ -33,7 +31,7 @@ type FeatureExperimentService struct { // NewFeatureExperimentService returns a new instance of the FeatureExperimentService func NewFeatureExperimentService(logger logging.OptimizelyLogProducer, compositeExperimentService ExperimentService) *FeatureExperimentService { return &FeatureExperimentService{ - logger: logger, + logger: logger, compositeExperimentService: compositeExperimentService, } } @@ -50,12 +48,6 @@ func (f FeatureExperimentService) GetDecision(decisionContext FeatureDecisionCon } experimentDecision, err := f.compositeExperimentService.GetDecision(experimentDecisionContext, userContext) - f.logger.Debug(fmt.Sprintf( - `Decision made for feature test with key "%s" for user "%s" with the following reason: "%s".`, - feature.Key, - userContext.ID, - experimentDecision.Reason, - )) // Variation not nil means we got a decision and should return it if experimentDecision.Variation != nil { diff --git a/pkg/logging/log_messages.go b/pkg/logging/log_messages.go index f083fdda8..a59653f3b 100644 --- a/pkg/logging/log_messages.go +++ b/pkg/logging/log_messages.go @@ -31,6 +31,7 @@ const ( AudienceEvaluationStarted LogMessage = `Starting to evaluate audience "%s".` // AudienceEvaluatedTo when single audience evaluation is completed AudienceEvaluatedTo LogMessage = `Audience "%s" evaluated to %t.` + // ExperimentAudiencesEvaluatedTo when collective audience evaluation for experiment is completed ExperimentAudiencesEvaluatedTo LogMessage = `Audiences for experiment %s collectively evaluated to %t.` // RolloutAudiencesEvaluatedTo when collective audience evaluation for rule is completed @@ -47,6 +48,30 @@ const ( UserNotInRollout LogMessage = `User "%s" does not meet conditions for targeting rule %s.` // UserNotInExperiment when user is not in experiment UserNotInExperiment LogMessage = `User "%s" does not meet conditions to be in experiment "%s".` + // UserBucketedIntoExperimentInGroup when user is bucketed to experiment group + UserBucketedIntoExperimentInGroup LogMessage = `User "%s" is in experiment "%s" of group "%s".` + // UserNotBucketedIntoExperimentInGroup when user is not bucketed to experiment group + UserNotBucketedIntoExperimentInGroup LogMessage = `User "%s" is not in experiment "%s" of group "%s".` + // UserNotBucketedIntoAnyExperimentInGroup when user is not bucketed to any experiment group + UserNotBucketedIntoAnyExperimentInGroup LogMessage = `User "%s" is not in any experiment of group "%s".` + // UserBucketedIntoVariationInExperiment when user is bucketed to a variation in experiment + UserBucketedIntoVariationInExperiment LogMessage = `User "%s" is in variation "%s" of experiment "%s"` + // UserNotBucketedIntoVariation when user not bucketed to a variation + UserNotBucketedIntoVariation LogMessage = `User "%s" is in no variation.` + // UserAssignedToBucketValue when user is assigned to a bucket value + UserAssignedToBucketValue LogMessage = `Assigned bucket "%d" to user with bucketing ID "%s".` + // VariableValueForFeatureFlag when got variable value for variable of feature flag + VariableValueForFeatureFlag LogMessage = `Got variable value "%s" for variable "%s" of feature flag "%s".` + // FeatureEnabledForUser when feature is enabled for user + FeatureEnabledForUser LogMessage = `Feature "%s" is enabled for user "%s".` + // FeatureNotEnabledForUser when feature is not enabled for user + FeatureNotEnabledForUser LogMessage = `Feature "%s" is not enabled for user "%s".` + // FeatureNotEnabledForUserReturningDefault when returning default value because feature is not enabled for user + FeatureNotEnabledForUserReturningDefault LogMessage = `Feature "%s" is not enabled for user "%s". Returning the default variable value "%s".` + // ReturningDefaultValue when user is not in variation or rollout rule + ReturningDefaultValue LogMessage = `User "%s" is not in any variation or rollout rule. Returning default value for variable "%s" of feature flag "%s".` + // ReturningAllDefaultValue when user is not in any variation or rollout rule + ReturningAllDefaultValue LogMessage = `User "%s" is not in any variation or rollout rule. Returning default value for all variables of feature flag "%s".` // Warning logs