def redis_get():
            r = None
            try:
                r = yield self._get_connection()
                """ :type: RedisClient """
                get_result = yield r.hget(self._features_key, key)
                if not get_result:
                    log.warn("Didn't get response from redis for key: " + key +
                             " Returning None.")
                    defer.returnValue(None)
                f_json = get_result.get(key)
                if f_json is None or f_json is "":
                    log.warn(
                        "TwistedRedisFeatureStore: feature flag with key: " +
                        key + " not found in Redis. Returning None.")
                    defer.returnValue(None)

                f = json.loads(f_json.decode('utf-8'))
                if f.get('deleted', False) is True:
                    log.warn(
                        "TwistedRedisFeatureStore: get returned deleted flag from Redis. Returning None."
                    )
                    defer.returnValue(None)
                self._cache[key] = f
                defer.returnValue(f)
            except Exception as e:
                log.error("Could not connect to Redis using url: " +
                          self._url + " with error message: " + e.message)
                defer.returnValue(None)
            finally:
                if r:
                    r.quit()
            defer.returnValue(None)
示例#2
0
    def run(self):
        if not self._running:
            log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval))
            self._running = True
            while self._running:
                start_time = time.time()
                try:
                    all_data = self._requester.get_all_data()
                    self._store.init(all_data)
                    if not self._ready.is_set() is True and self._store.initialized is True:
                        log.info("PollingUpdateProcessor initialized ok")
                        self._ready.set()
                except UnsuccessfulResponseException as e:
                    log.error(http_error_message(e.status, "polling request"))
                    if not is_http_error_recoverable(e.status):
                        self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited
                        self.stop()
                    break
                except Exception as e:
                    log.exception(
                        'Error: Exception encountered when updating flags. %s' % e)

                elapsed = time.time() - start_time
                if elapsed < self._config.poll_interval:
                    time.sleep(self._config.poll_interval - elapsed)
        def redis_get_all():
            r = None
            try:
                r = yield self._get_connection()
                """ :type: RedisClient """
                all_features = yield r.hgetall(self._features_key)
                if all_features is None or all_features is "":
                    log.warn(
                        "TwistedRedisFeatureStore: call to get all flags returned no results. Returning None."
                    )
                    defer.returnValue(None)

                results = {}
                for k, f_json in all_features.items() or {}:
                    f = json.loads(f_json.decode('utf-8'))
                    if 'deleted' in f and f['deleted'] is False:
                        results[f['key']] = f
                defer.returnValue(results)
            except Exception as e:
                log.error("Could not connect to Redis using url: " +
                          self._url + " with error message: " + e.message)
                defer.returnValue(None)
            finally:
                if r:
                    r.quit()
            defer.returnValue(None)
示例#4
0
 def run(self):
     log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri)
     self._running = True
     while self._running:
         try:
             messages = self._connect()
             for msg in messages:
                 if not self._running:
                     break
                 message_ok = self.process_message(self._store, self._requester, msg)
                 if message_ok is True and self._ready.is_set() is False:
                     log.info("StreamingUpdateProcessor initialized ok.")
                     self._ready.set()
         except HTTPError as e:
             log.error("Received unexpected status code %d for stream connection" % e.response.status_code)
             if e.response.status_code == 401:
                 log.error("Received 401 error, no further streaming connection will be made since SDK key is invalid")
                 self.stop()
                 break
             else:
                 log.warning("Restarting stream connection after one second.")
         except Exception:
             log.warning("Caught exception. Restarting stream connection after one second.",
                         exc_info=True)
         time.sleep(1)
示例#5
0
    def run(self):
        if not self._running:
            log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval))
            self._running = True
            while self._running:
                start_time = time.time()
                try:
                    all_data = self._requester.get_all_data()
                    self._store.init(all_data)
                    if not self._ready.is_set() is True and self._store.initialized is True:
                        log.info("PollingUpdateProcessor initialized ok")
                        self._ready.set()
                except HTTPError as e:
                    log.error('Received unexpected status code %d from polling request' % e.response.status_code)
                    if e.response.status_code == 401:
                        log.error('Received 401 error, no further polling requests will be made since SDK key is invalid')
                        self.stop()
                    break
                except:
                    log.exception(
                        'Error: Exception encountered when updating flags.')

                elapsed = time.time() - start_time
                if elapsed < self._config.poll_interval:
                    time.sleep(self._config.poll_interval - elapsed)
