Android Log

Android standard logging API (Log.d, Log.i, Log.w, Log.e, etc.). Widely used as simplest choice in Android application development. Suitable for rapid log output in debug builds, but limited advanced control in production builds.

Logging LibraryAndroidKotlinJavaDebuggingMobile Development

Library

Android Log

Overview

Android Log (android.util.Log) is the Android standard logging API (Log.d, Log.i, Log.w, Log.e, etc.), widely used as the simplest and most essential choice for Android application development. With its lightweight design integrated with system logs, it's optimized for rapid log output in debug builds, and logs remain even when applications crash, providing crucial information for problem analysis. As a fundamental debugging tool during development and testing phases, it's an indispensable tool for Android developers.

Details

Android Log continues to maintain its essential presence as a fundamental debugging tool for Android development in 2025. Progressive information management is possible through five major log levels (Verbose, Debug, Info, Warning, Error), with improved development experience through complete LogCat integration. It supports ProGuard automatic removal in production builds, TAG-based filtering, and structured log message formatting. In Kotlin development, TAG definition patterns using companion objects have been standardized, enabling efficient debugging workflows in modern Android development.

Key Features

  • 5-Level Log System: Importance classification through Verbose, Debug, Info, Warning, Error
  • TAG-Based Filtering: Efficient log searching by class name or module name
  • LogCat Integration: Real-time log display and analysis in Android Studio
  • ProGuard Optimization: Automatic log removal in production builds for performance improvement
  • System Integration: Complete integration with Android system logs and crash-time retention
  • Zero Dependencies: Immediately available without external libraries

Advantages and Disadvantages

Advantages

  • Available immediately without configuration as Android standard API
  • Excellent development experience through complete LogCat integration
  • Efficient debugging work through TAG-based filtering
  • Automatic optimization in production builds through ProGuard
  • Reliability with log retention even during system crashes
  • Unified usage experience in both Kotlin and Java

Disadvantages

  • Limited advanced log management features in production environments
  • Basic level support for structured logs and asynchronous processing
  • No enterprise-level features like log rotation or file output
  • Performance constraints during high-volume log output
  • Basic security-oriented log control features
  • Requires separate tools for complex log analysis and metrics collection

Reference Pages

Code Examples

Installation and Basic Setup

// No additional installation required for Android standard API
// Available through import statement
import android.util.Log

// TAG definition using Kotlin companion object (recommended pattern)
class MainActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "MainActivity"
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // Filtering confirmation in LogCat
        Log.d(TAG, "Activity created successfully")
    }
}

// Java version TAG definition
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Log.d(TAG, "Activity created successfully");
    }
}

Basic Log Level Usage (Verbose/Debug/Info/Warning/Error)

import android.util.Log

class UserService {
    companion object {
        private const val TAG = "UserService"
    }
    
    fun authenticateUser(username: String, password: String): Boolean {
        // Verbose: Most detailed information (development only)
        Log.v(TAG, "Starting authentication process for user: $username")
        
        // Debug: Debug information (development and test environments)
        Log.d(TAG, "Validating credentials for username: $username")
        
        // Info: General information (normal processing flow)
        Log.i(TAG, "User authentication attempt: $username")
        
        if (username.isEmpty() || password.isEmpty()) {
            // Warning: Warning level issues
            Log.w(TAG, "Authentication failed: Empty credentials provided")
            return false
        }
        
        return try {
            val result = performAuthentication(username, password)
            if (result) {
                Log.i(TAG, "Authentication successful for user: $username")
            } else {
                Log.w(TAG, "Authentication failed: Invalid credentials")
            }
            result
        } catch (e: Exception) {
            // Error: Error level (when exceptions occur)
            Log.e(TAG, "Authentication error for user: $username", e)
            false
        }
    }
    
    private fun performAuthentication(username: String, password: String): Boolean {
        // Authentication logic implementation
        Log.d(TAG, "Performing network authentication request")
        return username == "admin" && password == "password"
    }
}

