> ## 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.

# Logging configuration

<View title="Developer" />

<View title="Federal docs" />

<View title="EU docs" />

This topic explains how to configure the logging feature. Logging is available for client-side, server-side, and edge SDKs.

LaunchDarkly SDKs rely on built-in logging packages and libraries to track events. Depending on which language you use, you can configure logging behavior to be highly or minimally verbose.

* [Client-side SDKs](#client-side-sdks)
* [Server-side SDKs](#server-side-sdks)
* [Edge SDKs](#edge-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)
* [Node.js (client-side)](#node-js-client-side)
* [React Native](#react-native)
* [React Web](#react-web)
* [Roku](#roku)
* [Vue](#javascript): The Vue SDK relies on the JavaScript SDK for this functionality.

### .NET (client-side)

<Accordion title="Expand .NET (client-side) code sample">
  The client-side .NET SDK uses the `Common.Logging` framework. For an example configuration, read the [Common.Logging README](https://github.com/net-commons/common-logging#2-register-and-configure-commonlogging).

  There are two important things to consider before you enable the DEBUG log level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.
</Accordion>

### Android

<Accordion title="Expand Android code sample">
  The Android SDK makes heavy use of Timber logging. Include Timber in your application to enable debug output or production logging. An example is shown below to enable debug output when the application is built with a debug configuration.

  Here's how:

  <CodeGroup>
    ```java title="Java" lines wrap theme={null}
    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    }
    ```

    ```kotlin title="Kotlin" lines wrap theme={null}
    if (BuildConfig.DEBUG) {
        Timber.plant(Timber.DebugTree())
    }
    ```
  </CodeGroup>
</Accordion>

### C++ (client-side)

<Accordion title="Expand C++ (client-side) code sample">
  By default, the SDK uses a console logger. The SDK does not lock on any logging. Ensure that your implementation is thread safe.

  Whether you use a custom logger or the default logger, any logging configuration must be done using `LoggingBuilder` before the client is initialized. You cannot modify logging while the client is running.

  If you use the default logger, you can configure how verbose it should be. The options are debug, info, warn, and error.

  Here is an example:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    auto config_builder = client_side::ConfigBuilder("example-mobile-key");
    config_builder.Logging()
      .Logging(LoggingBuilder::BasicLogging().Tag("ArbitraryLogTag").Level(LogLevel::kWarn));
    auto config = config_builder.Build();
    ```

    ```c title="C++ SDK v3.0 (C binding)" lines wrap theme={null}
    LDClientConfigBuilder config_builder = LDClientConfigBuilder_New("example-mobile-key");

    LDLoggingBasicBuilder basic_logging = LDLoggingBasicBuilder_New();
    LDLoggingBasicBuilder_Tag(basic_logging, "ArbitraryLogTag");
    LDLoggingBasicBuilder_Level(basic_logging, LD_LOG_WARN);

    LDClientConfigBuilder_Logging_Basic(config_builder, basic_logging);

    LDClientConfig config;
    LDStatus status = LDClientConfigBuilder_Build(config_builder, &config);
    ```
  </CodeGroup>

  You can also use your own custom log function.

  First, implement the log backend interface. For C++, this means implementing the `ILogBackend` interface. If you are working with the C binding, define the callback functions to implement the log backend interface.

  Here's how:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    #include <launchdarkly/logging/log_level.hpp>
    #include <launchdarkly/logging/log_backend.hpp>

    using namespace launchdarkly;

    class CustomLogger : public ILogBackend {
       public:
        /* Should return true if the specified level is enabled; in this example, return true to log all messages. */
        bool Enabled(LogLevel level) noexcept override { return true; }

        /* Forwards to stdout as an example, printing the log tag along with the message. */
        void Write(LogLevel level, std::string message) noexcept override {
            std::cout << GetLogLevelName(level, "unknown") << ": " << message << std::endl;
    }
    };
    ```

    ```c title="C++ SDK v3.0 (C binding)" lines wrap theme={null}

    /* Should return true if the specified level is enabled; in this example, return true to log all messages. */
    static bool enabled(enum LDLogLevel level, void *user_data) {
        return true;
    }

    /* Forwards to stdout as an example, printing the log tag along with the message. */
    static void write(enum LDLogLevel level, const char *msg, void *user_data) {
        printf("%d: %s\n", level, msg);
    }
    ```
  </CodeGroup>

  Then, install the custom logger in the SDK's config:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    // Make sure the <memory> header is included for std::make_shared
    #include <memory>

    auto config_builder = client_side::ConfigBuilder("example-mobile-key");
    config_builder.Logging()
      .Logging(LoggingBuilder::CustomLogging().Backend(std::make_shared<CustomLogger>()));
    auto config = config_builder.Build();
    ```

    ```c title="C++ SDK v3.0 (C binding)" expandable lines wrap theme={null}
    struct LDLogBackend backend;
    LDLogBackend_Init(&backend);

    backend.Write = write;
    backend.Enabled = enabled;

    /* You may optionally assign the UserData pointer, which will be passed into Write and Enabled. */
    /* backend.UserData = &some_struct; */

    LDLoggingCustomBuilder custom_logging = LDLoggingCustomBuilder_New();
    LDLoggingCustomBuilder_Backend(custom_logging, backend);

    LDClientConfigBuilder builder = LDClientConfigBuilder_New("example-mobile-key");
    LDClientConfigBuilder_Logging_Custom(builder, custom_logging);

    LDClientConfig config;
    LDStatus status = LDClientConfigBuilder_Build(builder, &config);
    ```
  </CodeGroup>

  It's also possible to disable the SDK's logging:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    auto config_builder = client_side::ConfigBuilder("example-mobile-key");
    config_builder.Logging().Logging(LoggingBuilder::NoLogging());

    auto config = config_builder.Build();
    ```

    ```c title="C++ SDK v3.0 (C binding)" lines wrap theme={null}
    LDClientConfigBuilder builder = LDClientConfigBuilder_New("example-mobile-key");

    LDClientConfigBuilder_Logging_Disable(builder);

    LDClientConfig config;
    LDStatus status = LDClientConfigBuilder_Build(builder, &config);
    ```
  </CodeGroup>
</Accordion>

### Electron

<Accordion title="Expand Electron code sample">
  By default, the SDK uses the [`winston`](https://www.npmjs.com/package/winston) package. There are four logging levels: `debug`, `info`, `warn`, and `error`. By default, `debug` messages are hidden.

  To change the logging configuration, you can set `LDOptions.logger` to either another Winston instance or any object that implements the `LDLogger` interface. The `createConsoleLogger` function creates a minimal logger.
</Accordion>

### Flutter

<Accordion title="Expand Flutter code sample">
  The Flutter SDK supports logging in version 4 and later.

  There are four logging levels in the Flutter SDK: `debug`, `info`, `warn`, and `error`. You can also set the level to `none` to disable all logging.

  By default, the SDK logs at the `info` level with a tag of "LaunchDarkly." To change the logging, construct an `LDLogger` and include it in your `LDConfig`. You can change the log level, tag, and output destinations when you construct the logger.

  Here's how:

  <CodeGroup>
    ```dart title="Flutter SDK v4" lines wrap theme={null}
    final logger = LDLogger(level: LDLogLevel.warn);

    final config = LDConfig(
      CredentialSource.fromEnvironment(),
      AutoEnvAttributes.enabled,
      logger: logger,
    );
    ```
  </CodeGroup>

  Optionally, you can create a custom logging implementation. Your implementation should use the `LDLogAdapter` interface.

  To learn more, read [`LDLogger`](https://pub.dev/documentation/launchdarkly_flutter_client_sdk/latest/launchdarkly_flutter_client_sdk/LDLogger-class.html).
</Accordion>

### iOS

<Accordion title="Expand iOS code sample">
  The iOS SDK emits log messages using calls to [`os_log`](https://developer.apple.com/documentation/os/os_log). The SDK provides a default logger `OSLog(subsystem: "com.launchdarkly", category: "ios-client-sdk")`. This logger can be overridden using the `LDConfig.logger` property.

  Here's how:

  <CodeGroup>
    ```swift title="iOS SDK v9.x (Swift)" lines wrap theme={null}
    import OSLog

    var config = LDConfig(mobileKey: "example-mobile-key", autoEnvAttributes: .enabled)
    config.logger = OSLog(subsystem: "your.preferred.subsystem", category: "ld-sdk")

    // You can disable all SDK logging by setting this property to the shared disabled logger
    config.logger = .disabled
    ```

    ```objectivec title="iOS SDK v9.x (Objective-C)" lines wrap theme={null}
    @import OSLog;

    LDConfig *config = [[LDConfig alloc] initWithMobileKey:@"example-mobile-key" autoEnvAttributes:AutoEnvAttributesEnabled];
    config.logger = os_log_create("your.preferred.subsystem", "ld-sdk");

    // You can disable all SDK logging by setting this property to the shared disabled logger
    config.logger = OS_LOG_DISABLED;
    ```
  </CodeGroup>
</Accordion>

### JavaScript

<Accordion title="Expand JavaScript code sample">
  In the default configuration, the SDK sends the output to the console and enables all log levels except `debug`.

  To change the logging configuration, set `LDOptions.logger` to any object that implements the `LDLogger` interface and performs logging for the client. To learn more, read [LDOptions](https://launchdarkly.github.io/js-client-sdk/interfaces/LDOptions.html) and [LDLogger](https://launchdarkly.github.io/js-client-sdk/interfaces/LDLogger.html) in the SDK API docs.
</Accordion>

### Node.js (client-side)

<Accordion title="Expand Node.js (client-side) code sample">
  The client-side Node.js SDK offers several choices for logging.

  In the default configuration, the SDK sends output to the console and enables all log levels except `debug`.

  Versions 1.x of the SDK used the [`winston`](https://www.npmjs.com/package/winston) logging package for the default configuration. Versions 2.0 and later do not have a dependency on `winston`. Instead, they write directly to the console by default.

  For full control over logging behavior, you can set the [`logger`](https://launchdarkly.github.io/node-client-sdk/interfaces/LDOptions.html#logger) option to an object that implements the `LDLogger` interface. To learn more about the logger's requirements and methods, read [LDLogger](https://launchdarkly.github.io/node-client-sdk/interfaces/LDLogger.html). The `winston` package is compatible with this interface, so if you are already using a `winston` logger, you can pass it directly to the SDK.

  You can use [`basicLogger`](https://launchdarkly.github.io/node-client-sdk/functions/basicLogger.html) for simpler control over logging behavior. Versions 2.x and earlier of the SDK do not support `basicLogger`.

  This example shows how to use `basicLogger` to enable debug-level logging in your SDK configuration:

  <CodeGroup>
    ```javascript title="JavaScript" lines wrap theme={null}
    const LaunchDarkly = require('launchdarkly-node-client-sdk');

    const options = {
      logger: LaunchDarkly.basicLogger({
        level: 'debug',
      }),
    };

    const client = LaunchDarkly.initialize( 'example-client-side-id', user, options);

    ```

    ```ts title="TypeScript" lines wrap theme={null}
    import * as LaunchDarkly from 'launchdarkly-node-client-sdk';

    const logger: LaunchDarkly.LDLogger = LaunchDarkly.basicLogger({ level: 'debug' });
    const options: LaunchDarkly.LDOptions = { logger: logger };

    const client = LaunchDarkly.initialize( 'example-client-side-id', user, options);

    ```
  </CodeGroup>

  Be aware of two considerations when enabling debug-level logging:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly users you create when you use this SDK.
</Accordion>

### React Native

<Accordion title="Expand React Native code sample">
  By default, the React Native SDK uses [`BasicLogger`](https://launchdarkly.github.io/js-core/packages/sdk/react-native/docs/classes/platform-1.BasicLogger.html). This sends output to the console, with a default log level of `info`.

  You can use `BasicLogger` to make some changes to logging behavior. For full control over logging behavior, you can set the `LDOptions.logger` option to an object that implements the `LDLogger` interface and performs logging for the client.

  This example shows how to use `BasicLogger` to enable debug-level logging in your SDK configuration:

  <CodeGroup>
    ```ts title="React Native SDK v10" lines wrap theme={null}
    import {
      AutoEnvAttributes,
      BasicLogger,
      type LDOptions,
      ReactNativeLDClient,
    } from '@launchdarkly/react-native-client-sdk';

    const options: LDOptions = {
      logger: new BasicLogger({
        level: 'debug',
        destination: console.log,
      }),
    };
    const featureClient = new ReactNativeLDClient('example-mobile-key', AutoEnvAttributes.Enabled, options);
    ```
  </CodeGroup>

  To learn more, read [`LDOptions`](https://launchdarkly.github.io/js-core/packages/sdk/react-native/docs/index.html#LDOptions) and [`LDLogger`](https://launchdarkly.github.io/js-core/packages/sdk/react-native/docs/interfaces/platform-1.LDLogger.html).
</Accordion>

### React Web

<Accordion title="Expand React Web code sample">
  The React Web SDK relies on the JavaScript SDK for logging-related functionality. In the default configuration, the SDK sends the output to the console and enables all log levels except `debug`.

  To change the logging configuration, set `LDOptions.logger` to any object that implements the `LDLogger` interface and performs logging for the client. The `basicLogger`, available in the JavaScript SDK, produces such an object.

  Here's how:

  <CodeGroup>
    ```javascript title="JavaScript" lines wrap theme={null}
    import { basicLogger, withLDProvider } from 'launchdarkly-react-client-sdk';

    export default withLDProvider({
      clientSideID: 'example-client-side-id',
      options: {
        logger: basicLogger({level: 'debug'})
      }
    })(App);
    ```
  </CodeGroup>

  To learn more, read [LDOptions](https://launchdarkly.github.io/js-client-sdk/interfaces/LDOptions.html) and [LDLogger](https://launchdarkly.github.io/js-client-sdk/interfaces/LDLogger.html) in the JavaScript SDK API docs.
</Accordion>

### Roku

<Accordion title="Expand Roku code sample">
  The Roku SDK's logging feature is configurable. You can set different log levels, or use a custom logging backend with one of the two supported custom loggers.

  Those loggers are:

  * [Legacy API custom logger for the Roku SDK](#legacy-api-custom-logger-for-the-roku-sdk)
  * [SceneGraph API custom logger for the Roku SDK](#scenegraph-api-custom-logger-for-the-roku-sdk)

  #### Log levels in the Roku SDK

  You can configure the SDK log level. For example, to set the level to `info`:

  <CodeGroup>
    ```brightscript title="BrightScript" lines wrap theme={null}
    config.setLogLevel(LaunchDarklyLogLevels().info)

    ```
  </CodeGroup>

  The SDK supports the following Levels:

  <CodeGroup>
    ```brightscript title="BrightScript" lines wrap theme={null}
    LaunchDarklyLogLevels().error
    LaunchDarklyLogLevels().warn
    LaunchDarklyLogLevels().info
    LaunchDarklyLogLevels().debug
    LaunchDarklyLogLevels().none
    ```
  </CodeGroup>

  #### Legacy API custom logger for the Roku SDK

  Here is an example of the legacy API custom logger:

  <CodeGroup>
    ```brightscript title="Legacy custom logger (BrightScript)" lines wrap theme={null}
    function CustomLogger() as Object
        return {
            log: function(level as Integer, message as String)

                print level message
            end function
        }
    end function

    config.setLogger(CustomLogger())

    ```
  </CodeGroup>

  #### SceneGraph API custom logger for the Roku SDK

  Here is an example of the SceneGraph API custom logger:

  <CodeGroup>
    ```xml title="CustomLogger.xml" lines wrap theme={null}
    <!-- /components/CustomLogger.xml -->

    <component name="CustomLogger" extends="Task">
        <interface>
            <field id="log" type="assocarray" alwaysNotify="true"/>
        </interface>

        <script type="text/brightscript" uri="pkg:/components/CustomLogger.brs"/>
    </component>
    ```

    ```brightscript title="CustomLogger.brs" expandable lines wrap theme={null}
    ' /components/CustomLogger.brs

    function init()

        m.messagePort = createObject("roMessagePort")


        m.top.observeField("log", m.messagePort)

    end function

    function mainThread() as Void
        while (true)

            msg = wait(0, m.messagePort)


            if type(msg) = "roSGNodeEvent" then
                if msg.getField() = "log" then
                    value = msg.getData()

                    print value.level value.message
                end if
            end if
        end while
    end function
    ```
  </CodeGroup>

  To use the logger, create the SceneGraph logger node, and then:

  <CodeGroup>
    ```brightscript title="BrightScript" lines wrap theme={null}
    config.setLoggerNode(myLoggerNode)

    ```
  </CodeGroup>
</Accordion>

### Vue

The Vue SDK relies on the [JavaScript SDK](#javascript) for logging.

## Server-side SDKs

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)
* [Lua](#lua)
* [Node.js (server-side)](#node-js-server-side)
* [PHP](#php)
* [Python](#python)
* [Ruby](#ruby)
* [Rust](#rust)

### .NET (server-side)

<Accordion title="Expand .NET (server-side) code sample">
  The .NET SDK has four logging levels: `Debug`, `Info`, `Warn`, and `Error`. By default, the lowest enabled level is `Info`, so `Debug` messages are hidden. There are two important things to consider if you enable the `Debug` level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.

  The .NET SDK sends log output to `Console.Error` by default. The [`ConfigurationBuilder.Logging`](https://launchdarkly.github.io/dotnet-core/pkgs/sdk/server/api/LaunchDarkly.Sdk.Server.ConfigurationBuilder.html#LaunchDarkly_Sdk_Server_ConfigurationBuilder_Logging_) method and the [`LaunchDarkly.Logging`](https://launchdarkly.github.io/dotnet-logging/) API allow you to change the output destination and log level.

  Here is an example:

  <CodeGroup>
    ```csharp title="C#" lines wrap theme={null}
    using LaunchDarkly.Logging;
    using LaunchDarkly.Sdk.Server;

    var config = Configuration.Builder("YOUR_SDK_KEY")
        .Logging(
            Components.Logging(Logs.ToWriter(Console.Out)).Level(LogLevel.Debug)
        )
        .Build();
    ```
  </CodeGroup>

  The destination could be another logging framework, such as the .NET Core `Microsoft.Extensions.Logging` API in this example:

  <CodeGroup>
    ```csharp title="C#" lines wrap theme={null}
    using LaunchDarkly.Logging;
    using LaunchDarkly.Sdk.Server;
    using Microsoft.Extensions.Logging;

    // Pass an ILoggerFactory, for example from ASP.NET Core dependency injection
    var config = Configuration.Builder("YOUR_SDK_KEY")
        .Logging(Logs.CoreLogging(loggerFactory))
        .Build();
    ```
  </CodeGroup>

  To learn more about logging configuration and adapters for other logging frameworks, read the documentation for [`LaunchDarkly.Logging`](https://launchdarkly.github.io/dotnet-logging/).

  All log messages from the SDK are tagged with a logger name, indicating the category of messages. If you use a logging framework like `Microsoft.Extensions.Logging` or NLog, you can use these names to filter the output:

  * `LaunchDarkly.Sdk`: general messages about the operation of the SDK client.
  * `LaunchDarkly.Sdk.DataSource`: messages about how the SDK client receives feature flag data, such as if the connection to LaunchDarkly has been interrupted.
  * `LaunchDarkly.Sdk.DataStore` (or more specific names like `LaunchDarkly.Sdk.DataStore.Redis`): messages about how the SDK client stores feature flag data, such as if you are using a database integration.
  * `LaunchDarkly.Sdk.Evaluation`: messages about unusual conditions during feature flag evaluation, such as if a feature flag could not be evaluated because its configuration is invalid.
  * `LaunchDarkly.Sdk.Events`: messages about analytics events, such as if event data could not be sent to LaunchDarkly due to a network problem.

  Before version 6.0, the .NET SDK had different logging behavior:

  * The mechanism for specifying a log destination was the [`Common.Logging`](https://github.com/net-commons/common-logging#2-register-and-configure-commonlogging) framework.
  * If you did not specifically configure a log destination using `Common.Logging`, logging was disabled by default.
  * The main logger name was `LaunchDarkly.Client.LdClient`.
</Accordion>

### C++ (server-side)

<Accordion title="Expand C++ (server-side) code sample">
  By default, the SDK uses a console logger. The SDK does not lock on any logging. Ensure that your implementation is thread safe.

  Whether you use a custom logger or the default logger, any logging configuration must be done using `LoggingBuilder` before the client is initialized. You cannot modify logging while the client is running.

  If you use the default logger, you can configure how verbose it should be. The options are debug, info, warn, and error.

  Here is an example:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    auto config_builder = server_side::ConfigBuilder("YOUR_SDK_KEY");

    using LoggingBuilder = server_side::config::builders::LoggingBuilder;
    config_builder.Logging().Logging(
      LoggingBuilder::BasicLogging().Tag("ArbitraryLogTag").Level(LogLevel::kWarn)
    );
    ```

    ```c title="C++ SDK v3.0 (C binding)" lines wrap theme={null}
    LDServerConfigBuilder config_builder = LDServerConfigBuilder_New("YOUR_SDK_KEY");

    LDLoggingBasicBuilder basic_logging = LDLoggingBasicBuilder_New();
    LDLoggingBasicBuilder_Tag(basic_logging, "ArbitraryLogTag");
    LDLoggingBasicBuilder_Level(basic_logging, LD_LOG_WARN);

    LDServerConfigBuilder_Logging_Basic(config_builder, basic_logging);

    LDServerConfig config;
    LDStatus status = LDServerConfigBuilder_Build(config_builder, &config);
    ```
  </CodeGroup>

  You can also use your own custom log function.

  First, implement the log backend interface. For C++, this means implementing the `ILogBackend` interface. If you are working with the C binding, define the callback functions to implement the log backend interface.

  Here's how:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    #include <launchdarkly/logging/log_level.hpp>
    #include <launchdarkly/logging/log_backend.hpp>

    using namespace launchdarkly;

    class CustomLogger : public ILogBackend {
       public:
        /* Should return true if the specified level is enabled; in this example, return true to log all messages. */
        bool Enabled(LogLevel level) noexcept override { return true; }

        /* Forwards to stdout as an example, printing the log tag along with the message. */
        void Write(LogLevel level, std::string message) noexcept override {
            std::cout << GetLogLevelName(level, "unknown") << ": " << message << std::endl;
    }
    };
    ```

    ```c title="C++ SDK v3.0 (C binding)" lines wrap theme={null}

    /* Should return true if the specified level is enabled; in this example, return true to log all messages. */
    static bool enabled(enum LDLogLevel level, void *user_data) {
        return true;
    }

    /* Forwards to stdout as an example, printing the log tag along with the message. */
    static void write(enum LDLogLevel level, const char *msg, void *user_data) {
        printf("%d: %s\n", level, msg);
    }
    ```
  </CodeGroup>

  Then, install the custom logger in the SDK's config:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}
    // Make sure the <memory> header is included for std::make_shared
    #include <memory>

    auto config_builder = server_side::ConfigBuilder("YOUR_SDK_KEY");

    config_builder.Logging().Logging(LoggingBuilder::CustomLogging().Backend(
      std::make_shared<CustomLogger>()));

    auto config = config_builder.Build();
    ```

    ```c title="C++ SDK v3.0 (C binding)" expandable lines wrap theme={null}
    struct LDLogBackend backend;
    LDLogBackend_Init(&backend);

    backend.Write = write;
    backend.Enabled = enabled;

    /* You may optionally assign the UserData pointer, which will be passed into Write and Enabled. */
    /* backend.UserData = &some_struct; */

    LDLoggingCustomBuilder custom_logging = LDLoggingCustomBuilder_New();
    LDLoggingCustomBuilder_Backend(custom_logging, backend);

    LDServerConfigBuilder builder = LDServerConfigBuilder_New("YOUR_SDK_KEY");
    LDServerConfigBuilder_Logging_Custom(builder, custom_logging);

    LDServerConfig config;
    LDStatus status = LDServerConfigBuilder_Build(builder, &config);
    ```
  </CodeGroup>

  It's also possible to disable the SDK's logging:

  <CodeGroup>
    ```cpp title="C++ SDK v3.0 (native)" lines wrap theme={null}

    auto config_builder = server_side::ConfigBuilder("YOUR_SDK_KEY");

    using LoggingBuilder = server_side::config::builders::LoggingBuilder;
    config_builder.Logging().Logging(
      LoggingBuilder::NoLogging()
    );

    auto config = config_builder.Build();
    ```

    ```c title="C++ SDK v3.0 (C binding)" lines wrap theme={null}
    LDServerConfigBuilder builder = LDServerConfigBuilder_New("YOUR_SDK_KEY");

    LDServerConfigBuilder_Logging_Disable(builder);

    LDServerConfig config;
    LDStatus status = LDServerConfigBuilder_Build(builder, &config);
    ```
  </CodeGroup>
</Accordion>

### Go

<Accordion title="Expand Go code sample">
  The Go SDK uses a logging abstraction that can write to a [`log.Logger`](https://pkg.go.dev/log#Logger) or anything with a compatible interface. This adds a system of log levels similar to logging frameworks on other platforms. There are four logging levels: `Debug`, `Info`, `Warn`, and `Error`.

  By default, all levels of messages are enabled except `Debug`. You can tell the SDK to enable more or fewer levels, to send the output to a different destination, or to disable logging.

  Here's how:

  <CodeGroup>
    ```go title="Go SDK v6+" expandable lines wrap theme={null}
    import (
        "log"
        "os"
        ldlog "github.com/launchdarkly/go-sdk-common/v3/ldlog"
        ld "github.com/launchdarkly/go-server-sdk/v6"
        "github.com/launchdarkly/go-server-sdk/v6/ldcomponents"
    )

    var config ld.Config

    loggers := ldlog.NewDefaultLoggers()

    // Send output to a file
    file, _ := os.Create("app.log")
    loggers.SetBaseLogger(log.New(file, "", log.LstdFlags))

    config.Logging = ldcomponents.Logging().
        Loggers(loggers).
        MinLevel(ldlog.Warn) // Change minimum level to Warn (Debug and Info are disabled)

    // Or, disable logging
    config.Logging = ldcomponents.NoLogging()
    ```
  </CodeGroup>

  There are two things to consider if you enable the `Debug` log level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.
</Accordion>

### Java

<Accordion title="Expand Java code sample">
  In version 5.10.0 and higher, the Java SDK allows you to specify a destination for logging using the [`LDConfig.Builder.logging`](https://launchdarkly.github.io/java-core/lib/sdk/server/com/launchdarkly/sdk/server/LDConfig.Builder.html#logging-com.launchdarkly.sdk.server.subsystems.ComponentConfigurer-) method and the [`com.launchdarkly.logging`](https://github.com/launchdarkly/java-logging/) API.

  If you do not specify a destination, the default behavior depends on the version of the SDK:

  * In versions 5.10.0 and earlier, the default destination is [SLF4J](https://www.slf4j.org/). SLF4J has its own configuration mechanisms for determining where output will go, and for filtering by level and/or logger name. It will not generate any output unless you have provided a configuration. For an example of using SLF4J with a simple console logging configuration, visit an SLF4J-specific version of [hello-java](https://github.com/launchdarkly/hello-java/tree/slf4j-logging).
  * Starting with version 6.0.0, the SDK does not require SLF4J. Instead, it detects whether the application is already using SLF4J. If the SLF4J classes are present in the classpath, then it sends log output to SLF4J by default. If the SLF4J classes are not present in the classpath, then it sends log output to `System.err` by default.

  Here is an example of configuring log output to go to the standard output stream (`System.out`), and enabling `DEBUG` level:

  <CodeGroup>
    ```java title="Java SDK v5.10.x and later" lines wrap theme={null}
    import com.launchdarkly.logging.*;
    import com.launchdarkly.sdk.server.*;

    LDConfig config = new LDConfig.Builder()
      .logging(
        Components.logging(Logs.toStream(System.out)).level(LDLogLevel.DEBUG)
      )
      .build();
    ```
  </CodeGroup>

  To learn more about logging configuration and adapters for other logging frameworks, read the documentation for [`com.launchdarkly.logging`](https://github.com/launchdarkly/java-logging).

  All log messages from the SDK are tagged with a logger name, indicating the category of messages. If you use a logging framework like SLF4J, you can use these names to filter the output:

  * `com.launchdarkly.sdk.server.LDClient`: This is for general messages that do not fall into any other categories.
  * `com.launchdarkly.sdk.server.LDClient.DataSource`: This is for messages related to how the SDK obtains feature flag data. Usually, this means messages about the streaming connection to LaunchDarkly, but if you use polling mode or file data instead, the SDK logs those messages under this name.
  * `com.launchdarkly.sdk.server.LDClient.DataStore`: This is for messages related to how feature flag data is stored. For example, database errors appear here if you are using a database integration.
  * `com.launchdarkly.sdk.server.LDClient.Evaluation`: This is for messages related to feature flag evaluation.
  * `com.launchdarkly.sdk.server.LDClient.Events`: This is for messages related to analytics event processing.

  In versions of the SDK before 5.0, logger names were not standardized and were sometimes the names of Java classes that are not part of the public API, but they consistently had a package prefix of either `com.launchdarkly.client.` or `com.launchdarkly.eventsource.`

  There are two important things to consider before you enable the `DEBUG` log level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.
</Accordion>

### Lua

<Accordion title="Expand Lua code sample">
  The SDK configures a default logger if not otherwise specified. You may modify the behavior of the default logger by specifying a custom log tag and minimum log level, or you may completely replace it with a custom logging implementation.

  The SDK does not lock on any logging, so ensure that your custom implementation is thread safe.

  You cannot modify logging while the client is running.

  If you use the default logger, you can configure how verbose it should be. The options are 'debug', 'info', 'warn', and 'error'.

  Here is an example:

  <CodeGroup>
    ```lua title="Lua SDK v2" lines wrap theme={null}
    local config = {
        logging = {
            basic = {
                tag = "launchdarkly",
                level = "warn"
            }
        }
    }
    ```
  </CodeGroup>

  You can also use a custom logger. Here's how:

  <CodeGroup>
    ```lua title="Lua SDK v2" expandable lines wrap theme={null}
    local logger = ld.makeLogBackend(
        function(level)
            -- Log everything.
            return true
        end,
        function(level, message)
            -- Prints in the format: '[level] hello world'
            print(string.format("[%s] %s", level, message))
        end
    )

    local config = {
        logging = {
            custom = logger
        }
    }
    ```
  </CodeGroup>
</Accordion>

### Node.js (server-side)

<Accordion title="Expand Node.js (server-side) code sample">
  The Node.js SDK offers several choices for logging.

  In the default configuration, the SDK sends output to the console and enables all log levels except `debug`.

  Pre-6.0 versions of the SDK used the [`winston`](https://www.npmjs.com/package/winston) logging package for the default configuration. Versions 6.0 and later write directly to the console by default.

  For full control over logging behavior, you can set the [`logger`](https://launchdarkly.github.io/js-core/packages/sdk/server-node/docs/interfaces/LDOptions.html#logger) option to an object that implements the `LDLogger` interface. To learn more about the logger's requirements and methods, read [LDLogger](https://launchdarkly.github.io/js-core/packages/sdk/server-node/docs/interfaces/platform-1.LDLogger.html).

  You can use [`basicLogger`](https://launchdarkly.github.io/js-core/packages/sdk/server-node/docs/functions/basicLogger-1.html) for simpler control over logging behavior. Versions 5.x and earlier of the SDK do not support `basicLogger`.

  This example shows how to use `basicLogger` to enable debug-level logging in your SDK configuration:

  <CodeGroup>
    ```ts title="Node.js SDK v8.x+ (TypeScript)" lines wrap theme={null}
    import { LDOptions, LDLogger, basicLogger } from '@launchdarkly/node-server-sdk';

    const logger: LDLogger = basicLogger({
      level: 'debug',
      destination: console.log,
    });

    const options: LDOptions = { logger: logger };
    ```
  </CodeGroup>

  There are two important things to consider before you enable debug-level logging:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly users you create when you use this SDK.
</Accordion>

### PHP

<Accordion title="Expand PHP code sample">
  The PHP SDK uses [Monolog](https://github.com/Seldaek/monolog). All loggers are namespaced under `LaunchDarkly`.

  There are two important things to consider before you enable the DEBUG log level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.

  You can pass a custom logger to the SDK by using the configurable `logger` property:

  <CodeGroup>
    ```php title="PHP" lines wrap theme={null}
    $client = new LaunchDarkly\LDClient("YOUR_SDK_KEY", ["logger" => new Logger("LaunchDarkly", [new ErrorLogHandler(0, Level::Debug)])]);
    ```
  </CodeGroup>
</Accordion>

### Python

<Accordion title="Expand Python code sample">
  The Python SDK uses Python's built-in [logging library](https://docs.python.org/2/library/logging.html).

  Here's how to enable the debug log level:

  <CodeGroup>
    ```python title="Python" lines wrap theme={null}
    ld_logger = logging.getLogger("ldclient")
    ld_logger.setLevel(logging.DEBUG)
    ```
  </CodeGroup>

  There are two important things to consider before you enable the debug log level:

  * Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  * Debug-level logs include sensitive information, including LaunchDarkly users you create when you use this SDK.
</Accordion>

### Ruby

<Accordion title="Expand Ruby code sample">
  The Ruby SDK uses Ruby's built-in [Logger class](https://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html). All loggers are namespaced under `[LDClient]`.

  There are two important things to consider before you enable the DEBUG log level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.

     You can pass a custom logger to the SDK by using the configurable `logger` property:

  <CodeGroup>
    ```ruby title="Ruby" lines wrap theme={null}
    log = ::Logger.new($stdout)
    log.level = ::Logger::DEBUG
    config = LaunchDarkly::Config.new({logger: log})
    client = LaunchDarkly::LDClient.new("YOUR_SDK_KEY", config)
    ```
  </CodeGroup>
</Accordion>

### Rust

<Accordion title="Expand Rust code sample">
  The Rust SDK uses the [log](https://docs.rs/log/latest/log/) crate.

  There are two important things to consider before you enable the DEBUG log level:

  1. Debug-level logs can be very verbose. We do not recommend using debug logging in high-volume environments.
  2. Debug-level logs include sensitive information, including LaunchDarkly contexts you create when you use this SDK.
</Accordion>

## Edge SDKs

This feature is available in the following edge SDKs:

* [Cloudflare](#cloudflare)
* [Fastly](#fastly)
* [Vercel](#vercel)

### Cloudflare

<Accordion title="Expand Cloudflare code sample">
  In the default configuration, the SDK sends output to the console and enables all log levels except debug.

  For full control over logging behavior, you can set the logger option to an object that implements the LDLogger interface. To learn more about the logger's requirements and methods, read [LDLogger](https://launchdarkly.github.io/js-core/packages/sdk/cloudflare/docs/interfaces/LDLogger.html).

  This example shows how to use BasicLogger to enable debug-level logging in your SDK configuration:

  <CodeGroup>
    ```typescript title="TypeScript" lines wrap theme={null}
    import { BasicLogger, LDOptions } from '@launchdarkly/cloudflare-server-sdk';

    const options: LDOptions = {
      logger: new BasicLogger({ level: 'debug', }),
    };
    ```
  </CodeGroup>
</Accordion>

### Fastly

<Accordion title="Expand Fastly code sample">
  In the default configuration, the SDK sends output to the console and enables all log levels except debug.

  This example shows how to use BasicLogger to enable debug-level logging in your SDK configuration:

  <CodeGroup>
    ```typescript title="TypeScript" lines wrap theme={null}
    import { basicLogger, LDOptions } from '@launchdarkly/fastly-server-sdk';

    const options: LDOptions = {
      logger: basicLogger({ level: 'debug' }),
    };
    ```
  </CodeGroup>

  To learn more, read [`BasicLogger`](https://launchdarkly.github.io/js-core/packages/sdk/fastly/docs/classes/BasicLogger.html).
</Accordion>

### Vercel

<Accordion title="Expand Vercel code sample">
  In the default configuration, the SDK sends output to the console and enables all log levels except debug.

  This example shows how to use BasicLogger to enable debug-level logging in your SDK configuration:

  <CodeGroup>
    ```typescript title="TypeScript" lines wrap theme={null}
    import { BasicLogger, LDOptions } from '@launchdarkly/vercel-server-sdk';

    const options: LDOptions = {
      logger: new BasicLogger({ level: 'debug', }),
    };
    ```
  </CodeGroup>

  For full control over logging behavior, you can set the logger option to an object that implements the `LDLogger` interface. To learn more about the logger's requirements and methods, read [`LDLogger`](https://launchdarkly.github.io/js-core/packages/sdk/vercel/docs/interfaces/platform-1.LDLogger.html).
</Accordion>