示例#6
0
 def _run_main_loop(self):
     log.info("Starting event processor")
     while True:
         try:
             message = self._inbox.get(block=True)
             if message.type == 'event':
                 self._process_event(message.param)
             elif message.type == 'flush':
                 self._trigger_flush()
             elif message.type == 'flush_users':
                 self._user_keys.clear()
             elif message.type == 'diagnostic':
                 self._send_and_reset_diagnostics()
             elif message.type == 'test_sync':
                 self._flush_workers.wait()
                 if self._diagnostic_accumulator is not None:
                     self._diagnostic_flush_workers.wait()
                 message.param.set()
             elif message.type == 'stop':
                 self._do_shutdown()
                 message.param.set()
                 return
         except Exception:
             log.error('Unhandled exception in event processor',
                       exc_info=True)
示例#7
0
 def run(self):
     log.info("Starting MobileStreamingUpdateProcessor connecting to uri: " + self._uri)
     self._running = True
     while self._running:
         try:
             init_start = time.time()
             messages = self._connect()
             for msg in messages:
                 if not self._running:
                     break
                 message_ok = self.process_message(self._store, self._requester, msg)
                 if message_ok is True and self._ready.is_set() is False:
                     log.info("MobileStreamingUpdateProcessor initialized ok.")
                     init_duration = int((time.time() - init_start) * 1000)
                     request_success.fire(request_type="ld:init", name="mobile", response_time=init_duration, response_length=0)
                     self._ready.set()
         except UnsuccessfulResponseException as e:
             log.error(http_error_message(e.status, "stream connection"))
             init_duration = int((time.time() - init_start) * 1000)
             request_failure.fire(request_type="ld:init", name="mobile", response_time=init_duration, response_length=0, exception=e)
             if not is_http_error_recoverable(e.status):
                 self._ready.set()  # if client is initializing, make it stop waiting; has no effect if already inited
                 self.stop()
                 break
         except Exception as e:
             init_duration = int((time.time() - init_start) * 1000)
             request_failure.fire(request_type="ld:init", name="mobile", response_time=init_duration, response_length=0, exception=e)
             log.warning("Caught exception. Restarting stream connection after one second. %s" % e)
             # no stacktrace here because, for a typical connection error, it'll just be a lengthy tour of urllib3 internals
         time.sleep(1)
示例#8
0
 def cb(all_flags):
     try:
         return self._evaluate_multi(user, all_flags)
     except Exception as e:
         log.error("Exception caught in all_flags: " + e.message +
                   " for user: " + str(user))
     return {}
示例#9
0
    def __init__(self, sdk_key, config=None, start_wait=5):
        check_uwsgi()
        self._sdk_key = sdk_key
        self._config = config or Config.default()
        self._session = CacheControl(requests.Session())
        self._queue = queue.Queue(self._config.events_max_pending)
        self._event_consumer = None
        self._lock = Lock()

        self._store = self._config.feature_store
        """ :type: FeatureStore """

        if self._config.offline:
            self._config.events_enabled = False
            log.info("Started LaunchDarkly Client in offline mode")
            return

        if self._config.events_enabled:
            self._event_consumer = self._config.event_consumer_class(
                self._queue, self._sdk_key, self._config)
            self._event_consumer.start()

        if self._config.use_ldd:
            if self._store.__class__ == "RedisFeatureStore":
                log.info("Started LaunchDarkly Client in LDD mode")
                return
            log.error("LDD mode requires a RedisFeatureStore.")
            return

        if self._config.feature_requester_class:
            self._feature_requester = self._config.feature_requester_class(
                sdk_key, self._config)
        else:
            self._feature_requester = FeatureRequesterImpl(sdk_key, self._config)
        """ :type: FeatureRequester """

        update_processor_ready = threading.Event()

        if self._config.update_processor_class:
            self._update_processor = self._config.update_processor_class(
                sdk_key, self._config, self._feature_requester, self._store, update_processor_ready)
        else:
            if self._config.stream:
                self._update_processor = StreamingUpdateProcessor(
                    sdk_key, self._config, self._feature_requester, self._store, update_processor_ready)
            else:
                self._update_processor = PollingUpdateProcessor(
                    sdk_key, self._config, self._feature_requester, self._store, update_processor_ready)
        """ :type: UpdateProcessor """

        self._update_processor.start()
        log.info("Waiting up to " + str(start_wait) + " seconds for LaunchDarkly client to initialize...")
        update_processor_ready.wait(start_wait)

        if self._update_processor.initialized:
            log.info("Started LaunchDarkly Client: OK")
        else:
            log.info("Initialization timeout exceeded for LaunchDarkly Client. Feature Flags may not yet be available.")