// Java version log level usage example
public class UserService {
    private static final String TAG = "UserService";
    
    public boolean authenticateUser(String username, String password) {
        Log.v(TAG, "Starting authentication process for user: " + username);
        Log.d(TAG, "Validating credentials");
        Log.i(TAG, "User authentication attempt: " + username);
        
        if (username.isEmpty() || password.isEmpty()) {
            Log.w(TAG, "Authentication failed: Empty credentials");
            return false;
        }
        
        try {
            boolean result = performAuthentication(username, password);
            if (result) {
                Log.i(TAG, "Authentication successful");
            } else {
                Log.w(TAG, "Authentication failed: Invalid credentials");
            }
            return result;
        } catch (Exception e) {
            Log.e(TAG, "Authentication error", e);
            return false;
        }
    }
}

Structured Logs and Data Formats (JSON/Key-Value Format)

import android.util.Log
import org.json.JSONObject

class AnalyticsLogger {
    companion object {
        private const val TAG = "Analytics"
    }
    
    // Structured log - JSON format
    fun logUserAction(userId: String, action: String, metadata: Map<String, Any>) {
        val logData = JSONObject().apply {
            put("timestamp", System.currentTimeMillis())
            put("userId", userId)
            put("action", action)
            put("sessionId", getCurrentSessionId())
            metadata.forEach { (key, value) -> put(key, value) }
        }
        
        Log.i(TAG, "UserAction: $logData")
    }
    
    // Structured log - key-value format
    fun logPerformanceMetrics(operation: String, duration: Long, success: Boolean) {
        val logMessage = buildString {
            append("operation=$operation ")
            append("duration=${duration}ms ")
            append("success=$success ")
            append("memory=${getMemoryUsage()}MB ")
            append("thread=${Thread.currentThread().name}")
        }
        
        Log.d(TAG, "Performance: $logMessage")
    }
    
    // Detailed error log with stack trace
    fun logDetailedError(operation: String, error: Throwable, context: Map<String, String>) {
        val contextInfo = context.entries.joinToString(", ") { "${it.key}=${it.value}" }
        Log.e(TAG, "Error in $operation. Context: [$contextInfo]", error)
        
        // Additional debug information
        Log.d(TAG, "Error details: ${error.javaClass.simpleName} - ${error.message}")
        Log.d(TAG, "Stack trace: ${Log.getStackTraceString(error)}")
    }
    
    private fun getCurrentSessionId(): String = "session_${System.currentTimeMillis()}"
    private fun getMemoryUsage(): Long = Runtime.getRuntime().totalMemory() / 1024 / 1024
}

// Usage example
class ShoppingCartActivity : AppCompatActivity() {
    private val analytics = AnalyticsLogger()
    
    private fun addItemToCart(productId: String, quantity: Int) {
        val startTime = System.currentTimeMillis()
        
        try {
            // Add item processing
            performAddToCart(productId, quantity)
            
            val duration = System.currentTimeMillis() - startTime
            analytics.logPerformanceMetrics("add_to_cart", duration, true)
            
            analytics.logUserAction(
                userId = getCurrentUserId(),
                action = "add_to_cart",
                metadata = mapOf(
                    "productId" to productId,
                    "quantity" to quantity,
                    "cartTotal" to getCartTotal()
                )
            )
            
        } catch (e: Exception) {
            val duration = System.currentTimeMillis() - startTime
            analytics.logPerformanceMetrics("add_to_cart", duration, false)
            
            analytics.logDetailedError(
                operation = "add_to_cart",
                error = e,
                context = mapOf(
                    "productId" to productId,
                    "quantity" to quantity.toString(),
                    "userId" to getCurrentUserId()
                )
            )
        }
    }
}

Performance Measurement and Benchmarking

import android.util.Log

class PerformanceLogger {
    companion object {
        private const val TAG = "Performance"
    }
    
