Log Level in App Development: What It Is, Level Types, and Configuration

Author: IT Sectr Published: 2026-05-28 Reading time: 8 min

Log Level — a classification of logging messages by severity level, allowing developers to control the volume of output information at different stages of the application’s lifecycle. According to Google Android Developers, 2024, choosing the right logging level reduces log volume in production by 85–95% and accelerates error diagnostics. Each level serves its own purpose — from debugging during development to monitoring critical failures in production.

Key Takeaways

  • Log Level — a standardized severity scale from Verbose (detailed debugging) to Error (critical failures)
  • Verbose and Debug — levels intended for development, disabled in production builds for performance
  • Info — informational messages about key events: startup, authentication, navigation
  • Warn — warnings about potential issues that do not cause an immediate failure
  • Error — critical errors requiring immediate attention and analysis by the developer

What Is Log Level?

Log Level is an attribute of each log message that determines its importance and urgency of processing. Modern iOS and Android platforms support a unified scale of 6–7 levels: from the most detailed (Verbose/Trace) to critical (Error/Assert). The choice of level determines whether the message will be written to the log under the current application configuration.

The Log Level concept is based on the severity pyramid principle: the higher the level, the fewer messages are output at that level. According to Semaphore CI, 2024, in a production application the distribution looks like this: Info — 60% of messages, Warn — 25%, Error — 10%, Debug — 5%. Verbose messages should be completely disabled in production.

Each platform implements Log Level through its own API. Android uses android.util.Log with methods v(), d(), i(), w(), e(). Apple uses OSLog with levels default, info, debug, error, fault. Libraries like Timber and CocoaLumberjack add extra functionality on top of these standard APIs.

According to Google I/O 2023, incorrect Log Level selection is the cause of 40% of performance issues in production. Developers leave Debug logs in release builds, leading to excessive disk writes and accelerated battery drain.

Types of Logging Levels: From Verbose to Assert

Verbose (TRACE) — the most detailed level, intended exclusively for development. At this level, all intermediate calculations, loop iterations, and results of each algorithm step are output. On Android, this level corresponds to Log.v(), on iOS — OSLog with type debug (before iOS 14, os_trace was used).

Debug — messages useful during development and testing. They contain information about the state of key objects, SQL query results, and API call parameters. Unlike Verbose, Debug messages are structured and semantically meaningful. On iOS, this level corresponds to OSLogType.debug.

Info — informational messages about normal application events: SDK initialization, successful authentication, screen opening, data retrieval from the server. Info messages must not contain users’ personal data and should be safe for production analysis. On iOS OSLogType.info is used, on Android — Log.i().

Warn — warnings about potential problems. The application continues working, but the situation requires attention: cache size nearing its limit, outdated API version, slow network response, retrying a connection. On Android — Log.w(), on iOS — OSLogType.default (for warnings).

Error — critical errors where the application cannot perform the requested operation but continues running: failed API request, lost connection, database write error, missing permissions. On iOS, OSLogType.error is used for errors, on Android — Log.e().

Assert (WTF) — the highest level, indicating a situation that “cannot happen.” Used for logging bugs that violate fundamental system invariants. On Android, Assert messages are not shown in release builds by default. On iOS, WTF (What a Terrible Failure) is handled through OSLogType.fault.

Using Log Level on Android

Android Log API — the built-in logging mechanism from the android.util.Log package. It provides 6 static methods: Log.v(), Log.d(), Log.i(), Log.w(), Log.e() and Log.wtf(). Each method takes a tag (source identifier string) and msg (message text).

kotlin
class UserRepository {
    companion object {
        private val TAG = "UserRepo"
    }

    suspend fun loadUser(id: String): User {
        Log.d(TAG, "Loading user with id: $id")

        return try {
            val response = api.fetchUser(id)
            Log.i(TAG, "User loaded successfully")
            response.toUser()
        } catch (e: Exception) {
            Log.e(TAG, "Failed to load user: ${e.message}")
            throw e
        }
    }
}

Filtering by levels in Android Logcat is done via ADB: adb logcat *:E will show only Error messages. In production builds, all Log.v() and Log.d() calls are removed by ProGuard/R8 when minification is enabled. Log.i(), Log.w() and Log.e() remain, so it is important not to output sensitive data through these methods.

For custom filtering at runtime, Android provides Log.isLoggable(tag, level) — a method that checks whether the specified level is enabled for the given tag. This allows you to dynamically enable detailed logging for a specific module without rebuilding the application.

Using Log Level on iOS and macOS

OSLog — Apple’s unified logging system, replacing the deprecated NSLog. OSLog provides 5 levels: debug, info, default (notice), error, and fault. The main advantage is structured logging with support for formatted strings and dynamic filtering via the console.

swift
import OSLog

let logger = Logger(
    subsystem: "com.example.app",
    category: "network"
)

