---
source_url: https://www.pubnub.com/docs/sdks/kotlin/logging
title: Logging for Kotlin SDK
updated_at: 2026-06-19T11:37:45.637Z
sdk_name: PubNub Kotlin SDK
sdk_version: 13.4.0
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Logging for Kotlin SDK

PubNub Kotlin SDK, use the latest version: 13.4.0

Install:

```bash
Add PubNub dependency to your build@13.4.0
```

This page explains how to configure logging in the PubNub Kotlin Software Development Kit (SDK) using SLF4J and custom loggers.

## Logging architecture

You can configure logging for different environments and use cases:

* SLF4J integration: All logs route through SLF4J, allowing you to use any SLF4J-compatible backend (`logback`, `log4j`, `slf4j-simple`, `commons-logging`, `java.util.logging`)
* Custom loggers: Optionally add custom logger implementations via the `customLoggers` configuration parameter to route logs to external systems
* Structured logging: Logs include structured data with instance IDs, timestamps, log levels, and typed content

The SDK sends all log entries to both SLF4J and any configured custom loggers simultaneously.

## Log levels

SLF4J uses standard log levels:

| Level | Purpose |
| --- | --- |
| `TRACE` | Internal operations: serialization, encryption, subscription lifecycle, and detailed execution flow |
| `DEBUG` | User inputs, API parameters, HTTP requests and responses, and configuration properties |
| `INFO` | Significant events like successful initialization and configuration changes |
| `WARN` | Deprecation warnings, unusual conditions, and non-breaking validation warnings |
| `ERROR` | Errors, exceptions with stack traces, and failed operations |

The SDK logs all configuration properties at `DEBUG` level during initialization. Sensitive values such as secret keys are masked.

## Custom loggers

Beyond SLF4J, you can implement the `CustomLogger` interface to route logs to external monitoring services, databases, or analytics platforms alongside your SLF4J backend.

### CustomLogger interface

The `CustomLogger` interface supports string messages and structured `LogMessage` objects:

```kotlin
interface CustomLogger {
    val name: String get() = "CustomLogger"
    
    // String-based logging methods
    fun trace(message: String?)
    fun debug(message: String?)
    fun info(message: String?)
    fun warn(message: String?)
    fun error(message: String?)
    
    // Structured logging methods
    fun trace(logMessage: LogMessage)
    fun debug(logMessage: LogMessage)
    fun info(logMessage: LogMessage)
    fun warn(logMessage: LogMessage)
    fun error(logMessage: LogMessage)
}
```

The SDK calls both overloads for each log entry:

* String version - a simplified, human-readable message
* LogMessage version - structured data including instance ID, timestamp, location, and typed content

You only need to implement the methods you plan to use. The default implementations do nothing.

### Structured logging with LogMessage

`LogMessage` provides structured information for each log entry:

| Field | Description |
| --- | --- |
| `message` | The log content (one of: Text, Object, Error, NetworkRequest, or NetworkResponse) |
| `details` | Optional additional context |
| `type` | Automatically inferred from message content (TEXT, OBJECT, ERROR, NETWORK_REQUEST, NETWORK_RESPONSE) |
| `location` | Source class or method generating the log |
| `pubNubId` | PubNub instance identifier |
| `logLevel` | The SLF4J log level (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`) |
| `timestamp` | When the log was created with millisecond precision (format: `HH:mm:ss.SSS`) |

#### Custom logger example

```kotlin
import com.pubnub.api.logging.CustomLogger
import com.pubnub.api.logging.LogMessage
import com.pubnub.api.logging.LogMessageContent
import com.pubnub.api.logging.LogMessageType

class MonitoringLogger : CustomLogger {
    private val monitoringService: MonitoringService = MonitoringService()
    private val analyticsService: AnalyticsService = AnalyticsService()
    override val name = "MonitoringLogger"

    override fun error(logMessage: LogMessage) {
        // Access structured error data
        when (val content = logMessage.message) {
            is LogMessageContent.Error -> {
                // Send error details to monitoring service
                monitoringService.reportError(
                    message = content.message,
                    stackTrace = content.stack,
                    errorType = content.type,
                    instanceId = logMessage.pubNubId,
                    timestamp = logMessage.timestamp,
                    location = logMessage.location
                )
            }

            is LogMessageContent.NetworkRequest -> {
                // Track failed network requests
                val request = content
                if (request.failed) {
                    monitoringService.reportNetworkFailure(
                        url = "${request.origin}${request.path}",
                        method = request.method.value,
                        instanceId = logMessage.pubNubId
                    )
                }
            }

            else -> {
                // Handle other content types as needed
            }
        }
    }

    override fun warn(message: String?) {
        // Simple string-based warning for deprecations
        message?.let { monitoringService.logWarning(it) }
    }

    override fun debug(logMessage: LogMessage) {
        // Log configuration changes
        if (logMessage.type == LogMessageType.OBJECT) {
            val content = logMessage.message as? LogMessageContent.Object
            content?.operation?.let { operation ->
                if (operation.contains("Configuration")) {
                    analyticsService.trackConfigChange(
                        instanceId = logMessage.pubNubId,
                        details = content.arguments
                    )
                }
            }
        }
    }

    // Other methods use default no-op implementation
}
```

Configure the SDK with your custom logger:

```kotlin
// Configure SDK with custom logger
val config = PNConfiguration.builder(UserId("abc"), "subscribeKey") {
    customLoggers = listOf(MonitoringLogger())
}.build()

val pubnub = PubNub.create(config)
```

## Enable logging

Logging is automatically enabled when you create a PubNub instance. The SDK logs configuration details at the `DEBUG` level during initialization. SLF4J routes all logs to your configured backend.

```kotlin
val builder = PNConfiguration.builder(UserId("abc"), "subscribeKey") {
    publishKey = "publishKey"
}

val pubnub = PubNub.create(builder.build())
```

The SDK automatically logs the complete configuration at initialization, including:

* User ID and subscribe key
* Publish key (if set)
* Secret key status (masked as "set: *****" if configured)
* Connection timeouts and retry settings
* Presence and heartbeat configuration
* Custom logger count (if configured)
* Instance identifier

:::note Secret keys are masked
Secret keys and authentication tokens are never logged in plain text. The SDK logs only "set: *****" to indicate they are configured.
:::

## Logged information

The Kotlin SDK logs information at various stages of operation. This provides complete visibility into SDK behavior.

### Configuration at initialization

When you create a PubNub instance, the SDK logs all configuration properties at the `DEBUG` level. The SDK also logs any subsequent changes to these properties.

Each configuration log entry includes:

* Instance identifier (unique ID)
* SDK version information
* All configuration property names and values
* Masked sensitive values (secret keys, authentication tokens)

### API call parameters

The SDK logs user-provided input data for each API call at the `DEBUG` level.

Logged parameters include:

* Channel names and channel groups
* Messages and metadata
* Timetoken values
* Filter expressions
* Custom query parameters
* Operation-specific parameters

These logs help identify mismatches. You can compare expected input with actual data passed to endpoints.

### Network requests and responses

The SDK logs complete HTTP transaction information at the `DEBUG` level:

#### Request logs include

* HTTP method (GET, POST, PATCH, DELETE)
* Complete URL with origin and path
* Query parameters as key-value pairs
* Request headers
* Request body content (for POST, PUT, or PATCH requests)
* Request identifier (if configured)

#### Response logs include

* HTTP status code
* Response headers
* Response body content
* Request URL for correlation

Network logs help troubleshoot connectivity issues. Network logs also show exactly what data flows between your application and PubNub servers.

### Errors and exceptions

The SDK logs errors and exceptions at the `ERROR` level with:

* Error type (exception class name)
* Error message
* Stack trace (up to 10 frames)
* Duration before failure (for network errors)
* Request details for failed operations

## SLF4J backend configuration

The following sections explain how to configure different SLF4J backends. Choose the backend that best fits your environment and logging requirements.

For more information about configuration parameters, refer to [Configuration](https://www.pubnub.com/docs/sdks/kotlin/api-reference/configuration#configuration).

## Implement logging on Android

Add the following dependency to the project and configure it.

* [Logback Android dependency](https://mvnrepository.com/artifact/com.github.tony19/logback-android/2.0.1)

Example configuration:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Android LogCat appender with simple pattern -->
    <appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
        <encoder>
            <pattern>%logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Set DEBUG level for PubNub packages -->
    <logger name="com.pubnub" level="TRACE" />

    <!-- Root logger -->
    <root level="INFO">
        <appender-ref ref="LOGCAT" />
    </root>
</configuration>
```

The logs will appear in Logcat.

## Implement logging using log4j

To implement logging using `log4j` you need to add the following references to the project. Using `log4j` you can log to console or a file or both.

* [log4j-slf4j2](https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl) file which acts as a bridge between `slf4j` and `log4j`.
* [log4j-api](https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api) file, which provides the underlying logging framework API.
* [log4j-core](https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core) file, which provides the underlying logging framework Implementation.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} -=extra if you like=- [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console"/>
        </Root>
        <!-- Set PubNub logging level -->
        <Logger name="com.pubnub" level="TRACE" additivity="false">
            <AppenderRef ref="Console"/>
            <!-- <AppenderRef ref="YourCustomAppender"/>-->
        </Logger>
    </Loggers>
</Configuration>
```

See this example:

```kotlin
import com.pubnub.api.PubNub
import com.pubnub.api.PubNubException
import com.pubnub.api.UserId
import com.pubnub.api.v2.PNConfiguration
import org.slf4j.Logger
import org.slf4j.LoggerFactory

object LoggingLog4j2 {
    private val logger: Logger = LoggerFactory.getLogger(LoggingLog4j2::class.java)

    @JvmStatic
    fun main(args: Array<String>) {
        val config = PNConfiguration.builder(
            UserId("log4jDemoUser"),
            "demo" // Replace with your Subscribe Key from the PubNub Admin Portal
        ) {
            // Add publish key (only required if publishing)
            publishKey = "demo" // Replace with your Publish Key from the PubNub Admin Portal
        }

        // Initialize PubNub with the configuration
        val pubnub = PubNub.create(config.build())

        // Perform some operations to generate logs
        try {
            // Get time operation
            logger.info("Executing time operation")
            pubnub.time().sync()
            logger.info("Time operation completed")

            // Publish a message
            logger.info("Publishing a message")
            pubnub.channel("log4j-demo-channel")
                .publish("Hello from Log4j Example")
                .sync()
            logger.info("Message published successfully")
        } catch (e: PubNubException) {
            logger.error("Error during operations: ${e.message}", e)
        }

        logger.info("Log4j example complete - check the log file for details")
    }
}
```

## Implement logging using slf4j-simple

To implement logging using `slf4j-simple` you need to add the following references to the project.

* [SLF4J API dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-api)
* [SLF4J-Simple dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-simple) which provides the underlying logging framework

Along with these references you need to add the `simplelogger.properties` file in the `CLASSPATH`.

See this example:

```properties
# Set default logging level
org.slf4j.simpleLogger.defaultLogLevel=debug

# Configure PubNub logging
org.slf4j.simpleLogger.log.com.pubnub=debug

# Show date-time in logs
org.slf4j.simpleLogger.showDateTime=true
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss.SSS

# Show thread name
org.slf4j.simpleLogger.showThreadName=true

# Show log name
org.slf4j.simpleLogger.showLogName=true

# Show short log name (package path will be shortened)
org.slf4j.simpleLogger.showShortLogName=false

# Configure log level for specific packages (optional examples)
#org.slf4j.simpleLogger.log.com.your.path=debug
```

### Example usage of slf4j-simple

```kotlin
import com.pubnub.api.PubNub
import com.pubnub.api.PubNubException
import com.pubnub.api.UserId
import com.pubnub.api.v2.PNConfiguration
import org.slf4j.Logger
import org.slf4j.LoggerFactory

object LoggingSlf4jSimple {
    private val logger: Logger = LoggerFactory.getLogger(LoggingSlf4jSimple::class.java)

    @JvmStatic
    fun main(args: Array<String>) {
        val config = PNConfiguration.builder(
            UserId("slf4jSimpleDemoUser"),
            "demo" // Replace with your Subscribe Key from the PubNub Admin Portal
        ) {
            // Add publish key (only required if publishing)
            publishKey = "demo" // Replace with your Publish Key from the PubNub Admin Portal
        }

        // Initialize PubNub with the configuration
        val pubnub = PubNub.create(config.build())

        // Perform some operations to generate logs
        try {
            // Get time operation
            logger.info("Executing time operation")
            pubnub.time().sync()
            logger.info("Time operation completed")

            // Publish a message
            logger.info("Publishing a message")
            pubnub.channel("slf4j-simple-demo-channel")
                .publish("Hello from SLF4J Simple Example")
                .sync()
            logger.info("Message published successfully")
        } catch (e: PubNubException) {
            logger.error("Error during operations: ${e.message}", e)
        }

        logger.info("SLF4J Simple example complete - check the console output for details")
    }
}
```

## Implement logging using logback-classic

To implement logging using logback, you need to add the following references to the project.

* [Logback classic](https://mvnrepository.com/artifact/ch.qos.logback/logback-classic) and [logback-core](https://mvnrepository.com/artifact/ch.qos.logback/logback-core) jar files which provide the underlying logging framework
* [Logback core dependency](https://mvnrepository.com/artifact/ch.qos.logback/logback-core) which provides the underlying logging framework

See an sample `logback.xml` logback config file:

```xml
<configuration>
    <!-- Console appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- Format: timestamp [thread] level logger - message -->
            <pattern>%d{HH:mm:ss.SSS} -=extra if you like=- [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Set the default logging level for all loggers -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>

    <!-- Configure PubNub calls logging -->
    <logger name="com.pubnub" level="TRACE" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

</configuration>
```

See this example:

```kotlin
import com.pubnub.api.PubNub
import com.pubnub.api.PubNubException
import com.pubnub.api.UserId
import com.pubnub.api.v2.PNConfiguration
import org.slf4j.Logger
import org.slf4j.LoggerFactory

object LoggingLogback {
    // Get an SLF4j logger instance (named after the class)
    private val logger: Logger = LoggerFactory.getLogger(LoggingLogback::class.java)

    @JvmStatic
    fun main(args: Array<String>) {
        logger.info("Starting PubNub logging example...")

        val config = PNConfiguration.builder(
            UserId("loggingDemoUser"),
            "demo" // Replace with your Subscribe Key from the PubNub Admin Portal
        ) {
            // Add publish key (only required if publishing)
            publishKey = "demo" // Replace with your Publish Key from the PubNub Admin Portal

            // Other optional configurations
            secure = true
        }

        // Initialize PubNub with the configuration
        val pubnub = PubNub.create(config.build())

        // Perform some operations to generate logs
        try {
            // Get time operation will generate logs
            logger.info("Executing time operation...")
            pubnub.time().sync()
            logger.info("Time operation executed - check logs for details")

            logger.info("Publishing message...")
            pubnub.channel("logging-demo-channel")
                .publish("Hello from Logging Example")
                .sync()
            logger.info("Message published - check logs for details")
        } catch (e: PubNubException) {
            logger.error("Error during operations: {}", e.message, e)
        }

        logger.info("Logging example complete - detailed logs should be visible according to your logging configuration")
    }
}
```

## Implement logging using java.util.logging

To implement logging using `java.util.logging` you need to add the following references to the project.

* [SLF4J API dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-api)
* [SLF4J-JDK14 dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14) which acts as a bridge between `slf4j` and `java`

JVM runtime provides the underlying logging framework.

## Implement logging using commons-logging

To implement logging using `commons-logging` you need to add the following references to the project.

* [SLF4J API dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-api)
* [SLF4J-JCL dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-jcl) which acts as a bridge between slf4j and common-logging
* [commons-logging dependency](https://mvnrepository.com/artifact/commons-logging/commons-logging) which acts as an abstraction layer

The underlying logging framework is chosen dynamically by `commons-logging`.

## No logging

To implement no logging you have two options:

* Add the SLF4J API dependency which is the SLF4J API and nothing else.
* Add the SLF4J API dependency and then add a reference to SLF4J-nop dependency.

## Logging best practices

Use these recommendations for effective logging.

### Choose the right log level for your backend

| Environment | Recommended SLF4J Level |
| --- | --- |
| Production | `WARN` or `ERROR` to capture issues without performance impact. |
| Staging | `INFO` to monitor operational events. |
| Development | `DEBUG` when actively developing or investigating issues. |
| Deep troubleshooting | `TRACE` only when working with PubNub support on complex issues. |

Configure your SLF4J backend (log4j, logback, etc.) to set the appropriate level for the `com.pubnub` logger:

```xml
<!-- Example logback.xml configuration -->
<logger name="com.pubnub" level="INFO" additivity="false">
    <appender-ref ref="CONSOLE"/>
</logger>
```

### Protect sensitive data

Never enable `DEBUG` or `TRACE` logging in production environments that handle sensitive information.

These levels may expose:

* API keys (publish and subscribe keys are visible in requests)
* User identifiers
* Message content
* Authentication tokens (masked as "set: *****" in configuration, but visible in network requests)

### Use custom loggers for monitoring

Implement `CustomLogger` to route critical errors and metrics to external monitoring services without affecting SLF4J logging:

```kotlin
val configuration = PNConfiguration.builder(UserId("uniqueUserId"), "demo").apply {
    publishKey = "demo"
    customLoggers = listOf(
        MonitoringLogger(),  // Send errors to monitoring service
        MetricsLogger()      // Track usage patterns
    )
}.build()
```

Custom loggers receive structured `LogMessage` objects that are easier to parse than string-based logs.

### Monitor log volume

`TRACE` and `DEBUG` levels generate significant output. Ensure your logging infrastructure can handle the volume. This is especially important in high-throughput applications.

Network request and response logging at `DEBUG` level can be particularly verbose for applications with many API calls.

### Optimize performance in production

Set your SLF4J backend to `WARN` or `ERROR` level for the `com.pubnub` logger. This optimizes performance and reduces storage costs:

```xml
<!-- Production logback.xml configuration -->
<logger name="com.pubnub" level="WARN" additivity="false">
    <appender-ref ref="CONSOLE"/>
</logger>
```

### Android-specific recommendations

For Android applications, use SLF4J Android to optimize logging for mobile devices:

* Set log level to `WARN` or `ERROR` in release builds
* Consider disabling network request/response logging in production to reduce logcat noise
* Use custom loggers to send critical errors to crash reporting services like Firebase Crashlytics

### Provide complete logs to support

When reporting issues to [PubNub support](https://support.pubnub.com/), provide complete logs:

1. Set your SLF4J backend to `DEBUG` or `TRACE` level for `com.pubnub`
2. Reproduce the issue
3. Collect logs from SDK initialization through the problem occurrence
4. Include the PubNub SDK version and Kotlin version

The configuration is automatically logged at initialization, which helps support diagnose issues faster.