    // Method execution time measurement
    inline fun <T> measureTime(operation: String, block: () -> T): T {
        val startTime = System.nanoTime()
        return try {
            val result = block()
            val duration = (System.nanoTime() - startTime) / 1_000_000 // ms
            Log.d(TAG, "$operation completed in ${duration}ms")
            result
        } catch (e: Exception) {
            val duration = (System.nanoTime() - startTime) / 1_000_000
            Log.e(TAG, "$operation failed after ${duration}ms", e)
            throw e
        }
    }
    
    // Memory usage monitoring
    fun logMemoryUsage(operation: String) {
        val runtime = Runtime.getRuntime()
        val usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
        val maxMemory = runtime.maxMemory() / 1024 / 1024
        val memoryPercentage = (usedMemory * 100) / maxMemory
        
        Log.d(TAG, "$operation - Memory: ${usedMemory}MB/${maxMemory}MB (${memoryPercentage}%)")
        
        if (memoryPercentage > 80) {
            Log.w(TAG, "High memory usage detected: ${memoryPercentage}%")
        }
    }
    
    // Network request time measurement
    fun logNetworkRequest(url: String, method: String, responseTime: Long, responseCode: Int) {
        val logMessage = "NetworkRequest: method=$method url=$url " +
                "responseTime=${responseTime}ms responseCode=$responseCode"
        
        when {
            responseCode in 200..299 -> Log.i(TAG, logMessage)
            responseCode in 400..499 -> Log.w(TAG, logMessage)
            responseCode >= 500 -> Log.e(TAG, logMessage)
            else -> Log.d(TAG, logMessage)
        }
        
        // Performance warning
        if (responseTime > 5000) {
            Log.w(TAG, "Slow network request detected: ${responseTime}ms for $url")
        }
    }
}

// Usage example
class DataRepository {
    private val perfLogger = PerformanceLogger()
    
    suspend fun fetchUserData(userId: String): UserData? {
        return perfLogger.measureTime("fetchUserData") {
            perfLogger.logMemoryUsage("Before API call")
            
            val startTime = System.currentTimeMillis()
            val response = apiService.getUser(userId)
            val responseTime = System.currentTimeMillis() - startTime
            
            perfLogger.logNetworkRequest(
                url = "/api/users/$userId",
                method = "GET",
                responseTime = responseTime,
                responseCode = response.code()
            )
            
            perfLogger.logMemoryUsage("After API call")
            response.body()
        }
    }
}

Debug Tools and LogCat Integration

import android.util.Log

object DebugLogger {
    private const val TAG = "DebugLogger"
    private var isDebugMode = BuildConfig.DEBUG
    
    // Debug mode control
    fun setDebugMode(enabled: Boolean) {
        isDebugMode = enabled
        Log.i(TAG, "Debug mode ${if (enabled) "enabled" else "disabled"}")
    }
    
    // Conditional logging (output only in debug mode)
    fun d(tag: String, message: String) {
        if (isDebugMode) {
            Log.d(tag, message)
        }
    }
    
    fun v(tag: String, message: String) {
        if (isDebugMode) {
            Log.v(tag, message)
        }
    }
    
    // Log with class information
    fun logClassMethod(obj: Any, methodName: String, message: String = "") {
        if (isDebugMode) {
            val className = obj.javaClass.simpleName
            val fullMessage = if (message.isNotEmpty()) {
                "$className.$methodName(): $message"
            } else {
                "$className.$methodName() called"
            }
            Log.d(TAG, fullMessage)
        }
    }
    
    // Stack trace analysis
    fun logStackTrace(tag: String, message: String) {
        if (isDebugMode) {
            Log.d(tag, message)
            val stackTrace = Thread.currentThread().stackTrace
            stackTrace.forEachIndexed { index, element ->
                if (index > 1) { // Skip Thread.getStackTrace() and this method
                    Log.d(tag, "  at ${element.className}.${element.methodName}(${element.fileName}:${element.lineNumber})")
                }
            }
        }
    }
    
    // Custom tag generation for LogCat filtering
    fun createTag(className: String, feature: String): String {
        return "${className}_$feature"
    }
}

