# Logging APIs in Mobile SDKs


This guide walks you through the logging APIs available in the RudderStack [Android (Kotlin)]({{< ref "sources/event-streams/sdks/kotlin-sdk/" >}}) and [iOS (Swift)]({{< ref "sources/event-streams/sdks/swift-sdk/" >}}) SDKs.

## Overview

The RudderStack Android (Kotlin) and iOS (Swift) SDKs provide a flexible and configurable logging mechanism that helps you:

- Monitor and debug the SDK’s behavior. 
- Get fine-grained control over the amount of information to be recorded by leveraging multiple log levels.
- Set up and use a custom logging mechanism and log your own messages by leveraging the SDK’s logging APIs.

## Set the log level

You can define the log level by passing the value for the `logLevel` parameter. It controls the verbosity of the logs printed by the SDK and helps filter out logs based on their importance.

The Android (Kotlin) and iOS (Swift) SDKs support the following log levels:

| Log level | Description |
| :---| :----|
| `VERBOSE` | Logs all the messages, including detailed internal operations. It is useful for deep debugging. |
| `DEBUG` | Logs detailed information relevant for debugging and omits the additional internal logs. |
| `INFO` | Logs general operational information about the SDK’s execution and helps track high-level flow. |
| `WARN` | Logs potentially problematic situations that may not cause immediate failures. It is useful for detecting any unusual SDK behavior. |
| `ERROR`  | Logs only serious issues that impact the SDK's functionality and require attention. |
| `NONE` | Disables all logging. Use it when logging is unnecessary or needs to be disabled in production. |

The following sections show you how to set a `VERBOSE` log level in Android (Kotlin) and iOS (Swift) SDKs.

### Android (Kotlin)

{{< info >}}
**Instance-based logging in Android (Kotlin) SDK**

