Skip to content

Commit ebc3c64

Browse files
authored
fix(logs): Fixing log messages for Targeted Rollouts (#515)
1 parent b1e4596 commit ebc3c64

File tree

7 files changed

+203
-231
lines changed

7 files changed

+203
-231
lines changed

packages/optimizely-sdk/lib/core/bucketer/index.js

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,36 +104,19 @@ export var bucket = function(bucketerParams) {
104104
var bucketValue = this._generateBucketValue(bucketingId);
105105

106106
var bucketedUserLogMessage = sprintf(
107-
LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET,
107+
LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET,
108108
MODULE_NAME,
109109
bucketValue,
110110
bucketerParams.userId
111111
);
112112
bucketerParams.logger.log(LOG_LEVEL.DEBUG, bucketedUserLogMessage);
113113

114114
var entityId = this._findBucket(bucketValue, bucketerParams.trafficAllocationConfig);
115-
if (!entityId) {
116-
var userHasNoVariationLogMessage = sprintf(
117-
LOG_MESSAGES.USER_HAS_NO_VARIATION,
118-
MODULE_NAME,
119-
bucketerParams.userId,
120-
bucketerParams.experimentKey
121-
);
122-
bucketerParams.logger.log(LOG_LEVEL.DEBUG, userHasNoVariationLogMessage);
123-
} else if (!bucketerParams.variationIdMap.hasOwnProperty(entityId)) {
115+
116+
if (!bucketerParams.variationIdMap.hasOwnProperty(entityId)) {
124117
var invalidVariationIdLogMessage = sprintf(LOG_MESSAGES.INVALID_VARIATION_ID, MODULE_NAME);
125118
bucketerParams.logger.log(LOG_LEVEL.WARNING, invalidVariationIdLogMessage);
126119
return null;
127-
} else {
128-
var variationKey = bucketerParams.variationIdMap[entityId].key;
129-
var userInVariationLogMessage = sprintf(
130-
LOG_MESSAGES.USER_HAS_VARIATION,
131-
MODULE_NAME,
132-
bucketerParams.userId,
133-
variationKey,
134-
bucketerParams.experimentKey
135-
);
136-
bucketerParams.logger.log(LOG_LEVEL.INFO, userInVariationLogMessage);
137120
}
138121

139122
return entityId;

packages/optimizely-sdk/lib/core/bucketer/index.tests.js

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -75,27 +75,18 @@ describe('lib/core/bucketer', function() {
7575
expect(bucketer.bucket(bucketerParamsTest1)).to.equal('111128');
7676

7777
var bucketedUser_log1 = createdLogger.log.args[0][1];
78-
var bucketedUser_log2 = createdLogger.log.args[1][1];
79-
8078
expect(bucketedUser_log1).to.equal(
81-
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50', 'ppid1')
82-
);
83-
expect(bucketedUser_log2).to.equal(
84-
sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'ppid1', 'control', 'testExperiment')
79+
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50', 'ppid1')
8580
);
8681

8782
var bucketerParamsTest2 = cloneDeep(bucketerParams);
8883
bucketerParamsTest2.userId = 'ppid2';
8984
expect(bucketer.bucket(bucketerParamsTest2)).to.equal(null);
9085

91-
var notBucketedUser_log1 = createdLogger.log.args[2][1];
92-
var notBucketedUser_log2 = createdLogger.log.args[3][1];
86+
var notBucketedUser_log1 = createdLogger.log.args[1][1];
9387

9488
expect(notBucketedUser_log1).to.equal(
95-
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50000', 'ppid2')
96-
);
97-
expect(notBucketedUser_log2).to.equal(
98-
sprintf(LOG_MESSAGES.USER_HAS_NO_VARIATION, 'BUCKETER', 'ppid2', 'testExperiment')
89+
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50000', 'ppid2')
9990
);
10091
});
10192
});
@@ -142,7 +133,7 @@ describe('lib/core/bucketer', function() {
142133
expect(bucketer.bucket(bucketerParams)).to.equal('551');
143134

144135
sinon.assert.calledTwice(bucketerStub);
145-
sinon.assert.callCount(createdLogger.log, 4);
136+
sinon.assert.callCount(createdLogger.log, 3);
146137

147138
var log1 = createdLogger.log.args[0][1];
148139
expect(log1).to.equal(
@@ -162,12 +153,7 @@ describe('lib/core/bucketer', function() {
162153

163154
var log3 = createdLogger.log.args[2][1];
164155
expect(log3).to.equal(
165-
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50', 'testUser')
166-
);
167-
168-
var log4 = createdLogger.log.args[3][1];
169-
expect(log4).to.equal(
170-
sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'testUser', 'var1exp1', 'groupExperiment1')
156+
sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50', 'testUser')
171157
);
172158
});
173159

@@ -258,20 +244,10 @@ describe('lib/core/bucketer', function() {
258244
expect(bucketer.bucket(bucketerParams)).to.equal('553');
259245

260246
sinon.assert.calledOnce(bucketerStub);
261-
sinon.assert.calledTwice(createdLogger.log);
247+
sinon.assert.calledOnce(createdLogger.log);
262248

263249
var log1 = createdLogger.log.args[0][1];
264-
expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '0', 'testUser'));
265-
var log2 = createdLogger.log.args[1][1];
266-
expect(log2).to.equal(
267-
sprintf(
268-
LOG_MESSAGES.USER_HAS_VARIATION,
269-
'BUCKETER',
270-
'testUser',
271-
'overlappingvar1',
272-
'overlappingGroupExperiment1'
273-
)
274-
);
250+
expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '0', 'testUser'));
275251
});
276252