示例#10
0
 def on_connect_error(self, ignored):
     """
     :type ignored: twisted.python.Failure
     """
     from twisted.internet import reactor
     ignored.printTraceback()
     log.error("error connecting to endpoint {}: {}".format(
         self.url, ignored.getTraceback()))
     reactor.callLater(self.on_error_retry, self.connect)
示例#11
0
 def on_connect_error(self, ignored):
     """
     :type ignored: twisted.python.Failure
     """
     from twisted.internet import reactor
     ignored.printTraceback()
     log.error("error connecting to endpoint {}: {}".format(
         self.url, ignored.getTraceback()))
     reactor.callLater(self.on_error_retry, self.connect)
示例#12
0
    def _evaluate_internal(self, key, user, default, include_reasons_in_events):
        default = self._config.get_default(key, default)

        if self._config.offline:
            return EvaluationDetail(default, None, error_reason('CLIENT_NOT_READY'))
        
        if user is not None:
            self._sanitize_user(user)

        def send_event(value, variation=None, flag=None, reason=None):
            self._send_event({'kind': 'feature', 'key': key, 'user': user,
                              'value': value, 'variation': variation, 'default': default,
                              'version': flag.get('version') if flag else None,
                              'trackEvents': flag.get('trackEvents') if flag else None,
                              'debugEventsUntilDate': flag.get('debugEventsUntilDate') if flag else None,
                              'reason': reason if include_reasons_in_events else None})

        if not self.is_initialized():
            if self._store.initialized:
                log.warn("Feature Flag evaluation attempted before client has initialized - using last known values from feature store for feature key: " + key)
            else:
                log.warn("Feature Flag evaluation attempted before client has initialized! Feature store unavailable - returning default: "
                         + str(default) + " for feature key: " + key)
                reason = error_reason('CLIENT_NOT_READY')
                send_event(default, None, None, reason)
                return EvaluationDetail(default, None, reason)
        
        if user is not None and user.get('key', "") == "":
            log.warn("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly.")

        flag = self._store.get(FEATURES, key, lambda x: x)
        if not flag:
            reason = error_reason('FLAG_NOT_FOUND')
            send_event(default, None, None, reason)
            return EvaluationDetail(default, None, reason)
        else:
            if user is None or user.get('key') is None:
                reason = error_reason('USER_NOT_SPECIFIED')
                send_event(default, None, flag, reason)
                return EvaluationDetail(default, None, reason)

            try:
                result = evaluate(flag, user, self._store, include_reasons_in_events)
                for event in result.events or []:
                    self._send_event(event)
                detail = result.detail
                if detail.is_default_value():
                    detail = EvaluationDetail(default, None, detail.reason)
                send_event(detail.value, detail.variation_index, flag, detail.reason)
                return detail
            except Exception as e:
                log.error("Unexpected error while evaluating feature flag \"%s\": %s" % (key, e))
                log.debug(traceback.format_exc())
                reason = error_reason('EXCEPTION')
                send_event(default, None, flag, reason)
                return EvaluationDetail(default, None, reason)
示例#13
0
 def on_response(self, response):
     from twisted.internet import reactor
     if response.code != 200:
         log.error("non 200 response received: %d" % response.code)
         reactor.callLater(self.on_error_retry, self.connect)
     else:
         finished = Deferred()
         protocol = EventSourceProtocol(self.on_event, finished)
         finished.addBoth(self.reconnect)
         response.deliverBody(protocol)
         return finished
