Skip to content

Commit 28097f0

Browse files
mnoman09thomaszurkan-optimizely
authored andcommitted
Feat(AudienceEvalLogging): Added logging in Audience evaluation and its unit tests (#251)
* Refact(AudienceLogging): Added logging in audience evaluation * Updated headers * changed missing attribute log level to debug from warn * potential bug fixes and added null test in isuserinexperiment tests * Refact and changed log message of missing attribute value * Code refact * Added matchType Error enum to get match type error and log with audience condition * Updated logging with latest changes in audience logging docs * Refactored code and edited some logs * nit changes * made expected logging inline * header update * requested changes - on behalf of Noman * tom commits
1 parent d034f10 commit 28097f0

File tree

14 files changed

+277
-57
lines changed

14 files changed

+277
-57
lines changed

core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,13 @@ public Boolean evaluate(ProjectConfig config, Map<String, ?> attributes) {
7171
audience = config.getAudienceIdMapping().get(audienceId);
7272
}
7373
if (audience == null) {
74-
logger.error(String.format("Audience not set for audienceConditions %s", audienceId));
74+
logger.error("Audience {} could not be found.", audienceId);
7575
return null;
7676
}
77-
return audience.getConditions().evaluate(config, attributes);
77+
logger.debug("Starting to evaluate audience {} with conditions: \"{}\"", audience.getName(), audience.getConditions());
78+
Boolean result = audience.getConditions().evaluate(config, attributes);
79+
logger.info("Audience {} evaluated to {}", audience.getName(), result);
80+
return result;
7881
}
7982

8083
@Override

core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2121
import com.fasterxml.jackson.annotation.JsonProperty;
2222
import com.optimizely.ab.config.ProjectConfig;
23+
import com.optimizely.ab.config.audience.match.Match;
2324
import com.optimizely.ab.config.audience.match.MatchType;
25+
import com.optimizely.ab.config.audience.match.UnexpectedValueTypeException;
26+
import com.optimizely.ab.config.audience.match.UnknownMatchTypeException;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
2429

