Logging

Configure log levels, privacy settings, and custom log handlers to control how the Airship SDK logs messages.

The Airship SDK provides configurable log levels to help you debug issues without overwhelming the console. If you don’t configure logging, the SDK uses Info for development builds and Error for production builds with private privacy level.

Log levels

The log level acts as a minimum threshold—only logs at that level and higher will be logged. Available log levels, ordered from most to least verbose:

Log LevelPrefixDescription
VerboseVHighly detailed SDK status for deep debugging and troubleshooting
DebugDGeneral SDK status with more detailed information than Info
InfoIGeneral SDK status and lifecycle events
WarningWAPI deprecations, invalid setup, and other recoverable issues
ErrorECritical errors and exceptions that the SDK cannot gracefully handle
AssertDisables all logging

Log privacy levels

Control the visibility of log contents using privacy levels. This is especially useful when debugging release builds without exposing sensitive information.

  • private (default): Uses the standard android.util.Log. In production builds, verbose and debug messages are completely dropped and will not be logged. Use this for production builds to protect sensitive data.
  • public: Sends all logs with a public privacy level, making it easier to capture detailed information from release builds. To ensure visibility in production builds, verbose and debug messages are automatically elevated to the info log level.
 Note

When using public privacy level, verbose and debug log messages are automatically elevated to info level to ensure all detailed logs are visible when debugging production builds.

Configuration

You can set separate log levels and privacy levels for development and production builds in your Airship config during takeOff.

Common configuration

Typical setup: more verbose logging for development, minimal logging for production:

Common logging configuration

airshipConfigOptions {
    // Development: verbose logging for debugging
    setDevelopmentLogLevel(AirshipConfigOptions.LogLevel.VERBOSE)
    setDevelopmentLogPrivacyLevel(AirshipConfigOptions.PrivacyLevel.PUBLIC)

    // Production: minimal logging to reduce noise
    setProductionLogLevel(AirshipConfigOptions.LogLevel.ERROR)
    setProductionLogPrivacyLevel(AirshipConfigOptions.PrivacyLevel.PRIVATE)
    // ...
}
AirshipConfigOptions.newBuilder()
    // Development: verbose logging for debugging
    .setDevelopmentLogLevel(AirshipConfigOptions.LogLevel.VERBOSE)
    .setDevelopmentLogPrivacyLevel(AirshipConfigOptions.PrivacyLevel.PUBLIC)

    // Production: minimal logging to reduce noise
    .setProductionLogLevel(AirshipConfigOptions.LogLevel.ERROR)
    .setProductionLogPrivacyLevel(AirshipConfigOptions.PrivacyLevel.PRIVATE)
    ...

Debugging production issues

When debugging issues in production builds, temporarily enable verbose logging to capture detailed SDK behavior:

Debugging production

airshipConfigOptions {
    // Production debugging: enable verbose logs
    setProductionLogLevel(AirshipConfigOptions.LogLevel.VERBOSE)
    setProductionLogPrivacyLevel(AirshipConfigOptions.PrivacyLevel.PUBLIC)
    // ...
}
AirshipConfigOptions.newBuilder()
    // Production debugging: enable verbose logs
    .setProductionLogLevel(AirshipConfigOptions.LogLevel.VERBOSE)
    .setProductionLogPrivacyLevel(AirshipConfigOptions.PrivacyLevel.PUBLIC)
    ...

Verifying log level

You can confirm the current log level by checking the Airship log output in your console. On Android, Airship logs use the standard log priority tags (e.g., V, D, I). To find them, filter your logs for the tag UAirship and observe the priority letter.

Example (Verbose Log) The V in the output indicates a VERBOSE level log.

Sample - UALib com.urbanairship.sample V UAirship - !SDK-VERSION-STRING!:com.urbanairship.android:urbanairship-core:16.9.0

Custom log handler

You can provide a custom log handler to intercept and handle all Airship log messages. This is useful when you need to integrate Airship logs with your own logging system or customize how logs are formatted or stored.

When a custom log handler is set, the default Airship log handler is completely replaced. Log level filtering is performed before your handler is called, so your handler will only receive logs that meet the configured log level threshold.

Implement the AirshipLogHandler interface and set it on UALog.logHandler before calling Airship.takeOff():

Custom log handler

// Example log handler to forward logs to Android logcat and upload to a remote logging service
val customLogHandler = AirshipLogHandler { tag, logLevel, throwable, message ->
    val msg = message()

    // Forward to Android logcat
    when (logLevel) {
        Log.VERBOSE -> Log.v(tag, msg, throwable)
        Log.DEBUG -> Log.d(tag, msg, throwable)
        Log.INFO -> Log.i(tag, msg, throwable)
        Log.WARN -> Log.w(tag, msg, throwable)
        Log.ERROR -> Log.e(tag, msg, throwable)
        else -> Unit // Do nothing
    }

    // Optionally: send to remote logging service
    MyLoggingService.log(tag, logLevel, msg, throwable)
}

// Set the custom handler globally, before Airship.takeOff()
UALog.logHandler = customLogHandler
AirshipLogHandler logHandler = new AirshipLogHandler() {
    @Override
    public void log(@NotNull String tag, int logLevel, @Nullable Throwable throwable, @NotNull Function0<@NotNull String> message) {
        String msg = message.invoke();

        // Forward to Android logcat
        switch (logLevel) {
            case Log.VERBOSE:
                Log.v(tag, msg, throwable);
                break;
            case Log.DEBUG:
                Log.d(tag, msg, throwable);
                break;
            case Log.INFO:
                Log.i(tag, msg, throwable);
                break;
            case Log.WARN:
                Log.w(tag, msg, throwable);
                break;
            case Log.ERROR:
                Log.e(tag, msg, throwable);
                break;
            default:
                break; // Do nothing
        }

        // Optionally: send to remote logging service
        MyLoggingService.log(tag, logLevel, msg, throwable);
    }
};

// Set the custom handler globally, before Airship.takeOff()
UALog.setLogHandler(logHandler);