1212from ldclient .feature_store import _FeatureStoreDataSetSorter
1313from ldclient .flag import EvaluationDetail , evaluate , error_reason
1414from ldclient .flags_state import FeatureFlagsState
15+ from ldclient .impl .event_factory import _EventFactory
1516from ldclient .impl .stubs import NullEventProcessor , NullUpdateProcessor
1617from ldclient .interfaces import FeatureStore
1718from ldclient .polling import PollingUpdateProcessor
@@ -90,6 +91,8 @@ def __init__(self, sdk_key=None, config=None, start_wait=5):
9091
9192 self ._event_processor = None
9293 self ._lock = Lock ()
94+ self ._event_factory_default = _EventFactory (False )
95+ self ._event_factory_with_reasons = _EventFactory (True )
9396
9497 self ._store = _FeatureStoreClientWrapper (self ._config .feature_store )
9598 """ :type: FeatureStore """
@@ -168,7 +171,7 @@ def __exit__(self, type, value, traceback):
168171 def _send_event (self , event ):
169172 self ._event_processor .send_event (event )
170173
171- def track (self , event_name , user , data = None ):
174+ def track (self , event_name , user , data = None , metric_value = None ):
172175 """Tracks that a user performed an event.
173176
174177 LaunchDarkly automatically tracks pageviews and clicks that are specified in the Goals
@@ -178,11 +181,14 @@ def track(self, event_name, user, data=None):
178181 :param string event_name: the name of the event, which may correspond to a goal in A/B tests
179182 :param dict user: the attributes of the user
180183 :param data: optional additional data associated with the event
184+ :param metric_value: a numeric value used by the LaunchDarkly experimentation feature in
185+ numeric custom metrics. Can be omitted if this event is used by only non-numeric metrics.
186+ This field will also be returned as part of the custom event for Data Export.
181187 """
182188 if user is None or user .get ('key' ) is None :
183189 log .warning ("Missing user or user key when calling track()." )
184190 else :
185- self ._send_event ({ 'kind' : 'custom' , 'key' : event_name , ' user' : user , ' data' : data } )
191+ self ._send_event (self . _event_factory_default . new_custom_event ( event_name , user , data , metric_value ) )
186192
187193 def identify (self , user ):
188194 """Registers the user.
@@ -196,7 +202,7 @@ def identify(self, user):
196202 if user is None or user .get ('key' ) is None :
197203 log .warning ("Missing user or user key when calling identify()." )
198204 else :
199- self ._send_event ({ 'kind' : 'identify' , 'key' : str ( user . get ( 'key' )), ' user' : user } )
205+ self ._send_event (self . _event_factory_default . new_identify_event ( user ) )
200206
201207 def is_offline (self ):
202208 """Returns true if the client is in offline mode.
@@ -246,7 +252,7 @@ def variation(self, key, user, default):
246252 available from LaunchDarkly
247253 :return: one of the flag's variation values, or the default value
248254 """
249- return self ._evaluate_internal (key , user , default , False ).value
255+ return self ._evaluate_internal (key , user , default , self . _event_factory_default ).value
250256
251257 def variation_detail (self , key , user , default ):
252258 """Determines the variation of a feature flag for a user, like :func:`variation()`, but also
@@ -263,30 +269,22 @@ def variation_detail(self, key, user, default):
263269 :return: an object describing the result
264270 :rtype: EvaluationDetail
265271 """
266- return self ._evaluate_internal (key , user , default , True )
272+ return self ._evaluate_internal (key , user , default , self . _event_factory_with_reasons )
267273
268- def _evaluate_internal (self , key , user , default , include_reasons_in_events ):
274+ def _evaluate_internal (self , key , user , default , event_factory ):
269275 default = self ._config .get_default (key , default )
270276
271277 if self ._config .offline :
272278 return EvaluationDetail (default , None , error_reason ('CLIENT_NOT_READY' ))
273279
274- def send_event (value , variation = None , flag = None , reason = None ):
275- self ._send_event ({'kind' : 'feature' , 'key' : key , 'user' : user ,
276- 'value' : value , 'variation' : variation , 'default' : default ,
277- 'version' : flag .get ('version' ) if flag else None ,
278- 'trackEvents' : flag .get ('trackEvents' ) if flag else None ,
279- 'debugEventsUntilDate' : flag .get ('debugEventsUntilDate' ) if flag else None ,
280- 'reason' : reason if include_reasons_in_events else None })
281-
282280 if not self .is_initialized ():
283281 if self ._store .initialized :
284282 log .warning ("Feature Flag evaluation attempted before client has initialized - using last known values from feature store for feature key: " + key )
285283 else :
286284 log .warning ("Feature Flag evaluation attempted before client has initialized! Feature store unavailable - returning default: "
287285 + str (default ) + " for feature key: " + key )
288286 reason = error_reason ('CLIENT_NOT_READY' )
289- send_event ( default , None , None , reason )
287+ self . _send_event ( event_factory . new_unknown_flag_event ( key , user , default , reason ) )
290288 return EvaluationDetail (default , None , reason )
291289
292290 if user is not None and user .get ('key' , "" ) == "" :
@@ -298,32 +296,32 @@ def send_event(value, variation=None, flag=None, reason=None):
298296 log .error ("Unexpected error while retrieving feature flag \" %s\" : %s" % (key , repr (e )))
299297 log .debug (traceback .format_exc ())
300298 reason = error_reason ('EXCEPTION' )
301- send_event ( default , None , None , reason )
299+ self . _send_event ( event_factory . new_unknown_flag_event ( key , user , default , reason ) )
302300 return EvaluationDetail (default , None , reason )
303301 if not flag :
304302 reason = error_reason ('FLAG_NOT_FOUND' )
305- send_event ( default , None , None , reason )
303+ self . _send_event ( event_factory . new_unknown_flag_event ( key , user , default , reason ) )
306304 return EvaluationDetail (default , None , reason )
307305 else :
308306 if user is None or user .get ('key' ) is None :
309307 reason = error_reason ('USER_NOT_SPECIFIED' )
310- send_event ( default , None , flag , reason )
308+ self . _send_event ( event_factory . new_default_event ( flag , user , default , reason ) )
311309 return EvaluationDetail (default , None , reason )
312310
313311 try :
314- result = evaluate (flag , user , self ._store , include_reasons_in_events )
312+ result = evaluate (flag , user , self ._store , event_factory )
315313 for event in result .events or []:
316314 self ._send_event (event )
317315 detail = result .detail
318316 if detail .is_default_value ():
319317 detail = EvaluationDetail (default , None , detail .reason )
320- send_event ( detail . value , detail . variation_index , flag , detail . reason )
318+ self . _send_event ( event_factory . new_eval_event ( flag , user , detail , default ) )
321319 return detail
322320 except Exception as e :
323321 log .error ("Unexpected error while evaluating feature flag \" %s\" : %s" % (key , repr (e )))
324322 log .debug (traceback .format_exc ())
325323 reason = error_reason ('EXCEPTION' )
326- send_event ( default , None , flag , reason )
324+ self . _send_event ( event_factory . new_default_event ( flag , user , default , reason ) )
327325 return EvaluationDetail (default , None , reason )
328326
329327 def all_flags (self , user ):
0 commit comments