5
5
from .base import StoreProvider
6
6
from .exceptions import ConfigurationStoreError
7
7
8
- logger = logging .getLogger (__name__ )
9
-
10
8
11
9
class FeatureFlags :
12
- def __init__ (self , store : StoreProvider ):
10
+ def __init__ (self , store : StoreProvider , logger = None ):
13
11
"""Evaluates whether feature flags should be enabled based on a given context.
14
12
15
13
It uses the provided store to fetch feature flag rules before evaluating them.
@@ -37,9 +35,12 @@ def __init__(self, store: StoreProvider):
37
35
Store to use to fetch feature flag schema configuration.
38
36
"""
39
37
self ._store = store
38
+ if logger == None :
39
+ self .logger = logging .getLogger (__name__ )
40
+ else :
41
+ self .logger = logger
40
42
41
- @staticmethod
42
- def _match_by_action (action : str , condition_value : Any , context_value : Any ) -> bool :
43
+ def _match_by_action (self , action : str , condition_value : Any , context_value : Any ) -> bool :
43
44
if not context_value :
44
45
return False
45
46
mapping_by_action = {
@@ -54,7 +55,7 @@ def _match_by_action(action: str, condition_value: Any, context_value: Any) -> b
54
55
func = mapping_by_action .get (action , lambda a , b : False )
55
56
return func (context_value , condition_value )
56
57
except Exception as exc :
57
- logger . debug (f"caught exception while matching action: action={ action } , exception={ str (exc )} " )
58
+ self . loggerdebug (f"caught exception while matching action: action={ action } , exception={ str (exc )} " )
58
59
return False
59
60
60
61
def _evaluate_conditions (
@@ -65,7 +66,7 @@ def _evaluate_conditions(
65
66
conditions = cast (List [Dict ], rule .get (schema .CONDITIONS_KEY ))
66
67
67
68
if not conditions :
68
- logger . debug (
69
+ self . loggerdebug (
69
70
f"rule did not match, no conditions to match, rule_name={ rule_name } , rule_value={ rule_match_value } , "
70
71
f"name={ feature_name } "
71
72
)
@@ -77,13 +78,13 @@ def _evaluate_conditions(
77
78
cond_value = condition .get (schema .CONDITION_VALUE )
78
79
79
80
if not self ._match_by_action (action = cond_action , condition_value = cond_value , context_value = context_value ):
80
- logger . debug (
81
+ self . loggerdebug (
81
82
f"rule did not match action, rule_name={ rule_name } , rule_value={ rule_match_value } , "
82
83
f"name={ feature_name } , context_value={ str (context_value )} "
83
84
)
84
85
return False # context doesn't match condition
85
86
86
- logger . debug (f"rule matched, rule_name={ rule_name } , rule_value={ rule_match_value } , name={ feature_name } " )
87
+ self . loggerdebug (f"rule matched, rule_name={ rule_name } , rule_value={ rule_match_value } , name={ feature_name } " )
87
88
return True
88
89
89
90
def _evaluate_rules (
@@ -94,12 +95,12 @@ def _evaluate_rules(
94
95
rule_match_value = rule .get (schema .RULE_MATCH_VALUE )
95
96
96
97
# Context might contain PII data; do not log its value
97
- logger . debug (f"Evaluating rule matching, rule={ rule_name } , feature={ feature_name } , default={ feat_default } " )
98
+ self . loggerdebug (f"Evaluating rule matching, rule={ rule_name } , feature={ feature_name } , default={ feat_default } " )
98
99
if self ._evaluate_conditions (rule_name = rule_name , feature_name = feature_name , rule = rule , context = context ):
99
100
return bool (rule_match_value )
100
101
101
102
# no rule matched, return default value of feature
102
- logger . debug (f"no rule matched, returning feature default, default={ feat_default } , name={ feature_name } " )
103
+ self . loggerdebug (f"no rule matched, returning feature default, default={ feat_default } , name={ feature_name } " )
103
104
return feat_default
104
105
return False
105
106
@@ -146,7 +147,7 @@ def get_configuration(self) -> Union[Dict[str, Dict], Dict]:
146
147
```
147
148
"""
148
149
# parse result conf as JSON, keep in cache for max age defined in store
149
- logger . debug (f"Fetching schema from registered store, store={ self ._store } " )
150
+ self . loggerdebug (f"Fetching schema from registered store, store={ self ._store } " )
150
151
config = self ._store .get_configuration ()
151
152
validator = schema .SchemaValidator (schema = config )
152
153
validator .validate ()
@@ -190,21 +191,21 @@ def evaluate(self, *, name: str, context: Optional[Dict[str, Any]] = None, defau
190
191
try :
191
192
features = self .get_configuration ()
192
193
except ConfigurationStoreError as err :
193
- logger . debug (f"Failed to fetch feature flags from store, returning default provided, reason={ err } " )
194
+ self . loggerdebug (f"Failed to fetch feature flags from store, returning default provided, reason={ err } " )
194
195
return default
195
196
196
197
feature = features .get (name )
197
198
if feature is None :
198
- logger . debug (f"Feature not found; returning default provided, name={ name } , default={ default } " )
199
+ self . loggerdebug (f"Feature not found; returning default provided, name={ name } , default={ default } " )
199
200
return default
200
201
201
202
rules = feature .get (schema .RULES_KEY )
202
203
feat_default = feature .get (schema .FEATURE_DEFAULT_VAL_KEY )
203
204
if not rules :
204
- logger . debug (f"no rules found, returning feature default, name={ name } , default={ feat_default } " )
205
+ self . loggerdebug (f"no rules found, returning feature default, name={ name } , default={ feat_default } " )
205
206
return bool (feat_default )
206
207
207
- logger . debug (f"looking for rule match, name={ name } , default={ feat_default } " )
208
+ self . loggerdebug (f"looking for rule match, name={ name } , default={ feat_default } " )
208
209
return self ._evaluate_rules (feature_name = name , context = context , feat_default = bool (feat_default ), rules = rules )
209
210
210
211
def get_enabled_features (self , * , context : Optional [Dict [str , Any ]] = None ) -> List [str ]:
@@ -241,20 +242,20 @@ def get_enabled_features(self, *, context: Optional[Dict[str, Any]] = None) -> L
241
242
try :
242
243
features : Dict [str , Any ] = self .get_configuration ()
243
244
except ConfigurationStoreError as err :
244
- logger . debug (f"Failed to fetch feature flags from store, returning empty list, reason={ err } " )
245
+ self . loggerdebug (f"Failed to fetch feature flags from store, returning empty list, reason={ err } " )
245
246
return features_enabled
246
247
247
- logger . debug ("Evaluating all features" )
248
+ self . loggerdebug ("Evaluating all features" )
248
249
for name , feature in features .items ():
249
250
rules = feature .get (schema .RULES_KEY , {})
250
251
feature_default_value = feature .get (schema .FEATURE_DEFAULT_VAL_KEY )
251
252
if feature_default_value and not rules :
252
- logger . debug (f"feature is enabled by default and has no defined rules, name={ name } " )
253
+ self . loggerdebug (f"feature is enabled by default and has no defined rules, name={ name } " )
253
254
features_enabled .append (name )
254
255
elif self ._evaluate_rules (
255
256
feature_name = name , context = context , feat_default = feature_default_value , rules = rules
256
257
):
257
- logger . debug (f"feature's calculated value is True, name={ name } " )
258
+ self . loggerdebug (f"feature's calculated value is True, name={ name } " )
258
259
features_enabled .append (name )
259
260
260
261
return features_enabled
0 commit comments