Logging APIs in Mobile SDKs
7 minute read
This guide walks you through the logging APIs available in the RudderStack Android (Kotlin) and iOS (Swift) 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)
Instance-based logging in Android (Kotlin) SDK
Starting from v1.5.0, the Android (Kotlin) SDK only supports instance-based logging. You can configure the
loggerandlogLeveldirectly on theConfigurationobject while initializing the SDK — this ties the logger to a specificAnalyticsinstance, allowing different instances to use different loggers and log levels.The older
LoggerAnalyticssingleton approach is deprecated in Android (Kotlin) SDK v1.5.0+, but will continue to work for backward compatibility.
Instance-based logging (Recommended)
Pass the logLevel parameter when creating the Configuration object:
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)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);via LoggerAnalytics (Deprecated)
This method is deprecated but continues to work for backward compatibility. Use the instance-based logging approach instead.
LoggerAnalytics.logLevel = Logger.LogLevel.VERBOSELoggerAnalytics.INSTANCE.setLogLevel(Logger.LogLevel.VERBOSE);iOS (Swift)
The iOS (Swift) SDK only supports theLoggerAnalyticssingleton approach for logging.
LoggerAnalytics.logLevel = LogLevel.VERBOSE[RSSLoggerAnalytics setLogLevel: RSSLogLevelVerbose];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.
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
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.
The
loggeris only accessible within a custom plugin via thePlugin.loggerextension property. Direct access toanalytics.loggeroutside of a plugin is not part of the public API.You should access this property only after the plugin’s
setupmethod has been invoked, as it depends on the plugin’sanalyticsproperty being initialized.
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
}
}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);
}via LoggerAnalytics (Deprecated)
This approach is deprecated but continues to work for backward compatibility. Use theloggerextension property approach instead.
// 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.") // 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.") iOS (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.")// 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];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 library to create a Logger class:
- Create a custom logger class:
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)
}
}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);
}
}- Pass the custom logger via
Configuration:
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)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);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.
- Create a custom logger, as shown:
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)")
}
}
}@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- Add
MyCustomLoggerto theLoggerAnalyticsinstance, as shown:
LoggerAnalytics.setLogger(MyCustomLogger())[RSSLoggerAnalytics setLogger: [MyCustomLogger new]];