// Debug log usage example in Activity/Fragment
class MainActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "MainActivity"
        private val UI_TAG = DebugLogger.createTag("MainActivity", "UI")
        private val NETWORK_TAG = DebugLogger.createTag("MainActivity", "Network")
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DebugLogger.logClassMethod(this, "onCreate")
        
        setContentView(R.layout.activity_main)
        DebugLogger.d(UI_TAG, "Layout inflated successfully")
        
        setupClickListeners()
    }
    
    override fun onResume() {
        super.onResume()
        DebugLogger.logClassMethod(this, "onResume", "Activity becoming visible")
        
        // LogCat filter search examples:
        // tag:MainActivity_UI - Display only UI-related logs
        // tag:MainActivity_Network - Display only network-related logs
        // tag:MainActivity - Display all logs
    }
    
    private fun setupClickListeners() {
        DebugLogger.d(UI_TAG, "Setting up click listeners")
        
        findViewById<Button>(R.id.button).setOnClickListener {
            DebugLogger.logStackTrace(UI_TAG, "Button clicked")
            performNetworkRequest()
        }
    }
    
    private fun performNetworkRequest() {
        DebugLogger.d(NETWORK_TAG, "Starting network request")
        // Network processing
    }
}

// ProGuard configuration example (log removal in release build)
// proguard-rules.pro:
/*
-assumenosideeffects class android.util.Log {
    public static int d(...);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int e(...);
}

-assumenosideeffects class com.yourpackage.DebugLogger {
    public static void d(...);
    public static void v(...);
    public static void logClassMethod(...);
    public static void logStackTrace(...);
}
*/

Error Handling and Crash Reports

import android.util.Log
import java.io.PrintWriter
import java.io.StringWriter

class CrashReportLogger {
    companion object {
        private const val TAG = "CrashReport"
    }
    
    // Setup global exception handler
    fun setupGlobalExceptionHandler() {
        val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        
        Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
            logCrashReport(thread, exception)
            defaultHandler?.uncaughtException(thread, exception)
        }
        
        Log.i(TAG, "Global exception handler configured")
    }
    
    // Detailed crash report
    private fun logCrashReport(thread: Thread, exception: Throwable) {
        Log.e(TAG, "=== CRASH REPORT ===")
        Log.e(TAG, "Thread: ${thread.name}")
        Log.e(TAG, "Exception: ${exception.javaClass.simpleName}")
        Log.e(TAG, "Message: ${exception.message}")
        Log.e(TAG, "Stack trace:", exception)
        
        // Device information
        logDeviceInfo()
        
        // App state information
        logAppState()
        
        Log.e(TAG, "=== END CRASH REPORT ===")
    }
    
    // Try-catch exception handling
    inline fun <T> safeExecute(
        operation: String,
        onError: (Throwable) -> T? = { null },
        block: () -> T
    ): T? {
        return try {
            Log.d(TAG, "Executing: $operation")
            val result = block()
            Log.d(TAG, "Successfully completed: $operation")
            result
        } catch (e: Exception) {
            Log.e(TAG, "Error in $operation", e)
            logDetailedException(operation, e)
            onError(e)
        }
    }
    
    // Detailed exception information log
    private fun logDetailedException(operation: String, exception: Throwable) {
        Log.e(TAG, "=== DETAILED EXCEPTION ===")
        Log.e(TAG, "Operation: $operation")
        Log.e(TAG, "Exception type: ${exception.javaClass.canonicalName}")
        Log.e(TAG, "Message: ${exception.message}")
        Log.e(TAG, "Cause: ${exception.cause}")
        
        // Custom stack trace output
        val stringWriter = StringWriter()
        exception.printStackTrace(PrintWriter(stringWriter))
        Log.e(TAG, "Full stack trace:\n$stringWriter")
        
        // Output related exceptions too
        var cause = exception.cause
        var level = 1
        while (cause != null && level <= 5) {
            Log.e(TAG, "Caused by ($level): ${cause.javaClass.simpleName} - ${cause.message}")
            cause = cause.cause
            level++
        }
        
        Log.e(TAG, "=== END DETAILED EXCEPTION ===")
    }
    
    private fun logDeviceInfo() {
        Log.e(TAG, "Device info:")
        Log.e(TAG, "  Android version: ${android.os.Build.VERSION.RELEASE}")
        Log.e(TAG, "  API level: ${android.os.Build.VERSION.SDK_INT}")
        Log.e(TAG, "  Device: ${android.os.Build.DEVICE}")
        Log.e(TAG, "  Manufacturer: ${android.os.Build.MANUFACTURER}")
        Log.e(TAG, "  Model: ${android.os.Build.MODEL}")
    }
    
    private fun logAppState() {
        val runtime = Runtime.getRuntime()
        Log.e(TAG, "App state:")
        Log.e(TAG, "  Memory usage: ${(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024}MB")
        Log.e(TAG, "  Max memory: ${runtime.maxMemory() / 1024 / 1024}MB")
        Log.e(TAG, "  Available processors: ${runtime.availableProcessors()}")
        Log.e(TAG, "  Current thread: ${Thread.currentThread().name}")
    }
}