Starting from v1.5.0, the Android (Kotlin) SDK only supports [instance-based logging](#via-configuration-recommended). You can configure the `logger` and `logLevel` directly on the `Configuration` object while initializing the SDK — this ties the logger to a specific `Analytics` instance, allowing different instances to use different loggers and log levels.

The older [`LoggerAnalytics` singleton approach](#via-loggeranalytics-deprecated) is deprecated in Android (Kotlin) SDK v1.5.0+, but will continue to work for backward compatibility.
{{< /info >}}

#### Instance-based logging (Recommended)

Pass the `logLevel` parameter when creating the `Configuration` object:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Kotlin" %}}
```kotlin
import com.rudderstack.sdk.kotlin.android.Configuration
import com.rudderstack.sdk.kotlin.core.internals.logger.Logger

val config = Configuration(
    writeKey = WRITE_KEY,
    dataPlaneUrl = DATA_PLANE_URL,
    logLevel = Logger.LogLevel.VERBOSE
)
val analytics = Analytics(config)
```
{{% /tab %}}
{{% tab tabName="Java" %}}
```java
import com.rudderstack.sdk.kotlin.android.javacompat.ConfigurationBuilder;
import com.rudderstack.sdk.kotlin.android.Configuration;
import com.rudderstack.sdk.kotlin.core.internals.logger.Logger;

Configuration config = new ConfigurationBuilder(
    WRITE_KEY,
    DATA_PLANE_URL
)
    .setLogLevel(Logger.LogLevel.VERBOSE)
    .build();
Analytics analytics = new Analytics(config);
```
{{% /tab %}}
{{< /tabs >}}

#### via `LoggerAnalytics` (Deprecated)

{{< warning >}}
This method is deprecated but continues to work for backward compatibility. Use the [instance-based logging](#instance-based-logging-recommended) approach instead.
{{< /warning >}}

{{< tabs tabTotal="2" >}}
{{% tab tabName="Kotlin" %}}
```kotlin
LoggerAnalytics.logLevel = Logger.LogLevel.VERBOSE
```
{{% /tab %}}
{{% tab tabName="Java" %}}
```java
LoggerAnalytics.INSTANCE.setLogLevel(Logger.LogLevel.VERBOSE);
```
{{% /tab %}}
{{< /tabs >}}

### iOS (Swift)

{{< info >}}
The iOS (Swift) SDK only supports the `LoggerAnalytics` singleton approach for logging.
{{< /info >}}

{{< tabs tabTotal="2" >}}
{{% tab tabName="Swift" %}}
```swift
LoggerAnalytics.logLevel = LogLevel.VERBOSE
```
{{% /tab %}}
{{% tab tabName="Objective-C" %}}
```objectivec
[RSSLoggerAnalytics setLogLevel: RSSLogLevelVerbose];
```
{{% /tab %}}
{{< /tabs >}}

## Print custom logs

The Android (Kotlin) and iOS (Swift) SDKs provide a way to print custom logs for the SDK. It is helpful when the SDK logs need to be printed in a [custom plugin]({{< ref "sources/event-streams/sdks/client-side-features/plugin-architecture/_index.md#custom-plugins" >}}).

The SDKs provide the following methods for printing different levels of log messages:

### Android (Kotlin)

The Android (Kotlin) SDK supports the following ways for printing custom logs:

- [In a custom plugin](#in-a-custom-plugin)
- [via `LoggerAnalytics` (Deprecated)](#loggeranalytics-custom-logs)

#### In a custom plugin

Use the `logger` extension property available in the `Plugin` interface — this gives you the logger tied to the `Analytics` instance that the plugin belongs to.

{{< info >}}
The `logger` is only accessible within a custom plugin via the `Plugin.logger` extension property. Direct access to `analytics.logger` outside of a plugin is not part of the public API.

You should access this property only after the plugin’s `setup` method has been invoked, as it depends on the plugin’s `analytics` property being initialized.
{{< /info >}}

{{< tabs tabTotal="2" >}}
{{% tab tabName="Kotlin" %}}
```kotlin
import com.rudderstack.sdk.kotlin.core.internals.plugins.Plugin
import com.rudderstack.sdk.kotlin.core.internals.plugins.logger

class MyCustomPlugin : Plugin {
    override val pluginType: PluginType = PluginType.PreProcess
    override lateinit var analytics: Analytics

    override suspend fun intercept(event: Event): Event {
        logger.verbose("MyCustomPlugin: Processing event (messageId=${event.messageId})")
        logger.debug("MyCustomPlugin: Debug info for event processing")
        logger.info("MyCustomPlugin: Important operational info")
        logger.warn("MyCustomPlugin: Something unexpected happened")
        logger.error("MyCustomPlugin: Something went wrong")
        return event
    }
}
```
{{% /tab %}}
{{% tab tabName="Java" %}}
```java
import com.rudderstack.sdk.kotlin.core.internals.plugins.PluginKt;
import com.rudderstack.sdk.kotlin.core.internals.plugins.Plugin;

public class MyCustomPlugin implements Plugin {
    // After the plugin is added to Analytics, use PluginKt.getLogger(this)
    PluginKt.getLogger(this).verbose("MyCustomPlugin: Processing event");
    PluginKt.getLogger(this).debug("MyCustomPlugin: Debug info for event processing");
    PluginKt.getLogger(this).info("MyCustomPlugin: Important operational info");
    PluginKt.getLogger(this).warn("MyCustomPlugin: Something unexpected happened");
    PluginKt.getLogger(this).error("MyCustomPlugin: Something went wrong", null);
}
```
{{% /tab %}}
{{< /tabs >}}

#### via `LoggerAnalytics` (Deprecated) {#loggeranalytics-custom-logs}

{{< warning >}}
This approach is deprecated but continues to work for backward compatibility. Use the [`logger` extension property](#in-a-custom-plugin) approach instead.
{{< /warning >}}

{{< tabs tabTotal="2" >}}
{{% tab tabName="Android (Kotlin)" %}}
```kotlin
// Verbose log message
LoggerAnalytics.verbose("This is a verbose log message providing detailed internal information.")

// Debug log message
LoggerAnalytics.debug("Debugging info: API request started.")

// Info log message
LoggerAnalytics.info("SDK initialized successfully.")

// Warn log message
LoggerAnalytics.warn("Low memory warning detected.")

// Error log message
LoggerAnalytics.error("Failed to fetch user data: Network error.")  
```
{{% /tab %}}
{{% tab tabName="Java" %}}
```java
// Verbose log message
LoggerAnalytics.INSTANCE.verbose("This is a verbose log message providing detailed internal information.")

// Debug log message
LoggerAnalytics.INSTANCE.verbose("Debugging info: API request started.")

// Info log message
LoggerAnalytics.INSTANCE.verbose("SDK initialized successfully.")

// Warn log message
LoggerAnalytics.INSTANCE.verbose("Low memory warning detected.")

// Error log message
LoggerAnalytics.INSTANCE.verbose("Failed to fetch user data: Network error.") 
```
{{% /tab %}}
{{< /tabs >}}

### iOS (Swift)

{{< tabs tabTotal="2" >}}
{{% tab tabName="Swift" %}}
```swift
// Verbose log message
LoggerAnalytics.verbose(log: "This is a verbose log message providing detailed internal information.")

// Debug log message
LoggerAnalytics.debug(log: "Debugging info: API request started.")

// Info log message
LoggerAnalytics.info(log: "SDK initialized successfully.")

// Warn log message
LoggerAnalytics.warn(log: "Low memory warning detected.")

// Error log message
LoggerAnalytics.error(log: "Failed to fetch user data: Network error.")
```
{{% /tab %}}
{{% tab tabName="Objective-C" %}}
```objectivec
// Verbose log message
[RSSLoggerAnalytics verbose: @"This is a verbose log message providing detailed internal information."];
    
// Debug log message
[RSSLoggerAnalytics debug: @"Debugging info: API request started."];
    
// Info log message
[RSSLoggerAnalytics info: @"SDK initialized successfully."];
    
// Warn log message
[RSSLoggerAnalytics warn: @"Low memory warning detected."];
    
// Error log message
[RSSLoggerAnalytics error:@"Failed to fetch user data: Network error." error:nil];
```
{{% /tab %}}
{{< /tabs >}}

## Use a custom logger

You can create and use a custom logger that is used by the Kotlin and iOS (Swift) SDKs to log messages — this is helpful in cases where you don't want to rely on the SDK's default logger.

### Android (Kotlin)

For the Android (Kotlin) SDK, you can inherit the `Logger` interface to create a custom `Logger` class and then pass its instance to `Configuration` while initializing the SDK.

For example, follow these steps to use the [Timber](https://github.com/JakeWharton/timber) library to create a `Logger` class:

1. Create a custom logger class:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Kotlin" %}}
```kotlin
import com.rudderstack.sdk.kotlin.core.internals.logger.Logger

class MyCustomLogger : Logger {
    private val tag = "MyCustomTag"

    override fun verbose(log: String) {
        Timber.tag(tag).v(log)
    }

    override fun debug(log: String) {
        Timber.tag(tag).d(log)
    }

    override fun info(log: String) {
        Timber.tag(tag).i(log)
    }

    override fun warn(log: String) {
        Timber.tag(tag).w(log)
    }

    override fun error(log: String, throwable: Throwable?) {
        Timber.tag(tag).e(throwable, log)
    }
}
```
{{% /tab %}}
{{% tab tabName="Java" %}}
```java
import androidx.annotation.NonNull;
import com.rudderstack.sdk.kotlin.core.internals.logger.Logger;
public class JavaCustomLogger implements Logger {
    private final String TAG = "MyCustomTag";
    @Override
    public void verbose(@NonNull String log) {
        System.out.println(TAG + ": Verbose: " + log);
    }
    @Override
    public void debug(@NonNull String log) {
        System.out.println(TAG + ": Debug: " + log);
    }
    @Override
    public void info(@NonNull String log) {
        System.out.println(TAG + ": Info: " + log);
    }
    @Override
    public void warn(@NonNull String log) {
        System.out.println(TAG + ": Warn: " + log);
    }
    @Override
    public void error(@NonNull String log, Throwable throwable) {
        System.out.println(TAG + ": Error: " + log);
    }
}
```
{{% /tab %}}
{{< /tabs >}}

2. Pass the custom logger via `Configuration`:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Kotlin" %}}
```kotlin
import com.rudderstack.sdk.kotlin.android.Configuration
import com.rudderstack.sdk.kotlin.core.internals.logger.Logger

val config = Configuration(
    writeKey = WRITE_KEY,
    dataPlaneUrl = DATA_PLANE_URL,
    logger = MyCustomLogger(),
    logLevel = Logger.LogLevel.VERBOSE
)
val analytics = Analytics(config)
```
{{% /tab %}}
{{% tab tabName="Java" %}}
```java
import com.rudderstack.sdk.kotlin.android.Configuration;
import com.rudderstack.sdk.kotlin.android.javacompat.ConfigurationBuilder;
import com.rudderstack.sdk.kotlin.core.internals.logger.Logger;

Configuration config = new ConfigurationBuilder(
    WRITE_KEY,
    DATA_PLANE_URL
)
    .setLogger(new JavaCustomLogger())
    .setLogLevel(Logger.LogLevel.VERBOSE)
    .build();
Analytics analytics = new Analytics(config);
```
{{% /tab %}}
{{< /tabs >}}

### iOS (Swift)

For the iOS (Swift) SDK, you can inherit the `Logger` protocol to create a custom `Logger` class and then pass its instance to the `setLogger` API of `LoggerAnalytics`.

1. Create a custom logger, as shown:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Swift" %}}
```swift
class MyCustomLogger: Logger {
    private let tag = "MyCustomTag"
    
    func verbose(log: String) {
        print("\(tag) :: Verbose :: \(log)")
    }
    
    func debug(log: String) {
        print("\(tag) :: Debug :: \(log)")
    }
    
    func info(log: String) {
        print("\(tag) :: Info :: \(log)")
    }
    
    func warn(log: String) {
        print("\(tag) :: Warn :: \(log)")
    }
    
    func error(log: String, error: (any Error)?) {
        print("\(tag) :: Error :: \(log)")
        if let error {
            print("\(tag) :: Error Details :: \(error)")
        }
    }
}
```
{{% /tab %}}
{{% tab tabName="Objective-C" %}}
```objectivec
@implementation MyCustomLogger {
    NSString *tag;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        tag = @"MyCustomTag";
    }
    return self;
}

- (void)verbose:(NSString *)log {
    NSLog(@"%@ :: Verbose :: %@", tag, log);
}

- (void)debug:(NSString *)log {
    NSLog(@"%@ :: Debug :: %@", tag, log);
}

- (void)info:(NSString *)log {
    NSLog(@"%@ :: Info :: %@", tag, log);
}

- (void)warn:(NSString *)log {
    NSLog(@"%@ :: Warn :: %@", tag, log);
}

- (void)error:(NSString *)log error:(NSError * _Nullable)error {
    NSLog(@"%@ :: Error :: %@", tag, log);
    if (error) {
        NSLog(@"%@ :: Error Details :: %@", tag, error);
    }
}

@end
```
{{% /tab %}}
{{< /tabs >}}

2. Add `MyCustomLogger` to the `LoggerAnalytics` instance, as shown:

{{< tabs tabTotal="2" >}}
{{% tab tabName="Swift" %}}
```swift
LoggerAnalytics.setLogger(MyCustomLogger())
```
{{% /tab %}}
{{% tab tabName="Objective-C" %}}
```objectivec
[RSSLoggerAnalytics setLogger: [MyCustomLogger new]];
```
{{% /tab %}}
{{< /tabs >}}
