> ## Documentation Index
> Fetch the complete documentation index at: https://launchdarkly.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Monitoring SDK status

<View title="Developer" />

<View title="Federal docs" />

<View title="EU docs" />

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

## Monitor 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](#client-side-sdks)
* [Server-side SDKs](#server-side-sdks)

## Client-side SDKs

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

* [.NET (client-side)](#net-client-side)
* [Android](#android)
* [C++ (client-side)](#c++-client-side)
* [Electron](#electron)
* [Flutter](#flutter)
* [iOS](#ios)
* [JavaScript](#javascript)
* [React Native](#react-native)
* [Roku](#roku)

### .NET (client-side)

<Accordion title="Expand .NET (client-side) code sample">
  <Note>
    **Supported versions**

    This feature is available in the client-side .NET SDK's versions 2.0.0 and later.
  </Note>

  The client-side .NET SDK defines "data source status" as 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.

  To check the data source status:

  <CodeGroup>
    ```csharp title="C#" lines wrap theme={null}
    var dataSourceStatus = client.DataSourceStatusProvider.Status;
    ```
  </CodeGroup>

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

  Here is an example:

  <CodeGroup>
    ```csharp title="C#" lines wrap theme={null}
    client.DataSourceStatusProvider.StatusChanged +=
        (sender, status) => {
            Console.WriteLine("new status is: {0}", status);
        };
    ```
  </CodeGroup>

  To learn more, read [`IDataSourceStatusProvider`](https://launchdarkly.github.io/dotnet-core/pkgs/sdk/client/api/LaunchDarkly.Sdk.Client.Interfaces.IDataSourceStatusProvider.html).
</Accordion>

### Android

<Accordion title="Expand Android code sample">
  <Note>
    **Supported versions**

    This feature is available in the Android SDK's versions 2.8.0 and later.
  </Note>

  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:

  <table>
    <thead>
      <tr>
        <th>Connection mode</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td>
          <code>STREAMING</code>
        </td>

        <td>
          The SDK is either connected to the flag stream, or is actively attempting to acquire a connection.
        </td>
      </tr>

      <tr>
        <td>
          <code>POLLING</code>
        </td>

        <td>The SDK is in foreground polling mode because it was configured with streaming disabled.</td>
      </tr>

      <tr>
        <td>
          <code>BACKGROUND\_POLLING</code>
        </td>

        <td>
          The SDK has detected the application is in the background and has transitioned to battery-saving background
          polling.
        </td>
      </tr>

      <tr>
        <td>
          <code>BACKGROUND\_DISABLED</code>
        </td>

        <td>
          The 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.
        </td>
      </tr>

      <tr>
        <td>
          <code>OFFLINE</code>
        </td>

        <td>
          The 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.
        </td>
      </tr>

      <tr>
        <td>
          <code>SET\_OFFLINE</code>
        </td>

        <td>
          The SDK has been explicitly set offline, either in the initial configuration, by <code>setOffline()</code>, or
          as a result of failed authentication to LaunchDarkly. The SDK will stay offline unless <code>setOnline()</code>{' '}
          is called.
        </td>
      </tr>

      <tr>
        <td>
          <code>SHUTDOWN</code>
        </td>

        <td>
          The shutdown state indicates the SDK has been permanently shutdown as a result of a call to <code>close()</code>
          .
        </td>
      </tr>
    </tbody>
  </table>

  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.

  <CodeGroup>
    ```java title="Java" lines wrap theme={null}
    LDClient client = LDClient.get();
    ConnectionInformation connectionInfo = client.getConnectionInformation();
    // One of the seven modes described above
    ConnectionInformation.ConnectionMode connectionMode =
      connectionInfo.getConnectionMode();
    // Most recent successful flag cache update in millis from the epoch
    // Or null if flags have never been retrieved
    Long lastSuccess = connectionInfo.getLastSuccessfulConnection();
    // Most recent unsuccessful flag cache update attempt in millis from the epoch
    // Or null if flag update has never been attempted
    Long lastError = connectionInfo.getLastFailedConnection();
    // Most recent failure or null
    LDFailure ldFailure = connectionInfo.getLastFailure();
    ```

    ```kotlin title="Kotlin" lines wrap theme={null}
    val client: LDClient = LDClient.get()
    val connectionInfo: ConnectionInformation = client.connectionInformation
    // One of the seven modes described above
    val connectionMode: ConnectionInformation.ConnectionMode =
        connectionInfo.connectionMode
    // Most recent successful flag cache update in millis from the epoch
    // Or null if flags have never been retrieved
    val lastSuccess: Long? = connectionInfo.lastSuccessfulConnection
    // Most recent unsuccessful flag cache update attempt in millis from the epoch
    // Or null if flag update has never been attempted
    val lastError: Long? = connectionInfo.lastFailedConnection
    // Most recent failure or null
    val ldFailure: LDFailure? = connectionInfo.lastFailure
    ```
  </CodeGroup>

  `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:

  <table>
    <thead>
      <tr>
        <th>FailureType</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td>
          <code>INVALID\_RESPONSE\_BODY</code>
        </td>

        <td>A response body received either through polling or streaming was unable to be parsed.</td>
      </tr>

      <tr>
        <td>
          <code>NETWORK\_FAILURE</code>
        </td>

        <td>
          A network request for polling, or the <code>EventSource</code> stream reported a failure.
        </td>
      </tr>

      <tr>
        <td>
          <code>UNEXPECTED\_STREAM\_ELEMENT\_TYPE</code>
        </td>

        <td>
          An event was received through the stream with an unknown event key. This could indicate a newer SDK is available if new event kinds have become available through the flag stream since the SDK's release.
        </td>
      </tr>

      <tr>
        <td>
          <code>UNEXPECTED\_RESPONSE\_CODE</code>
        </td>

        <td>
          This indicates the <code>LDFailure</code> is an instance of <code>LDInvalidResponseCodeFailure</code>. Continue reading below for more details.
        </td>
      </tr>

      <tr>
        <td>
          <code>UNKNOWN\_ERROR</code>
        </td>

        <td>Some other issue occurred.</td>
      </tr>
    </tbody>
  </table>

  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:

  <CodeGroup>
    ```java title="Java" maxLines=0 expandable lines wrap theme={null}
    LDClient client = LDClient.get();
    ConnectionInformation connectionInfo = client.getConnectionInformation();
    LDFailure ldFailure = connectionInfo.getLastFailure();
    if (ldFailure != null) {
        Timber.d("Received failure with message %s", ldFailure.getMessage());
        // Retrieve the failure type
        LDFailure.FailureType failureType = ldFailure.getFailureType();
        switch (failureType) {
            case INVALID_RESPONSE_BODY:
                Timber.d("Received invalid response body");
                break;
            case NETWORK_FAILURE:
                Timber.d("Network failure, may have bad connection");
                break;
            case UNEXPECTED_STREAM_ELEMENT_TYPE:
                Timber.d("Unexpected stream element, may require update");
                break;
            case UNEXPECTED_RESPONSE_CODE:
                LDInvalidResponseCodeFailure responseCodeFailure =
                    (LDInvalidResponseCodeFailure) ldFailure;
                int responseCode = responseCodeFailure.getResponseCode();
                if (responseCodeFailure.isRetryable()) {
                    Timber.d("Received invalid response code %d", responseCode);
                } else {
                    Timber.d("Received invalid response code %d, giving up", responseCode);
                }
                break;
            case UNKNOWN_ERROR:
            default:
                Timber.d("Unknown error");
                break;
        }

        Throwable cause = ldFailure.getCause();
        if (cause != null) {
            // Do something with underlying cause
        }
    }
    ```

    ```kotlin title="Kotlin" maxLines=0 expandable lines wrap theme={null}
    val client: LDClient = LDClient.get()
    val connectionInfo: ConnectionInformation = client.connectionInformation
    val ldFailure: LDFailure? = connectionInfo.lastFailure
    if (ldFailure != null) {
        Timber.d("Received failure with message %s", ldFailure.message)
        // Retrieve the failure type
        val failureType: LDFailure.FailureType = ldFailure.failureType
        when (failureType) {
            INVALID_RESPONSE_BODY -> {
                Timber.d("Received invalid response body")
            }
            NETWORK_FAILURE -> {
                Timber.d("Network failure, may have bad connection")
            }
            UNEXPECTED_STREAM_ELEMENT_TYPE -> {
                Timber.d("Unexpected stream element, may require update")
            }
            UNEXPECTED_RESPONSE_CODE -> {
                val responseCodeFailure: LDInvalidResponseCodeFailure =
                    ldFailure as LDInvalidResponseCodeFailure
                val responseCode = responseCodeFailure.responseCode
                if (responseCodeFailure.isRetryable) {
                    Timber.d("Received invalid response code %d", responseCode)
                } else {
                    Timber.d("Received invalid response code %d, giving up", responseCode)
                }
            }
            UNKNOWN_ERROR -> {
                Timber.d("Unknown error")
            }
        }
        val cause: Throwable? = ldFailure.cause
        if (cause != null) {
            // Do something with underlying cause
        }
    }
    ```
  </CodeGroup>

  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.

  <Warning>
    **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.
  </Warning>

  Here is a brief example:

  <CodeGroup>
    ```java title="Java" maxLines=0 expandable lines wrap theme={null}
    class MainActivity extends Activity {
        private LDClient client;
        private LDStatusListener ldStatusListener;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            ldStatusListener = new LDStatusListener() {
                @Override
                public void onConnectionModeChanged(ConnectionInformation connectionInfo) {
                    // handle new connection info
                }

                @Override
                public void onInternalFailure(LDFailure ldFailure) {
                    // handle failure
                }
            };

            try {
                client = LDClient.get();
            } catch (LaunchDarklyException e) {
                // client was not initialized before calling get()
                return;
            }
            client.registerStatusListener(ldStatusListener);
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (client != null) {
                client.unregisterStatusListener(ldStatusListener);
            }
        }
    }
    ```

    ```kotlin title="Kotlin" maxLines=0 expandable lines wrap theme={null}
    class MainActivity : Activity() {
        private var client: LDClient? = null
        private var ldStatusListener: LDStatusListener? = null

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            ldStatusListener = object : LDStatusListener {
                override fun onConnectionModeChanged(connectionInfo: ConnectionInformation) {
                    // handle new connection info
                }

                override fun onInternalFailure(ldFailure: LDFailure) {
                    // handle failure
                }
            }

            client = LDClient.get()
            client?.registerStatusListener(ldStatusListener)
        }

        override fun onDestroy() {
            super.onDestroy()
            client?.unregisterStatusListener(ldStatusListener)
        }
    }
    ```
  </CodeGroup>
</Accordion>

### C++ (client-side)

<Accordion title="Expand C++ (client-side) code sample">
  The C++ (client-side) SDK exposes some of its internal status to let your application monitor it.

  The SDK has several data source states dependent on its configuration, network connectivity, and calls explicitly setting the client offline or online.

  This table describes the states:

  <table>
    <thead>
      <tr>
        <th>Data source state</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td><code>kInitializing</code></td>
        <td>The initial state of the data source when the SDK is being initialized.</td>
      </tr>

      <tr>
        <td><code>kValid</code></td>
        <td>The data source is currently operational and has not had any problems since the last time it received data.</td>
      </tr>

      <tr>
        <td><code>kInterrupted</code></td>
        <td>The data source encountered an error that it will attempt to recover from.</td>
      </tr>

      <tr>
        <td><code>kSetOffline</code></td>
        <td>The application has told the SDK to stay offline.</td>
      </tr>

      <tr>
        <td><code>kShutdown</code></td>
        <td>The data source has been permanently shut down. This could be because it encountered an unrecoverable error, or because the SDK client was explicitly shut down.</td>
      </tr>
    </tbody>
  </table>

  The SDK stores a timestamp of when the data source state most recently changed. It also stores information about the last error that the data source encountered, if any.

  This table describes the possible errors:

  <table>
    <thead>
      <tr>
        <th><code>LastError</code> value</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td><code>kUnknown</code></td>
        <td>An unexpected error, such as an uncaught exception.</td>
      </tr>

      <tr>
        <td><code>kNetworkError</code></td>
        <td>An I/O error, such as a dropped connection.</td>
      </tr>

      <tr>
        <td><code>kErrorResponse</code></td>
        <td>The LaunchDarkly service returned an HTTP response with an error status.</td>
      </tr>

      <tr>
        <td><code>kInvalidData</code></td>
        <td>The SDK received malformed data from the LaunchDarkly service.</td>
      </tr>

      <tr>
        <td><code>kStoreError</code></td>
        <td>The data source itself is working, but when it tried to put an update into the data store, the data store failed. The SDK may not have the latest data.</td>
      </tr>
    </tbody>
  </table>

  Here's how to monitor the data source state:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    client.DataSourceStatus().OnDataSourceStatusChange([](client_side::data_sources::DataSourceStatus status) {
      if (status.State() ==
      client_side::data_sources::DataSourceStatus::
      DataSourceState::kValid) {
        /* Flag data has been received from LaunchDarkly.*/
      }
    });
    ```
  </CodeGroup>

  If you are working in C, there are a few more steps:

  1. Define a callback to receive the data source status change:

  <CodeGroup>
    ```c title="C++ SDK v3.0 (C binding), define callback" lines wrap theme={null}
    void OnDataSourceStatusChanged(LDDataSourceStatus status, void* user_data) {
      printf("status: %d\n", LDDataSourceStatus_GetState(status));
    }
    ```
  </CodeGroup>

  2. Assign the callback by creating a listener connection:

  <CodeGroup>
    ```c title="C++ SDK v3.0 (C binding), create listener connection" lines wrap theme={null}
    struct LDDataSourceStatusListener listener;
    LDDataSourceStatusListener_Init(&listener);

    listener.StatusChanged = OnDataSourceStatusChanged;

    /* You may optionally assign the UserData pointer, which will be passed into StatusChanged. */
    /* listener.UserData = &some_struct; */

    LDListenerConnection connection =
            LDClientSDK_DataSourceStatus_OnStatusChange(sdk, listener);

    /* You can disconnect the listener later */
    LDListenerConnection_Disconnect(connection);
    ```
  </CodeGroup>

  3. Ensure the connection is freed when you are done with it:

  <CodeGroup>
    ```c title="C++ SDK v3.0 (C binding), free connection" lines wrap theme={null}
    LDListenerConnection_Free(connection);
    ```
  </CodeGroup>

  To learn more, read [`DataSourceStatus`](https://launchdarkly.github.io/cpp-sdks/libs/client-sdk/docs/html/classlaunchdarkly_1_1client__side_1_1Client.html#adedade4cae0355b19f954907dbcf2362).
</Accordion>

### Electron

<Accordion title="Expand Electron code sample">
  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:

  <CodeGroup>
    ```js title="JavaScript" lines wrap theme={null}
    client.on('change:example-flag-key', (newValue, oldValue) => {
      console.log('The flag was ' + oldValue + ' and now it is ' + newValue);
    });
    ```
  </CodeGroup>

  Alternatively, you can listen for all feature flag changes.

  Here's how:

  <CodeGroup>
    ```js title="JavaScript" lines wrap theme={null}
    client.on('change', (allFlagChanges) => {
      Object.keys(allFlagChanges).forEach((key) => {
        console.log('Flag ' + key + ' is now ' + allFlagChanges[key]);
      });
    });
    ```
  </CodeGroup>

  Subscribing to `change` events automatically turns on streaming mode as well, unless you have explicitly set `streaming` to `false`.
</Accordion>

### Flutter

<Accordion title="Expand Flutter code sample">
  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 diagnose potential reasons for the flag cache to be out of date.

  The SDK 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 `dataSourceStatus` returns a structure that lets you retrieve these fields.

  <CodeGroup>
    ```dart title="Flutter SDK v4" lines wrap theme={null}
    // get the current status
    final status = client.dataSourceStatus;

    // listen for changes
    final sub = client.dataSourceStatusChanges.listen((status){
      // act on status
    });
    ```
  </CodeGroup>

  To learn more about the connection status fields, read [`DataSourceStatus`](https://pub.dev/documentation/launchdarkly_flutter_client_sdk/latest/launchdarkly_flutter_client_sdk/DataSourceStatus-class.html).
</Accordion>

### iOS

<Accordion title="Expand iOS code sample">
  <Note>
    **Supported versions**

    This feature is available in the iOS SDK's versions 4.2.0 and later.
  </Note>

  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:

  <table>
    <thead>
      <tr>
        <th>Connection Mode</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td>
          <code>streaming</code>
        </td>

        <td>The SDK has an active streaming connection.</td>
      </tr>

      <tr>
        <td>
          <code>polling</code>
        </td>

        <td>The SDK has an active polling connection.</td>
      </tr>

      <tr>
        <td>
          <code>offline</code>
        </td>

        <td>The SDK is set offline or has no network connection.</td>
      </tr>

      <tr>
        <td>
          <code>establishingStreamingConnection</code>
        </td>

        <td>The SDK is attempting to connect to LaunchDarkly by streaming.</td>
      </tr>
    </tbody>
  </table>

  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:

  <table>
    <thead>
      <tr>
        <th>lastFailureReason value</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td>
          <code>none</code>
        </td>

        <td>This returns when no error has been recorded.</td>
      </tr>

      <tr>
        <td>
          <code>unknownError</code>
        </td>

        <td>This returns when there is an internal error in the stream request.</td>
      </tr>

      <tr>
        <td>
          <code>unauthorized</code>
        </td>

        <td>This returns when an incorrect mobile key is provided.</td>
      </tr>

      <tr>
        <td>
          <code>httpError</code>
        </td>

        <td>This returns when an error with an HTTP error code is present.</td>
      </tr>
    </tbody>
  </table>

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

  Here is an example of the API:

  <CodeGroup>
    ```swift title="Swift" lines wrap theme={null}
    // Get current connection information
    let connectionInformation = LDClient.get()!.getConnectionInformation()
    // Setting a connection mode update observer
    LDClient.get()!.observeCurrentConnectionMode(owner: self) { [weak self] connectionMode in
        // do something after ConnectionMode was updated.
    }
    ```
  </CodeGroup>
</Accordion>

### JavaScript

<Accordion title="Expand JavaScript code sample">
  The SDK uses an emitter pattern for errors. You can subscribe to `change`, `error`, and `dataSourceStatus` events. When you subscribe to `dataSourceStatus` events, the state returned may be one of `Initializing`, `Valid`, `Interrupted`, `SetOffline`, `Closed`.

  Here's how:

  <CodeGroup>
    ```js title="JavaScript SDK, v4.x" lines wrap theme={null}
    client.on('dataSourceStatus', (context, error) => {
      // handle error

      // error.state may be one of:
      // Initializing, Valid, Interrupted, SetOffline, Closed
    });
    ```
  </CodeGroup>

  To learn more, read [DataSourceStatus](https://launchdarkly.github.io/js-core/packages/shared/sdk-client/docs/interfaces/DataSourceStatus.html).
</Accordion>

### React Native

<Accordion title="Expand React Native code sample">
  Starting with version 10 of the React Native SDK, the SDK uses an emitter pattern for errors. You can subscribe to `error` and `change` events.

  Here's how:

  <CodeGroup>
    ```ts title="React Native SDK, v10" lines wrap theme={null}
    client.on('error', (context: LDContext, error: Error) => {
      // handle error
    });
    ```
  </CodeGroup>

  To learn more, read [`on`](https://launchdarkly.github.io/js-core/packages/sdk/react-native/docs/classes/LDClientImpl.html#on) and [`off`](https://launchdarkly.github.io/js-core/packages/sdk/react-native/docs/classes/LDClientImpl.html#off).
</Accordion>

### Roku

<Accordion title="Expand Roku code sample">
  You can use Roku's `observeField` method on your node to respond to changes in status.

  Here's how:

  <CodeGroup>
    ```brightscript title="BrightScript" lines wrap theme={null}

    ' replace "onStatusChange" with the name of your handler functions
    launchDarklyNode.observeField("status", "onStatusChange")

    ```
  </CodeGroup>
</Accordion>

## 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 (server-side)](#net-server-side)
* [C++ (server-side)](#c++-server-side)
* [Go](#go)
* [Java](#java)
* [Python](#python)
* [Ruby](#ruby)

### .NET (server-side)

<Accordion title="Expand .NET (server-side) code sample">
  <Note>
    **Supported versions**

    This feature is available in the .NET SDK's versions 6.0.0 and later.
  </Note>

  To check the data source status:

  <CodeGroup>
    ```csharp title="C#" lines wrap theme={null}
    var dataSourceStatus = client.DataSourceStatusProvider.Status;
    ```
  </CodeGroup>

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

  Here is an example:

  <CodeGroup>
    ```csharp title="C#" lines wrap theme={null}
    client.DataSourceStatusProvider.StatusChanged +=
        (sender, status) => {
            Console.WriteLine("new status is: {0}", status);
        };
    ```
  </CodeGroup>
</Accordion>

### C++ (server-side)

<Accordion title="Expand C++ (server-side) code sample">
  The C++ (server-side) SDK exposes some of its internal status to let your application monitor it.

  The SDK has several data source states dependent on its configuration, network connectivity, and calls explicitly setting the client offline or online.

  This table describes the states:

  <table>
    <thead>
      <tr>
        <th>Data source state</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td><code>kInitializing</code></td>
        <td>The initial state of the data source when the SDK is being initialized.</td>
      </tr>

      <tr>
        <td><code>kValid</code></td>
        <td>The data source is currently operational and has not had any problems since the last time it received data.</td>
      </tr>

      <tr>
        <td><code>kInterrupted</code></td>
        <td>The data source encountered an error that it will attempt to recover from.</td>
      </tr>

      <tr>
        <td><code>kSetOffline</code></td>
        <td>The application has told the SDK to stay offline.</td>
      </tr>

      <tr>
        <td><code>kShutdown</code></td>
        <td>The data source has been permanently shut down. This could be because it encountered an unrecoverable error, or because the SDK client was explicitly shut down.</td>
      </tr>
    </tbody>
  </table>

  The SDK stores a timestamp of when the data source state most recently changed. It also stores information about the last error that the data source encountered, if any.

  This table describes the possible errors:

  <table>
    <thead>
      <tr>
        <th><code>LastError</code> value</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td><code>kUnknown</code></td>
        <td>An unexpected error, such as an uncaught exception.</td>
      </tr>

      <tr>
        <td><code>kNetworkError</code></td>
        <td>An I/O error, such as a dropped connection.</td>
      </tr>

      <tr>
        <td><code>kErrorResponse</code></td>
        <td>The LaunchDarkly service returned an HTTP response with an error status.</td>
      </tr>

      <tr>
        <td><code>kInvalidData</code></td>
        <td>The SDK received malformed data from the LaunchDarkly service.</td>
      </tr>

      <tr>
        <td><code>kStoreError</code></td>
        <td>The data source itself is working, but when it tried to put an update into the data store, the data store failed. The SDK may not have the latest data.</td>
      </tr>
    </tbody>
  </table>

  Here's how to monitor the data source state:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    client.DataSourceStatus().OnDataSourceStatusChange([](server_side::DataSourceStatus status) {
      if (status.State() ==
      server_side::DataSourceStatus::
      DataSourceState::kValid) {
        /* Flag data has been received from LaunchDarkly.*/
      }
    });
    ```
  </CodeGroup>

  If you are working in C, there are a few more steps:

  1. Define a callback to receive the data source status change:

  <CodeGroup>
    ```c title="C++ SDK v3.0 (C binding), define callback" lines wrap theme={null}
    void OnDataSourceStatusChanged(LDServerDataSourceStatus status, void* user_data) {
      printf("status: %d\n", LDServerDataSourceStatus_GetState(status));
    }
    ```
  </CodeGroup>

  2. Assign the callback by creating a listener connection:

  <CodeGroup>
    ```c title="C++ SDK v3.0 (C binding), create listener connection" lines wrap theme={null}
    struct LDServerDataSourceStatusListener listener;
    LDServerDataSourceStatusListener_Init(&listener);

    listener.StatusChanged = OnDataSourceStatusChanged;

    /* You may optionally assign the UserData pointer, which will be passed into
    * StatusChanged. */

    /* listener.UserData = &some_struct; */

    LDListenerConnection connection = LDServerSDK_DataSourceStatus_OnStatusChange(client, listener);

    /* You can disconnect the listener later */
    LDListenerConnection_Disconnect(connection);
    ```
  </CodeGroup>

  3. Ensure the connection is freed when you are done with it:

  <CodeGroup>
    ```c title="C++ SDK v3.0 (C binding), free connection" lines wrap theme={null}
    LDListenerConnection_Free(connection);
    ```
  </CodeGroup>

  To learn more, read `DataSourceStatus()` in [`Client`](https://launchdarkly.github.io/cpp-sdks/libs/server-sdk/docs/html/classlaunchdarkly_1_1server__side_1_1Client.html).
</Accordion>

### Go

<Accordion title="Expand Go code sample">
  To check the data source status:

  <CodeGroup>
    ```go title="Go" lines wrap theme={null}
    dataSourceStatus := client.GetDataSourceStatusProvider().GetStatus()
    ```
  </CodeGroup>

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

  <CodeGroup>
    ```go title="Go" lines wrap theme={null}
    dataStoreStatusChannel := client.GetDataStoreStatusProvider().AddStatusListener()
    go func() {
        for status := range dataStoreStatusChannel {
            fmt.Println("new status is: ", status)
        }
    }()
    ```
  </CodeGroup>

  To learn more, read [`GetDataSourceStatusProvider`](https://pkg.go.dev/github.com/launchdarkly/go-server-sdk/v7#LDClient.GetDataSourceStatusProvider) and [`GetDataStoreStatusProvider`](https://pkg.go.dev/github.com/launchdarkly/go-server-sdk/v7#LDClient.GetDataStoreStatusProvider).
</Accordion>

### Java

<Accordion title="Expand Java code sample">
  To check the data source status:

  <CodeGroup>
    ```java title="Java" lines wrap theme={null}
    DataSourceStatusProvider.Status dataSourceStatus =
      client.getDataSourceStatusProvider().getStatus();
    ```
  </CodeGroup>

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

  <CodeGroup>
    ```java title="Java" lines wrap theme={null}
    client.getDataSourceStatusProvider().addStatusListener(
      status -> {
        System.out.println("new status is: " + status);
      }
    );
    ```
  </CodeGroup>

  To learn more, read [`DataSourceStatusProvider`](https://launchdarkly.github.io/java-core/lib/sdk/server/com/launchdarkly/sdk/server/interfaces/DataSourceStatusProvider.html) and [`DataStoreStatusProvider`](https://launchdarkly.github.io/java-core/lib/sdk/server/com/launchdarkly/sdk/server/interfaces/DataStoreStatusProvider.html).
</Accordion>

### Python

<Accordion title="Expand Python code sample">
  To check the data source status:

  <CodeGroup>
    ```python title="Python" lines wrap theme={null}
    status = ldclient.get().data_source_status_provider.status
    ```
  </CodeGroup>

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

  <CodeGroup>
    ```python title="Python" lines wrap theme={null}
    listener = ldclient.get().data_source_status_provider.add_listener(source_status_listener)
    ```
  </CodeGroup>

  To learn more, read [`DataSourceStatusProvider`](https://launchdarkly-python-sdk.readthedocs.io/en/latest/api-extending.html#ldclient.interfaces.DataSourceStatusProvider) and [`DataStoreStatusProvider`](https://launchdarkly-python-sdk.readthedocs.io/en/latest/api-extending.html#ldclient.interfaces.DataStoreStatusProvider).

  If you are working with forking processes, any configuration that you provide to the SDK must survive the forking process independently. We recommend that you add any listeners after a `postfork()` call, unless you are certain they can survive the forking process. To learn more, read [Initialize the client](/docs/sdk/server-side/python#initialize-the-client) and [`ldclient.postfork`](https://launchdarkly-python-sdk.readthedocs.io/en/latest/api-main.html#ldclient.client.LDClient.postfork).
</Accordion>

### Ruby

<Accordion title="Expand Ruby code sample">
  To check the data source status:

  <CodeGroup>
    ```ruby title="Ruby" lines wrap theme={null}
    status = client.data_source_status_provider.status
    ```
  </CodeGroup>

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

  <CodeGroup>
    ```ruby title="Ruby" lines wrap theme={null}
    # listener#update will be called when the status is changed
    client.data_source_status_provider.add_listener(listener);
    ```
  </CodeGroup>

  To learn more, read [`DataSource::StatusProvider`](https://launchdarkly.github.io/ruby-server-sdk/LaunchDarkly/Interfaces/DataSource/StatusProvider.html) and [`DataStore::StatusProvider`](https://launchdarkly.github.io/ruby-server-sdk/LaunchDarkly/Interfaces/DataStore/StatusProvider.html).

  If you are working with forking processes, any configuration that you provide to the SDK must survive the forking process independently. We recommend that you add any listeners after a `postfork()` call, unless you are certain they can survive the forking process. To learn more, read [Initialize the client](/docs/sdk/server-side/ruby#initialize-the-client) and [`postfork`](https://launchdarkly.github.io/ruby-server-sdk/LaunchDarkly/LDClient.html#postfork-instance_method).
</Accordion>