func fetchData(from url: URL) {
    logger.debug("Starting request to \(url.absoluteString)")

    do {
        let data = try Data(contentsOf: url)
        logger.info("Received \(data.count) bytes")
    } catch {
        logger.error("Request failed: \(error.localizedDescription)")
    }
}

OSLog filtering system operates at the operating system level. Debug messages are only written when the debugger is attached or when the -com.apple.CoreData.Logging.debug 1 argument is enabled. Info messages are collected in device memory (up to 512 KB) and are accessible via Console.app. Error and fault messages are written continuously and are available for collection through crash-reporting systems.

An important feature of OSLog: formatted strings with placeholders. Instead of Swift string interpolation (which is always evaluated, regardless of the level), OSLog uses os_log format with %{public}@ and %{private}@ for distinguishing sensitive data. Private parameters are masked in production logs.

Production vs Debug: How to Configure Level Filtering

The main rule — a minimal set of levels in production: Info, Warn, Error, Assert. Debug and Verbose must be disabled. The reason is not so much security as performance: each log call takes CPU time to format the string, even if the message is not output.

Lazy String Formatting

Critical optimization — never use string interpolation in log calls. If the string is built before the log() call, CPU time is wasted even when the level is disabled. Use lazy formatting via lambdas or guard conditions.

On Android, the Log.isLoggable() method serves this purpose; on OSLog, native formatted strings with placeholders are supported. Timber for Android solves the problem through timber.log.Tree with level checking inside the tree.

Dynamic Level Switching on the Fly

Remote Log Level — a practice where the logging level is controlled from the server via Firebase Remote Config or a similar service. If a complex error occurs in production, the developer can remotely enable Debug logging for a specific module on devices of a selected user group.

According to Firebase, 2024, this practice reduces the time to diagnose rare bugs by 60% and allows getting a complete picture of the problem without installing a debug build. The main limitation is that logging is only enabled on the next application launch after receiving the configuration.

Automatic Filtering by Build Type

BuildConfig.DEBUG on Android and #if DEBUG on Swift are standard conditional compilation mechanisms that disable debug levels in release builds. For clean architecture, it is recommended to move Log Level selection into a DI container or logger factory to avoid cluttering business logic with conditional directives.

Best Practices for Choosing a Logging Level

First rule — each log call should answer the question “who, what, when.” Who — the component or module (tag on Android, category on iOS). What — the specific event or state change. When — the timestamp, automatically added by the logging system.

Second rule — do not log sensitive data through Info and above. Passwords, tokens, emails, phone numbers, precise geo-coordinates are strictly prohibited in any log that ends up in production. If necessary, use masking: “email: us***@example.com.”

Third rule — Warn level is the developer’s responsibility, Error is the team’s. Warn means “there is a potential problem here, keep an eye on it.” Error means “there is a problem here, fix it.” Do not use Error for situations that are expected and handled (e.g., a 404 API error).

Fourth rule — consistency. The entire project should use unified naming conventions for tags and categories. ClassName.methodName is recommended for Android tags and module.subsystem for iOS categories. This allows quick filtering of logs by component.

Fifth rule — test your logs. In unit tests, verify that the correct Log Level is called in specific scenarios. Mock logging libraries exist for this purpose: Mockito for Android, Cuckoo for iOS. Checking levels in tests prevents debug messages from leaking into production.

Frequently Asked Questions

What happens if Debug logs are left in production?

Accelerated battery drain and excessive disk writes. Each Debug log formats a string and writes data to the buffer. On devices with Flash memory, this accelerates storage wear. Additionally, Debug logs may contain sensitive data that is not meant to be viewed in production.

Which Log Level should I use for logging network requests?

Debug — for request and response body, headers, and status code. Info — for the fact of a completed request (URL, method, duration). Error — for failed requests with 4xx/5xx codes. Never use Verbose for network logs in production.

How is OSLogType.default different from OSLogType.info?

OSLogType.default (notice level) — messages of medium importance, saved in the system log and visible in Console.app. OSLogType.info — technical messages, not saved permanently, only available during active profiling via Instruments.

How does ProGuard handle Log calls on Android?

R8/ProGuard removes Log.v() and Log.d() when minification is enabled in release builds. Log.i(), Log.w(), and Log.e() are preserved. For complete removal of all logs, a custom rule -assumenosideeffects class android.util.Log with all levels specified is required.

Should every method log its start and end?

No — excessive logging impairs readability and performance. Log entry only in complex or asynchronous methods. For synchronous methods, a single log at the return point or error point is sufficient. Use Debug level for call tracing.

Summary

  • Log Level — a severity scale from Verbose to Assert that determines the visibility of each log message
  • Verbose and Debug — intended for development and must be disabled in production builds
  • Info — key application events, safe for production analysis
  • Warn — potential issues that do not require immediate fixing
  • Error — critical failures requiring intervention from the development team
  • Android Log API uses tag + level; OSLog on iOS uses subsystem + category + level
  • Lazy formatting and conditional compilation are key techniques for optimizing logging in production

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also