277253
it('should return null when a user does not fall into an experiment within an overlapping group', function() {
@@ -308,7 +284,7 @@ describe('lib/core/bucketer', function() {
308284
it('should return null', function() {
309285
var bucketerParamsTest1 = cloneDeep(bucketerParams);
310286
bucketerParamsTest1.userId = 'ppid1';
311-
expect(bucketer.bucket(bucketerParamsTest1)).to.equal('');
287+
expect(bucketer.bucket(bucketerParamsTest1)).to.equal(null);
312288
});
313289
});
314290

packages/optimizely-sdk/lib/core/decision_service/index.js

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var ERROR_MESSAGES = enums.ERROR_MESSAGES;
2727
var LOG_LEVEL = enums.LOG_LEVEL;
2828
var LOG_MESSAGES = enums.LOG_MESSAGES;
2929
var DECISION_SOURCES = enums.DECISION_SOURCES;
30+
var AUDIENCE_EVALUATION_TYPES = enums.AUDIENCE_EVALUATION_TYPES;
3031

3132
/**
3233
* Optimizely's decision service that determines which variation of an experiment the user will be allocated to.
@@ -90,17 +91,39 @@ DecisionService.prototype.getVariation = function(configObj, experimentKey, user
9091
}
9192

9293
// Perform regular targeting and bucketing
93-
if (!this.__checkIfUserIsInAudience(configObj, experimentKey, userId, attributes)) {
94+
if (!this.__checkIfUserIsInAudience(configObj, experimentKey, AUDIENCE_EVALUATION_TYPES.EXPERIMENT, userId, attributes, '')) {
95+
var userDoesNotMeetConditionsLogMessage = sprintf(
96+
LOG_MESSAGES.USER_NOT_IN_EXPERIMENT,
97+
MODULE_NAME,
98+
userId,
99+
experimentKey
100+
);
101+
this.logger.log(LOG_LEVEL.INFO, userDoesNotMeetConditionsLogMessage);
94102
return null;
95103
}
96104

97105
var bucketerParams = this.__buildBucketerParams(configObj, experimentKey, bucketingId, userId);
98106
var variationId = bucketer.bucket(bucketerParams);
99107
variation = configObj.variationIdMap[variationId];
100108
if (!variation) {
109+
var userHasNoVariationLogMessage = sprintf(
110+
LOG_MESSAGES.USER_HAS_NO_VARIATION,
111+
MODULE_NAME,
112+
userId,
113+
experimentKey
114+
);
115+
this.logger.log(LOG_LEVEL.DEBUG, userHasNoVariationLogMessage);
101116
return null;
102117
}
103118

119+
var userInVariationLogMessage = sprintf(
120+
LOG_MESSAGES.USER_HAS_VARIATION,
121+
MODULE_NAME,
122+
userId,
123+
variation.key,
124+
experimentKey
125+
);
126+
this.logger.log(LOG_LEVEL.INFO, userInVariationLogMessage);
104127
// persist bucketing
105128
this.__saveUserProfile(experiment, variation, userId, experimentBucketMap);
106129

@@ -171,21 +194,24 @@ DecisionService.prototype.__getWhitelistedVariation = function(experiment, userI
171194

172195
/**
173196
* Checks whether the user is included in experiment audience
174-
* @param {Object} configObj The parsed project configuration object
175-
* @param {string} experimentKey Key of experiment being validated
176-
* @param {string} userId ID of user
177-
* @param {Object} attributes Optional parameter for user's attributes
197+
* @param {Object} configObj The parsed project configuration object
198+
* @param {string} experimentKey Key of experiment being validated
199+
* @param {string} evaluationAttribute String representing experiment key or rule
200+
* @param {string} userId ID of user
201+
* @param {Object} attributes Optional parameter for user's attributes
202+
* @param {string} loggingKey String representing experiment key or rollout rule. To be used in log messages only.
178203
* @return {boolean} True if user meets audience conditions
179204
*/
180-
DecisionService.prototype.__checkIfUserIsInAudience = function(configObj, experimentKey, userId, attributes) {
205+
DecisionService.prototype.__checkIfUserIsInAudience = function(configObj, experimentKey, evaluationAttribute, userId, attributes, loggingKey) {
181206
var experimentAudienceConditions = projectConfig.getExperimentAudienceConditions(configObj, experimentKey);
182207
var audiencesById = projectConfig.getAudiencesById(configObj);
183208
this.logger.log(
184209
LOG_LEVEL.DEBUG,
185210
sprintf(
186211
LOG_MESSAGES.EVALUATING_AUDIENCES_COMBINED,
187212
MODULE_NAME,
188-
experimentKey,
213+
evaluationAttribute,
214+
loggingKey || experimentKey,
189215
JSON.stringify(experimentAudienceConditions)
190216
)
191217
);
@@ -195,23 +221,13 @@ DecisionService.prototype.__checkIfUserIsInAudience = function(configObj, experi
195221
sprintf(
196222
LOG_MESSAGES.AUDIENCE_EVALUATION_RESULT_COMBINED,
197223
MODULE_NAME,
198-
experimentKey,
224+
evaluationAttribute,
225+
loggingKey || experimentKey,
199226
result.toString().toUpperCase()
200227
)
201228
);
202229

203-
if (!result) {
204-
var userDoesNotMeetConditionsLogMessage = sprintf(
205-
LOG_MESSAGES.USER_NOT_IN_EXPERIMENT,
206-
MODULE_NAME,
207-
userId,
208-
experimentKey
209-
);
210-
this.logger.log(LOG_LEVEL.INFO, userDoesNotMeetConditionsLogMessage);
211-
return false;
212-
}
213-
214-
return true;
230+
return result;
215231
};
216232

217233
/**
@@ -335,25 +351,9 @@ DecisionService.prototype.__saveUserProfile = function(experiment, variation, us
335351
DecisionService.prototype.getVariationForFeature = function(configObj, feature, userId, attributes) {
336352
var experimentDecision = this._getVariationForFeatureExperiment(configObj, feature, userId, attributes);
337353
if (experimentDecision.variation !== null) {
338-
this.logger.log(
339-
LOG_LEVEL.DEBUG,
340-
sprintf(
341-
LOG_MESSAGES.USER_IN_FEATURE_EXPERIMENT,
342-
MODULE_NAME,
343-
userId,
344-
experimentDecision.variation.key,
345-
experimentDecision.experiment.key,
346-
feature.key
347-
)
348-
);
349354
return experimentDecision;
350355
}
351356

352-
this.logger.log(
353-
LOG_LEVEL.DEBUG,
354-
sprintf(LOG_MESSAGES.USER_NOT_IN_FEATURE_EXPERIMENT, MODULE_NAME, userId, feature.key)
355-
);
356-
357357
var rolloutDecision = this._getVariationForRollout(configObj, feature, userId, attributes);
358358
if (rolloutDecision.variation !== null) {
359359
this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_IN_ROLLOUT, MODULE_NAME, userId, feature.key));
@@ -456,50 +456,56 @@ DecisionService.prototype._getVariationForRollout = function(configObj, feature,
456456
// "everyone else", which will be evaluated separately outside this loop
457457
var endIndex = rollout.experiments.length - 1;
458458
var index;
459-
var experiment;
459+
var rolloutRule;
460460
var bucketerParams;
461461
var variationId;
462462
var variation;
463+
var loggingKey;
463464
for (index = 0; index < endIndex; index++) {
464-
experiment = configObj.experimentKeyMap[rollout.experiments[index].key];
465+
rolloutRule = configObj.experimentKeyMap[rollout.experiments[index].key];
466+
loggingKey = index + 1;
465467

466-
if (!this.__checkIfUserIsInAudience(configObj, experiment.key, userId, attributes)) {
468+
if (!this.__checkIfUserIsInAudience(configObj, rolloutRule.key, AUDIENCE_EVALUATION_TYPES.RULE, userId, attributes, loggingKey)) {
467469
this.logger.log(
468470
LOG_LEVEL.DEBUG,
469-
sprintf(LOG_MESSAGES.USER_DOESNT_MEET_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, index + 1)
471+
sprintf(LOG_MESSAGES.USER_DOESNT_MEET_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, loggingKey)
470472
);
471473
continue;
472474
}
473475

474476
this.logger.log(
475477
LOG_LEVEL.DEBUG,
476-
sprintf(LOG_MESSAGES.USER_MEETS_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, index + 1)
478+
sprintf(LOG_MESSAGES.USER_MEETS_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, loggingKey)
477479
);
478-
bucketerParams = this.__buildBucketerParams(configObj, experiment.key, bucketingId, userId);
480+
bucketerParams = this.__buildBucketerParams(configObj, rolloutRule.key, bucketingId, userId);
479481
variationId = bucketer.bucket(bucketerParams);
480482
variation = configObj.variationIdMap[variationId];
481483
if (variation) {
482484
this.logger.log(
483485
LOG_LEVEL.DEBUG,
484-
sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, index + 1)
486+
sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, loggingKey)
485487
);
486488
return {
487-
experiment: experiment,
489+
experiment: rolloutRule,
488490
variation: variation,
489491
decisionSource: DECISION_SOURCES.ROLLOUT,
490492
};
491493
} else {
492494
this.logger.log(
493495
LOG_LEVEL.DEBUG,
494-
sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, index + 1)
496+
sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, loggingKey)
495497
);
496498
break;
497499
}
498500
}
499501

500-
var everyoneElseExperiment = configObj.experimentKeyMap[rollout.experiments[endIndex].key];
501-
if (this.__checkIfUserIsInAudience(configObj, everyoneElseExperiment.key, userId, attributes)) {
502-
bucketerParams = this.__buildBucketerParams(configObj, everyoneElseExperiment.key, bucketingId, userId);
502+
var everyoneElseRule = configObj.experimentKeyMap[rollout.experiments[endIndex].key];
503+
if (this.__checkIfUserIsInAudience(configObj, everyoneElseRule.key, AUDIENCE_EVALUATION_TYPES.RULE, userId, attributes, 'Everyone Else')) {
504+
this.logger.log(
505+
LOG_LEVEL.DEBUG,
506+
sprintf(LOG_MESSAGES.USER_MEETS_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, 'Everyone Else')
507+
);
508+
bucketerParams = this.__buildBucketerParams(configObj, everyoneElseRule.key, bucketingId, userId);
503509
variationId = bucketer.bucket(bucketerParams);
504510
variation = configObj.variationIdMap[variationId];
505511
if (variation) {
@@ -508,7 +514,7 @@ DecisionService.prototype._getVariationForRollout = function(configObj, feature,
508514
sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EVERYONE_TARGETING_RULE, MODULE_NAME, userId)
509515
);
510516
return {
511-
experiment: everyoneElseExperiment,
517+
experiment: everyoneElseRule,
512518
variation: variation,
513519
decisionSource: DECISION_SOURCES.ROLLOUT,
514520
};

0 commit comments

Comments
 (0)