• HOME
  • INTEGRATIONS
  • SDKS
  • GUIDES
  • API DOCS
No results for ""
EXPAND ALL
launchdarkly.com

EDIT ON GITHUB

Monitoring SDK status

Read time: 5 minutes
Last edited: Jun 18, 2021

Overview

This topic explains how various client-side SDKs monitor the connection to LaunchDarkly to determine the most recent flag values.

Monitoring the connection to LaunchDarkly

Some SDKs expose some of their internal statuses through the Connection Status API to allow your application to monitor the SDK's status. This is provided primarily as a mechanism for the application to determine how recently the internal flag cache has been updated with the most recent values, as well as diagnosing potential reasons for the flag cache to be out of date.

Client-side SDKs

This feature is available in the following client-side SDKs:

  • Android
  • Electron
  • Flutter
  • iOS
  • React Native

Android

Supported versions
This feature is available in the Android SDK's versions 2.8.0 and later.

The Android SDK exposes some of its internal status through APIs to let your application monitor it. This allows the application to determine how recently the internal flag cache has been updated with the most recent values, as well as diagnosing potential reasons for the flag cache to be out of date.

The SDK has seven connectivity states dependent on its configuration, application foreground state, network connectivity, and calls explicitly setting the client offline or online.

This table describes the states:

Connection modeDescription
STREAMINGThe SDK is either connected to the flag stream, or is actively attempting to acquire a connection.
POLLINGThe SDK is in foreground polling mode because it was configured with streaming disabled.
BACKGROUND_POLLINGThe SDK has detected the application is in the background and has transitioned to battery-saving background polling.
BACKGROUND_DISABLEDThe SDK was configured with background polling disabled. The SDK has detected the application is in the background and is not attempting to update the flag cache.
OFFLINEThe SDK has detected that the mobile device does not have an active network connection. It has ceased flag update attempts until the network status changes.
SET_OFFLINEThe SDK has been explicitly set offline, either in the initial configuration, by setOffline(), or as a result of failed authentication to LaunchDarkly. The SDK will stay offline unless setOnline() is called.
SHUTDOWNThe shutdown state indicates the SDK has been permanently shutdown as a result of a call to close().

The SDK also internally stores a timestamp of the most recent successful and failed connections to LaunchDarkly, as well as information related to the most recent failed connection. The LDClient method getConnectionInformation() returns a structure allowing retrieval of these fields.

1LDClient ldClient = LDClient.get();
2ConnectionInformation connectionInfo = ldClient.getConnectionInformation();
3// One of the seven modes described above
4ConnectionInformation.ConnectionMode connectionMode =
5 connectionInfo.getConnectionMode();
6// Most recent successful flag cache update in millis from the epoch
7// Or null if flags have never been retrieved
8Long lastSuccess = connectionInfo.getLastSuccessfulConnection();
9// Most recent unsuccessful flag cache update attempt in millis from the epoch
10// Or null if flag update has never been attempted
11Long lastSuccess = connectionInfo.getLastFailedConnection();
12// Most recent failure or null
13LDFailure ldFailure = connectionInfo.getLastFailure();

LDFailure is a LaunchDarklyException with an associated FailureType. It may include a .cause(), which is propagated from an underlying exception associated with the update's failure. The cause itself should be considered unstable because it is dependent on internal implementation, though the mechanism to retrieve it will be maintained.

The failure types are summarized below:

FailureTypeDescription
INVALID_RESPONSE_BODYA response body received either through polling or streaming was unable to be parsed.
NETWORK_FAILUREA network request for polling, or the EventSource stream reported a failure.
UNEXPECTED_STREAM_ELEMENT_TYPEAn event was received through the stream with an unknown event name. This could indicate a newer SDK is available if new event types have become available through the flag stream since the SDK's release.
UNEXPECTED_RESPONSE_CODEThis indicates the LDFailure is an instance of LDInvalidResponseCodeFailure. Continue reading below for more details.
UNKNOWN_ERRORSome other issue occurred.

If matching on the FailureType, use a default case to handle any future cases provided. The UNEXPECTED_RESPONSE_CODE case indicates that you can cast the LDFailure to a LDInvalidResponseCodeFailure for more information. This more specific failure includes a response code and whether the failure is considered retryable.

Here is an example:

1LDClient ldClient = LDClient.get();
2ConnectionInformation connectionInfo = ldClient.getConnectionInformation();
3LDFailure ldFailure = connectionInfo.getLastFailure();
4if (ldFailure != null) {
5 Timber.d("Received failure with message %s", ldFailure.getMessage());
6 // Retrieve the failure type
7 LDFailure.FailureType failureType = ldFailure.getFailureType();
8 switch (failureType) {
9 case INVALID_RESPONSE_BODY:
10 Timber.d("Received invalid response body");
11 break;
12 case NETWORK_FAILURE:
13 Timber.d("Network failure, may have bad connection");
14 break;
15 case UNEXPECTED_STREAM_ELEMENT_TYPE:
16 Timber.d("Unexpected stream element, may require update");
17 break;
18 case UNEXPECTED_RESPONSE_CODE:
19 LDInvalidResponseCodeFailure responseCodeFailure =
20 (LDInvalidResponseCodeFailure) ldFailure;
21 int responseCode = responseCodeFailure.getResponseCode();
22 if (responseCodeFailure.isRetryable()) {
23 Timber.d("Received invalid response code %d", responseCode);
24 } else {
25 Timber.d("Received invalid response code %d, giving up", responseCode);
26 }
27 break;
28 case UNKNOWN_ERROR:
29 default:
30 Timber.d("Unknown error");
31 break;
32 }
33
34 Throwable cause = ldFailure.getCause();
35 if (cause != null) {
36 // Do something with underlying cause
37 }
38}

A callback-based interface is also provided to allow notifying the application when the ConnectionMode changes, as well as whenever the LDFailure in ConnectionStatus changes. The application must provide a class instance implementing LDStatusListener to the SDK client instance method registerStatusListener to register the listeners with the SDK.

Listener weak reference

The SDK maintains only a weak reference to the registered LDStatusListener, so the application maintains a reference for as long as the application desires the listener to be available. This helps prevent creating a long-term reference to an Activity by creating a static internal class instance for use as a listener. By using a weak reference to the listener, the Activity can still be garbage collected normally, even if it maintains a registered LDStatusListener. We recommend unregistering the listener when it's finished.

Here is a brief example:

1class MainActivity extends Activity {
2 private LDClient ldClient;
3 private LDStatusListener ldStatusListener;
4
5 @Override
6 protected void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.activity_main);
9
10 ldStatusListener = new LDStatusListener() {
11 @Override
12 public void onConnectionModeChanged(ConnectionInformation connectionInfo) {
13 // handle new connection info
14 }
15
16 @Override
17 public void onInternalFailure(LDFailure ldFailure) {
18 // handle failure
19 }
20 };
21
22 ldClient = LDClient.get();
23 ldClient.registerStatusListener(ldStatusListener);
24 }
25
26 @Override
27 protected void onDestroy() {
28 super.onDestroy();
29 ldClient.unregisterStatusListener(ldStatusListener);
30 }
31}

Electron

By default, the client requests feature flag values only once per user. This happens once at startup time, and then each time you call identify(). You can also use a persistent connection to receive flag updates whenever they occur.

Enable this behavior by setting streaming to true in the client options or calling client.setStreaming(true). LaunchDarkly pushes new values to the SDK, which updates the current feature flag state in the background, ensuring that variation() always returns the latest values.

If you want to be notified when a flag has changed, you can use an event listener for a specific flag.

Here's how:

1client.on('change:YOUR_FEATURE_KEY', function(newValue, oldValue) {
2 console.log('The flag was ' + oldValue + ' and now it is ' + newValue);
3});

Alternatively, you can listen for all feature flag changes.

Here's how:

1client.on('change', function(allFlagChanges)) {
2 Object.keys(allFlagChanges).forEach(function(key) {
3 console.log('Flag ' + key + ' is now ' + allFlagChanges[key]);
4 });
5});

Subscribing to change events automatically turns on streaming mode as well, unless you have explicitly set streaming to false.

Flutter

The Flutter SDK exposes some of its internal status through APIs to let your application monitor it. This allows the application to determine how recently the internal flag cache has been updated with the most recent values, as well as diagnosing potential reasons for the flag cache to be out of date.

The SDK also stores a timestamp of the most recent successful and failed connections to LaunchDarkly, as well as information related to the most recent failed connection. The LDClient method getConnectionInformation() returns a structure that lets you retrieve these fields.

1LDConnectionInformation connectionInfo = await LDClient.getConnectionInformation();
2// The current connection state
3LDConnectionState connectionState = connectionInfo.connectionState;
4// Most recent successful flag cache update
5DateTime lastSuccess = connectionInfo.lastSuccessfulConnection;
6// Most recent unsuccessful flag cache update attempt
7DateTime lastfailure = connectionInfo.lastFailedConnection;
8// Most recent failure or null
9LDFailure ldFailure = connectionInfo.lastFailure;

To learn more about the connection status fields, read the API docs.

iOS

The iOS SDK exposes some of its internal status through the Connection Status API to let your application monitor it. This allows the application to determine how recently the internal flag cache has been updated with the most recent values, as well as diagnosing potential reasons for the flag cache to be out of date.

The SDK has four connectivity states dependent on its configuration, application foreground state, network connectivity, and calls explicitly setting the client offline or online.

This table describes the states:

Connection ModeDescription
streamingThe SDK has an active streaming connection.
pollingThe SDK has an active polling connection.
offlineThe SDK is set offline or has no network connection.
establishingStreamingConnectionThe SDK is attempting to connect to LaunchDarkly by streaming.

The SDK also stores a timestamp of the most recent successful and failed connections to LaunchDarkly, as well as information related to the most recent failed connection. lastKnownFlagValidity is nil if either no connection has ever been made successfully or if the SDK has an active streaming connection. It has a value if it is in polling mode and at least one poll has completed successfully, or if it is in streaming mode whenever the streaming connection closes. The LDClient.shared method getConnectionInformation() returns a structure that lets you retrieve these fields.

The ConnectionInformation class can return four different values for lastFailureReason.

This table describes the values:

lastFailureReason valueDescription
noneThis returns when no error has been recorded.
unknownErrorThis returns when there is an internal error in the stream request.
unauthorizedThis returns when an incorrect mobile key is provided.
httpErrorThis returns when an error with an HTTP error code is present.

You can listen to changes in ConnectionInformation.ConnectionMode in a similar manner to flag observers.

Here is an example of the API:

1// Get current connection information
2let connectionInformation = LDClient.get()!.getConnectionInformation()
3// Setting a connection mode update observer
4LDClient.get()!.observeCurrentConnectionMode(owner: self) { [weak self] connectionMode in
5 // do something after ConnectionMode was updated.
6}

React Native

The React Native SDK exposes some of its internal status through the Connection Status API to allow your application to monitor the SDK's status. This is provided primarily as a mechanism for the application to determine how recently the internal flag cache has been updated with the most recent values, as well as diagnosing potential reasons for the flag cache to be out of date.

1const connectionMode = await client.getConnectionMode();
2const lastSuccessfulConnection = await client.getLastSuccessfulConnection();
3const lastFailedConnection = await client.getLastFailedConnection();
4const lastFailureReason = await client.getLastFailure();

To learn more about the return types, read the API documentation about LDConnectionMode and LDFailureReason.

Server-side SDKs

The following server-side SDKs provide two kinds of SDK status monitoring:

  • Data source status
  • Data store status

Data source status is the status of the SDK's communication with LaunchDarkly to get feature flag data. If the streaming connection to LaunchDarkly is interrupted, or, in polling mode, if a polling request fails, the SDK might not be able to receive flag updates. The data source status will indicate this, providing an overall state such as "valid" or "interrupted" and information about the last error that occurred, if any.

Data store status is the status of a database, such as Redis or DynamoDB, if the SDK has been configured to use one. If the SDK tries to read from or write to a database and encounters an error, the data store status will indicate this.

This feature is available in the following server-side SDKs:

.NET

Supported versions
This feature is available in the .NET SDK's versions 6.0.0 and later.

To check the data source status:

1var dataSourceStatus = client.DataSourceStatusProvider.Status;

To check the data store status:

1var dataStoreStatus = client.DataStoreStatusProvider.Status;

Alternatively, you can register an event handler that will receive a new status value whenever the status changes.

Here is an example:

1client.DataSourceStatusProvider.StatusChanged +=
2 (sender, status) -> {
3 Console.WriteLine("new status is: {0}", status);
4 };
5
6client.DataStoreStatusProvider.StatusChanged +=
7 (sender, status) -> {
8 Console.WriteLine("new status is: {0}", status);
9 };

Go

To check the data source status:

1dataSourceStatus := client.GetDataSourceStatusProvider().GetStatus()

To check the data store status:

1dataStoreStatus := client.GetDataStoreStatusProvider().GetStatus()

Alternatively, you can monitor a channel that provides a new status value whenever the status changes:

1dataSourceStatusChannel := client.GetDataSourceStatusProvider().AddStatusListener()
2go func() {
3 for status := range dataSourceStatusChannel {
4 fmt.Println("new status is: ", status)
5 }
6}()
7
8dataStoreStatusChannel := client.GetDataStoreStatusProvider().AddStatusListener()
9go func() {
10 for status := range dataStoreStatusChannel {
11 fmt.Println("new status is: ", status)
12 }
13}()

To learn more, read GetDataSourceStatusProvider and GetDataStoreStatusProvider.

Java

To check the data source status:

1DataSourceStatusProvider.Status dataSourceStatus =
2 client.getDataSourceStatusProvider().getStatus()

To check the data store status:

1DataStoreStatusProvider.Status dataStoreStatus =
2 client.getDataStoreStatusProvider().getStatus()

Alternatively, you can register an event listener that receives a new status value whenever the status changes:

1client.getDataSourceStatusProvider().addStatusListener(
2 status -> {
3 System.out.println("new status is: " + status);
4 }
5);
6
7client.getDataStoreStatusProvider().addStatusListener(
8 status -> {
9 System.out.println("new status is: " + status);
10 }
11);

To learn more, read DataSourceStatusProvider and DataStoreStatusProvider.