示例#14
0
 def on_response(self, response):
     from twisted.internet import reactor
     if response.code != 200:
         log.error("non 200 response received: %d" % response.code)
         reactor.callLater(self.on_error_retry, self.connect)
     else:
         finished = Deferred()
         protocol = EventSourceProtocol(self.on_event, finished)
         finished.addBoth(self.reconnect)
         response.deliverBody(protocol)
         return finished
示例#15
0
    def all_flags_state(self, user, **kwargs):
        """Returns an object that encapsulates the state of all feature flags for a given user,
        including the flag values and also metadata that can be used on the front end. 
        
        This method does not send analytics events back to LaunchDarkly.

        :param dict user: the end user requesting the feature flags
        :param kwargs: optional parameters affecting how the state is computed: set
          `client_side_only=True` to limit it to only flags that are marked for use with the
          client-side SDK (by default, all flags are included); set `with_reasons=True` to
          include evaluation reasons in the state (see `variation_detail`)
        :return: a FeatureFlagsState object (will never be None; its 'valid' property will be False
          if the client is offline, has not been initialized, or the user is None or has no key)
        :rtype: FeatureFlagsState
        """
        if self._config.offline:
            log.warn("all_flags_state() called, but client is in offline mode. Returning empty state")
            return FeatureFlagsState(False)

        if not self.is_initialized():
            if self._store.initialized:
                log.warn("all_flags_state() called before client has finished initializing! Using last known values from feature store")
            else:
                log.warn("all_flags_state() called before client has finished initializing! Feature store unavailable - returning empty state")
                return FeatureFlagsState(False)

        if user is None or user.get('key') is None:
            log.warn("User or user key is None when calling all_flags_state(). Returning empty state.")
            return FeatureFlagsState(False)
        
        state = FeatureFlagsState(True)
        client_only = kwargs.get('client_side_only', False)
        with_reasons = kwargs.get('with_reasons', False)
        try:
            flags_map = self._store.all(FEATURES, lambda x: x)
        except Exception as e:
            log.error("Unable to read flags for all_flag_state: %s" % e)
            return FeatureFlagsState(False)
        
        for key, flag in flags_map.items():
            if client_only and not flag.get('clientSide', False):
                continue
            try:
                detail = evaluate(flag, user, self._store, False).detail
                state.add_flag(flag, detail.value, detail.variation_index,
                    detail.reason if with_reasons else None)
            except Exception as e:
                log.error("Error evaluating flag \"%s\" in all_flags_state: %s" % (key, e))
                log.debug(traceback.format_exc())
                reason = {'kind': 'ERROR', 'errorKind': 'EXCEPTION'}
                state.add_flag(flag, None, None, reason if with_reasons else None)
        
        return state
 def _load_all(self):
     all_data = {FEATURES: {}, SEGMENTS: {}}
     for path in self._paths:
         try:
             self._load_file(path, all_data)
         except Exception as e:
             log.error('Unable to load flag data from "%s": %s' %
                       (path, repr(e)))
             traceback.print_exc()
             return
     self._store.init(all_data)
     self._inited = True
示例#17
0
 def _handle_response(self, r):
     server_date_str = r.getheader('Date')
     if server_date_str is not None:
         server_date = parsedate(server_date_str)
         if server_date is not None:
             timestamp = int(time.mktime(server_date) * 1000)
             self._last_known_past_time = timestamp
     if r.status > 299:
         log.error(http_error_message(r.status, "event delivery", "some events were dropped"))
         if not is_http_error_recoverable(r.status):
             self._disabled = True
             return
示例#18
0
        def cb(flag):
            try:
                if not flag:
                    log.warn("Feature Flag key: " + key +
                             " not found in Feature Store. Returning default.")
                    send_event(default)
                    return default

                return self._evaluate_and_send_events(flag, user, default)

            except Exception as e:
                log.error("Exception caught in variation: " + e.message +
                          " for flag key: " + key + " and user: " + str(user))

            return default