// Usage example
class NetworkManager {
    private val crashLogger = CrashReportLogger()
    
    fun fetchData(url: String): String? {
        return crashLogger.safeExecute(
            operation = "fetchData from $url",
            onError = { exception ->
                // Custom error handling
                when (exception) {
                    is java.net.UnknownHostException -> {
                        Log.w("NetworkManager", "No internet connection")
                        "offline_data"
                    }
                    is java.net.SocketTimeoutException -> {
                        Log.w("NetworkManager", "Request timeout")
                        "cached_data"
                    }
                    else -> null
                }
            }
        ) {
            // Network processing
            performNetworkCall(url)
        }
    }
    
    private fun performNetworkCall(url: String): String {
        // Actual network processing
        if (url.isEmpty()) throw IllegalArgumentException("URL cannot be empty")
        return "response_data"
    }
}

// Initialization in Application class
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        if (BuildConfig.DEBUG) {
            CrashReportLogger().setupGlobalExceptionHandler()
        }
    }
}

Production Environment Optimization and Security

import android.util.Log

// Production environment log manager
object ProductionLogger {
    private const val TAG = "ProductionLogger"
    
    // Log level control
    private val isReleaseMode = !BuildConfig.DEBUG
    private val allowedLogLevels = if (isReleaseMode) {
        setOf(Log.ERROR, Log.WARN)  // Only Error and Warning in production
    } else {
        setOf(Log.VERBOSE, Log.DEBUG, Log.INFO, Log.WARN, Log.ERROR)  // All levels in development
    }
    
    // Secure log (sensitive information removal)
    fun secureLog(tag: String, level: Int, message: String) {
        if (level !in allowedLogLevels) return
        
        val sanitizedMessage = sanitizeMessage(message)
        when (level) {
            Log.VERBOSE -> Log.v(tag, sanitizedMessage)
            Log.DEBUG -> Log.d(tag, sanitizedMessage)
            Log.INFO -> Log.i(tag, sanitizedMessage)
            Log.WARN -> Log.w(tag, sanitizedMessage)
            Log.ERROR -> Log.e(tag, sanitizedMessage)
        }
    }
    
    // Sensitive information removal and masking
    private fun sanitizeMessage(message: String): String {
        var sanitized = message
        
        // Password removal
        sanitized = sanitized.replace(Regex("password[=:\"']\\s*\\S+", RegexOption.IGNORE_CASE), "password=***")
        
        // Partial email address masking
        sanitized = sanitized.replace(Regex("([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})")) { matchResult ->
            val email = matchResult.value
            val atIndex = email.indexOf('@')
            val username = email.substring(0, atIndex)
            val domain = email.substring(atIndex)
            val maskedUsername = if (username.length > 2) {
                username.take(2) + "*".repeat(username.length - 2)
            } else {
                "*".repeat(username.length)
            }
            "$maskedUsername$domain"
        }
        
        // Phone number masking
        sanitized = sanitized.replace(Regex("\\b\\d{3}-?\\d{4}-?\\d{4}\\b"), "***-****-****")
        
        // API key and token removal
        sanitized = sanitized.replace(Regex("(api[_-]?key|token|secret)[=:\"']\\s*\\S+", RegexOption.IGNORE_CASE), "$1=***")
        
        return sanitized
    }
    