2530
import javax.annotation.Nonnull;
2631
import javax.annotation.Nullable;
@@ -35,6 +40,7 @@
3540
@JsonIgnoreProperties(ignoreUnknown = true)
3641
public class UserAttribute<T> implements Condition<T> {
3742

43+
private static final Logger logger = LoggerFactory.getLogger(UserAttribute.class);
3844
private final String name;
3945
private final String type;
4046
private final String match;
@@ -76,16 +82,42 @@ public Boolean evaluate(ProjectConfig config, Map<String, ?> attributes) {
7682
Object userAttributeValue = attributes.get(name);
7783

7884
if (!"custom_attribute".equals(type)) {
79-
MatchType.logger.error(String.format("condition type not equal to `custom_attribute` %s", type));
85+
logger.warn("Audience condition \"{}\" has an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK", this);
8086
return null; // unknown type
8187
}
8288
// check user attribute value is equal
8389
try {
84-
return MatchType.getMatchType(match, value).getMatcher().eval(userAttributeValue);
90+
Match matchType = MatchType.getMatchType(match, value).getMatcher();
91+
Boolean result = matchType.eval(userAttributeValue);
92+
93+
if (result == null) {
94+
if (!attributes.containsKey(name)) {
95+
//Missing attribute value
96+
logger.debug("Audience condition \"{}\" evaluated to UNKNOWN because no value was passed for user attribute \"{}\"", this, name);
97+
} else {
98+
//if attribute value is not valid
99+
if (userAttributeValue != null) {
100+
logger.warn(
101+
"Audience condition \"{}\" evaluated to UNKNOWN because a value of type \"{}\" was passed for user attribute \"{}\"",
102+
this,
103+
userAttributeValue.getClass().getCanonicalName(),
104+
name);
105+
} else {
106+
logger.warn(
107+
"Audience condition \"{}\" evaluated to UNKNOWN because a null value was passed for user attribute \"{}\"",
108+
this,
109+
name);
110+
}
111+
}
112+
}
113+
return result;
114+
} catch (UnknownMatchTypeException | UnexpectedValueTypeException ex) {
115+
logger.warn("Audience condition \"{}\" " + ex.getMessage(),
116+
this);
85117
} catch (NullPointerException np) {
86-
MatchType.logger.error(String.format("attribute or value null for match %s", match != null ? match : "legacy condition"), np);
87-
return null;
118+
logger.error("attribute or value null for match {}", match != null ? match : "legacy condition", np);
88119
}
120+
return null;
89121
}
90122

91123
@Override

core-api/src/main/java/com/optimizely/ab/config/audience/match/AttributeMatch.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,13 @@ abstract class AttributeMatch<T> implements Match {
2020
T castToValueType(Object o, Object value) {
2121
try {
2222
if (!o.getClass().isInstance(value) && !(o instanceof Number && value instanceof Number)) {
23-
MatchType.logger.warn(
24-
String.format("Incompatible type %s, %s",
25-
o.getClass().getCanonicalName(), value.getClass().getCanonicalName())
26-
);
27-
2823
return null;
2924
}
3025

3126
T rv = (T) o;
3227

3328
return rv;
3429
} catch (Exception e) {
35-
MatchType.logger.error(
36-
"Cannot evaluate targeting condition since the value for attribute is an incompatible type",
37-
e
38-
);
3930
return null;
4031
}
4132
}

core-api/src/main/java/com/optimizely/ab/config/audience/match/ExactNumberMatch.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ public Boolean eval(Object attributeValue) {
3636
try {
3737
return value.doubleValue() == castToValueType(attributeValue, value).doubleValue();
3838
} catch (Exception e) {
39-
MatchType.logger.error("Exact number match failed ", e);
4039
}
4140

4241
return null;

core-api/src/main/java/com/optimizely/ab/config/audience/match/GTMatch.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public Boolean eval(Object attributeValue) {
3030
try {
3131
return castToValueType(attributeValue, value).doubleValue() > value.doubleValue();
3232
} catch (Exception e) {
33-
MatchType.logger.error("Greater than match failed ", e);
3433
return null;
3534
}
3635
}

core-api/src/main/java/com/optimizely/ab/config/audience/match/LTMatch.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public Boolean eval(Object attributeValue) {
3030
try {
3131
return castToValueType(attributeValue, value).doubleValue() < value.doubleValue();
3232
} catch (Exception e) {
33-
MatchType.logger.error("Less than match failed ", e);
3433
return null;
3534
}
3635
}

core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchType.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class MatchType {
2828
private String matchType;
2929
private Match matcher;
3030

31-
public static MatchType getMatchType(String matchType, Object conditionValue) {
31+
public static MatchType getMatchType(String matchType, Object conditionValue) throws UnexpectedValueTypeException, UnknownMatchTypeException {
3232
if (matchType == null) matchType = "legacy_custom_attribute";
3333

3434
switch (matchType) {
@@ -64,10 +64,10 @@ public static MatchType getMatchType(String matchType, Object conditionValue) {
6464
}
6565
break;
6666
default:
67-
return new MatchType(matchType, new NullMatch());
67+
throw new UnknownMatchTypeException();
6868
}
6969

70-
return new MatchType(matchType, new NullMatch());
70+
throw new UnexpectedValueTypeException();
7171
}
7272

7373
private static boolean isValidNumber(Object conditionValue) {

core-api/src/main/java/com/optimizely/ab/config/audience/match/SubstringMatch.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ public Boolean eval(Object attributeValue) {
3636
try {
3737
return castToValueType(attributeValue, value).contains(value);
3838
} catch (Exception e) {
39-
MatchType.logger.error("Substring match failed ", e);
4039
return null;
4140
}
4241
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2018-2019, Optimizely and contributors
3+
* Copyright 2019, Optimizely and contributors
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -14,22 +14,13 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package com.optimizely.ab.config.audience.match;
18-
19-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20-
21-
import javax.annotation.Nullable;
2217

23-
class NullMatch extends AttributeMatch<Object> {
24-
@SuppressFBWarnings("URF_UNREAD_FIELD")
25-
Object value;
18+
package com.optimizely.ab.config.audience.match;
2619

27-
protected NullMatch() {
28-
this.value = null;
29-
}
20+
public class UnexpectedValueTypeException extends Exception {
21+
private static String message = "has an unexpected value type. You may need to upgrade to a newer release of the Optimizely SDK";
3022

31-
@Nullable
32-
public Boolean eval(Object attributeValue) {
33-
return null;
23+
public UnexpectedValueTypeException() {
24+
super(message);
3425
}
3526
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
*
3+
* Copyright 2019, Optimizely and contributors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.optimizely.ab.config.audience.match;
19+
20+
public class UnknownMatchTypeException extends Exception {
21+
private static String message = "uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK";
22+
23+
public UnknownMatchTypeException() {
24+
super(message);
25+
}
26+
}

0 commit comments

Comments
 (0)