示例#19
0
 def run(self):
     log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._config.stream_uri)
     self._running = True
     hdrs = _stream_headers(self._sdk_key)
     uri = self._config.stream_uri
     while self._running:
         try:
             messages = SSEClient(uri, verify=self._config.verify_ssl, headers=hdrs)
             for msg in messages:
                 if not self._running:
                     break
                 self.process_message(self._store, self._requester, msg, self._ready)
         except Exception as e:
             log.error("Could not connect to LaunchDarkly stream: " + str(e.message) +
                       " waiting 1 second before trying again.")
             time.sleep(1)
示例#20
0
 def _run_main_loop(self):
     log.info("Starting event processor")
     while True:
         try:
             message = self._queue.get(block=True)
             if message.type == 'event':
                 self._process_event(message.param)
             elif message.type == 'flush':
                 self._trigger_flush()
             elif message.type == 'flush_users':
                 self._user_keys.clear()
             elif message.type == 'test_sync':
                 self._flush_workers.wait()
                 message.param.set()
             elif message.type == 'stop':
                 self._do_shutdown()
                 message.param.set()
                 return
         except Exception:
             log.error('Unhandled exception in event processor', exc_info=True)
示例#21
0
 def run(self):
     log.info("Starting StreamingUpdateProcessor connecting to uri: " +
              self._uri)
     self._running = True
     attempts = 0
     while self._running:
         if attempts > 0:
             delay = self._retry_delay.next_retry_delay(time.time())
             log.info("Will reconnect after delay of %fs" % delay)
             time.sleep(delay)
         attempts += 1
         try:
             self._es_started = int(time.time() * 1000)
             messages = self._connect()
             for msg in messages:
                 if not self._running:
                     break
                 self._retry_delay.set_good_since(time.time())
                 message_ok = self.process_message(self._store, msg)
                 if message_ok:
                     self._record_stream_init(False)
                     self._es_started = None
                 if message_ok is True and self._ready.is_set() is False:
                     log.info("StreamingUpdateProcessor initialized ok.")
                     self._ready.set()
         except UnsuccessfulResponseException as e:
             log.error(http_error_message(e.status, "stream connection"))
             self._record_stream_init(True)
             self._es_started = None
             if not is_http_error_recoverable(e.status):
                 self._ready.set(
                 )  # if client is initializing, make it stop waiting; has no effect if already inited
                 self.stop()
                 break
         except Exception as e:
             log.warning(
                 "Unexpected error on stream connection: %s, will retry" %
                 e)
             self._record_stream_init(True)
             self._es_started = None
示例#22
0
 def run(self):
     log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri)
     self._running = True
     while self._running:
         try:
             messages = self._connect()
             for msg in messages:
                 if not self._running:
                     break
                 message_ok = self.process_message(self._store, self._requester, msg)
                 if message_ok is True and self._ready.is_set() is False:
                     log.info("StreamingUpdateProcessor initialized ok.")
                     self._ready.set()
         except UnsuccessfulResponseException as e:
             log.error(http_error_message(e.status, "stream connection"))
             if not is_http_error_recoverable(e.status):
                 self._ready.set()  # if client is initializing, make it stop waiting; has no effect if already inited
                 self.stop()
                 break
         except Exception as e:
             log.warning("Caught exception. Restarting stream connection after one second. %s" % e)
             # no stacktrace here because, for a typical connection error, it'll just be a lengthy tour of urllib3 internals
         time.sleep(1)
示例#23
0
    def run(self):
        if not self._running:
            log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval))
            self._running = True
            while self._running:
                start_time = time.time()
                try:
                    all_data = self._requester.get_all_data()
                    self._store.init(all_data)
                    if not self._ready.is_set() is True and self._store.initialized is True:
                        log.info("PollingUpdateProcessor initialized ok")
                        self._ready.set()
                except UnsuccessfulResponseException as e:
                    log.error(http_error_message(e.status, "polling request"))
                    if not is_http_error_recoverable(e.status):
                        self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited
                        self.stop()
                except Exception as e:
                    log.exception(
                        'Error: Exception encountered when updating flags. %s' % e)

                elapsed = time.time() - start_time
                if elapsed < self._config.poll_interval:
                    time.sleep(self._config.poll_interval - elapsed)