    // Usage-limited logging (spam prevention)
    private val logCounters = mutableMapOf<String, LogCounter>()
    
    data class LogCounter(
        var count: Int = 0,
        var lastLogTime: Long = 0L
    )
    
    fun throttledLog(tag: String, level: Int, message: String, maxLogsPerMinute: Int = 10) {
        val key = "$tag:$message"
        val currentTime = System.currentTimeMillis()
        val counter = logCounters.getOrPut(key) { LogCounter() }
        
        // Reset counter after 1 minute
        if (currentTime - counter.lastLogTime > 60_000) {
            counter.count = 0
            counter.lastLogTime = currentTime
        }
        
        if (counter.count < maxLogsPerMinute) {
            secureLog(tag, level, message)
            counter.count++
        } else if (counter.count == maxLogsPerMinute) {
            secureLog(tag, Log.WARN, "Log throttling activated for: $message")
            counter.count++
        }
        // Ignore if exceeds maxLogsPerMinute
    }
    
    // Production environment error-only logging
    fun logProductionError(tag: String, error: Throwable, context: Map<String, String> = emptyMap()) {
        if (!isReleaseMode) {
            // Detailed log in development environment
            Log.e(tag, "Error: ${error.message}", error)
            context.forEach { (key, value) ->
                Log.e(tag, "Context $key: $value")
            }
        } else {
            // Minimal information only in production environment
            val sanitizedContext = context.mapValues { (_, value) -> sanitizeMessage(value) }
            val errorInfo = "ErrorType: ${error.javaClass.simpleName}, Context: $sanitizedContext"
            Log.e(tag, sanitizeMessage(errorInfo))
        }
    }
}

// Usage example
class UserAuthenticator {
    companion object {
        private const val TAG = "UserAuth"
    }
    
    fun login(email: String, password: String): Boolean {
        // Detailed log in development environment, sensitive information removed in production
        ProductionLogger.secureLog(TAG, Log.INFO, "Login attempt for: $email")
        
        return try {
            val result = performLogin(email, password)
            if (result) {
                ProductionLogger.secureLog(TAG, Log.INFO, "Login successful for: $email")
            } else {
                // Throttling for spam prevention
                ProductionLogger.throttledLog(TAG, Log.WARN, "Login failed for: $email", maxLogsPerMinute = 5)
            }
            result
        } catch (e: Exception) {
            ProductionLogger.logProductionError(
                tag = TAG,
                error = e,
                context = mapOf(
                    "operation" to "login",
                    "email" to email,
                    "timestamp" to System.currentTimeMillis().toString()
                )
            )
            false
        }
    }
    
    private fun performLogin(email: String, password: String): Boolean {
        // Authentication logic
        return email.isNotEmpty() && password.isNotEmpty()
    }
}

// Production optimization through ProGuard configuration
// app/proguard-rules.pro:
/*
# Complete debug log removal in production builds
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int d(...);
    public static int i(...);
}

# ProductionLogger development-only method removal
-assumenosideeffects class com.yourpackage.ProductionLogger {
    # Remove Info level and below logs in production
    public static void secureLog(java.lang.String, int, java.lang.String) 
        return false;
        
    # Remove development-only part of throttledLog
    public static void throttledLog(...) 
        return false;
}

# BuildConfig.DEBUG optimization
-assumenosideeffects class com.yourpackage.BuildConfig {
    public static final boolean DEBUG return false;
}
*/