示例#24
0
 def do_send(should_retry):
     # noinspection PyBroadException
     try:
         json_body = self._serializer.serialize_events(events)
         log.debug('Sending events payload: ' + json_body)
         hdrs = _headers(self._config.sdk_key)
         uri = self._config.events_uri
         r = self._session.post(uri,
                                headers=hdrs,
                                timeout=(self._config.connect_timeout,
                                         self._config.read_timeout),
                                data=json_body)
         if r.status_code == 401:
             log.error(
                 'Received 401 error, no further events will be posted since SDK key is invalid'
             )
             self.stop()
             return
         r.raise_for_status()
     except ProtocolError as e:
         if e.args is not None and len(
                 e.args) > 1 and e.args[1] is not None:
             inner = e.args[1]
             if inner.errno is not None and inner.errno == errno.ECONNRESET and should_retry:
                 log.warning(
                     'ProtocolError exception caught while sending events. Retrying.'
                 )
                 do_send(False)
         else:
             log.warning(
                 'Unhandled exception in event consumer. Analytics events were not processed.',
                 exc_info=True)
     except:
         log.warning(
             'Unhandled exception in event consumer. Analytics events were not processed.',
             exc_info=True)
示例#25
0
 def run(self):
     log.info("Starting StreamingUpdateProcessor connecting to uri: " +
              self._uri)
     self._running = True
     while self._running:
         try:
             self._es_started = int(time.time() * 1000)
             messages = self._connect()
             for msg in messages:
                 if not self._running:
                     break
                 message_ok = self.process_message(self._store,
                                                   self._requester, msg)
                 if message_ok:
                     self._record_stream_init(False)
                     self._es_started = None
                 if message_ok is True and self._ready.is_set() is False:
                     log.info("StreamingUpdateProcessor initialized ok.")
                     self._ready.set()
         except UnsuccessfulResponseException as e:
             log.error(http_error_message(e.status, "stream connection"))
             self._record_stream_init(True)
             self._es_started = None
             if not is_http_error_recoverable(e.status):
                 self._ready.set(
                 )  # if client is initializing, make it stop waiting; has no effect if already inited
                 self.stop()
                 break
         except Exception as e:
             log.warning(
                 "Caught exception. Restarting stream connection after one second. %s"
                 % e)
             self._record_stream_init(True)
             self._es_started = None
             # no stacktrace here because, for a typical connection error, it'll just be a lengthy tour of urllib3 internals
         time.sleep(1)
示例#26
0
    def all_flags_state(self, user, **kwargs):
        """Returns an object that encapsulates the state of all feature flags for a given user,
        including the flag values and also metadata that can be used on the front end. See the
        JavaScript SDK Reference Guide on
        `Bootstrapping <https://docs.launchdarkly.com/docs/js-sdk-reference#section-bootstrapping>`_.
        
        This method does not send analytics events back to LaunchDarkly.

        :param dict user: the end user requesting the feature flags
        :param kwargs: optional parameters affecting how the state is computed - see below

        :Keyword Arguments:
          * **client_side_only** (*boolean*) --
            set to True to limit it to only flags that are marked for use with the client-side SDK
            (by default, all flags are included)
          * **with_reasons** (*boolean*) --
            set to True to include evaluation reasons in the state (see :func:`variation_detail()`)
          * **details_only_for_tracked_flags** (*boolean*) --
            set to True to omit any metadata that is normally only used for event generation, such
            as flag versions and evaluation reasons, unless the flag has event tracking or debugging
            turned on

        :return: a FeatureFlagsState object (will never be None; its ``valid`` property will be False
          if the client is offline, has not been initialized, or the user is None or has no key)
        :rtype: FeatureFlagsState
        """
        if self._config.offline:
            log.warn(
                "all_flags_state() called, but client is in offline mode. Returning empty state"
            )
            return FeatureFlagsState(False)

        if not self.is_initialized():
            if self._store.initialized:
                log.warn(
                    "all_flags_state() called before client has finished initializing! Using last known values from feature store"
                )
            else:
                log.warn(
                    "all_flags_state() called before client has finished initializing! Feature store unavailable - returning empty state"
                )
                return FeatureFlagsState(False)

        if user is None or user.get('key') is None:
            log.warn(
                "User or user key is None when calling all_flags_state(). Returning empty state."
            )
            return FeatureFlagsState(False)

        state = FeatureFlagsState(True)
        client_only = kwargs.get('client_side_only', False)
        with_reasons = kwargs.get('with_reasons', False)
        details_only_if_tracked = kwargs.get('details_only_for_tracked_flags',
                                             False)
        try:
            flags_map = self._store.all(FEATURES, lambda x: x)
            if flags_map is None:
                raise ValueError("feature store error")
        except Exception as e:
            log.error("Unable to read flags for all_flag_state: %s" % repr(e))
            return FeatureFlagsState(False)

        for key, flag in flags_map.items():
            if client_only and not flag.get('clientSide', False):
                continue
            try:
                detail = evaluate(flag, user, self._store, False).detail
                state.add_flag(flag, detail.value, detail.variation_index,
                               detail.reason if with_reasons else None,
                               details_only_if_tracked)
            except Exception as e:
                log.error(
                    "Error evaluating flag \"%s\" in all_flags_state: %s" %
                    (key, repr(e)))
                log.debug(traceback.format_exc())
                reason = {'kind': 'ERROR', 'errorKind': 'EXCEPTION'}
                state.add_flag(flag, None, None,
                               reason if with_reasons else None,
                               details_only_if_tracked)

        return state
示例#27
0
    def _evaluate_internal(self, key, user, default, event_factory):
        default = self._config.get_default(key, default)

        if self._config.offline:
            return EvaluationDetail(default, None,
                                    error_reason('CLIENT_NOT_READY'))

        if not self.is_initialized():
            if self._store.initialized:
                log.warning(
                    "Feature Flag evaluation attempted before client has initialized - using last known values from feature store for feature key: "
                    + key)
            else:
                log.warning(
                    "Feature Flag evaluation attempted before client has initialized! Feature store unavailable - returning default: "
                    + str(default) + " for feature key: " + key)
                reason = error_reason('CLIENT_NOT_READY')
                self._send_event(
                    event_factory.new_unknown_flag_event(
                        key, user, default, reason))
                return EvaluationDetail(default, None, reason)

        if user is not None and user.get('key', "") == "":
            log.warning(
                "User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly."
            )

        try:
            flag = self._store.get(FEATURES, key, lambda x: x)
        except Exception as e:
            log.error(
                "Unexpected error while retrieving feature flag \"%s\": %s" %
                (key, repr(e)))
            log.debug(traceback.format_exc())
            reason = error_reason('EXCEPTION')
            self._send_event(
                event_factory.new_unknown_flag_event(key, user, default,
                                                     reason))
            return EvaluationDetail(default, None, reason)
        if not flag:
            reason = error_reason('FLAG_NOT_FOUND')
            self._send_event(
                event_factory.new_unknown_flag_event(key, user, default,
                                                     reason))
            return EvaluationDetail(default, None, reason)
        else:
            if user is None or user.get('key') is None:
                reason = error_reason('USER_NOT_SPECIFIED')
                self._send_event(
                    event_factory.new_default_event(flag, user, default,
                                                    reason))
                return EvaluationDetail(default, None, reason)

            try:
                result = evaluate(flag, user, self._store, event_factory)
                for event in result.events or []:
                    self._send_event(event)
                detail = result.detail
                if detail.is_default_value():
                    detail = EvaluationDetail(default, None, detail.reason)
                self._send_event(
                    event_factory.new_eval_event(flag, user, detail, default))
                return detail
            except Exception as e:
                log.error(
                    "Unexpected error while evaluating feature flag \"%s\": %s"
                    % (key, repr(e)))
                log.debug(traceback.format_exc())
                reason = error_reason('EXCEPTION')
                self._send_event(
                    event_factory.new_default_event(flag, user, default,
                                                    reason))
                return EvaluationDetail(default, None, reason)
示例#28
0
 def log_backoff_message(props):
     log.error("Streaming connection failed, will attempt to restart")
     log.info("Will reconnect after delay of %fs", props['wait'])
示例#29
0
 def log_backoff_message(props):
     log.error("Streaming connection failed, will attempt to restart")
     log.info("Will reconnect after delay of %fs", props['wait'])