Log Level — класификация на лог съобщенията по степен на критичност, позволяваща на разработчиците да контролират обема на изходната информация на различните етапи от работата на приложението. Според данни на Google Android Developers, 2024, правилният избор на ниво на логване намалява обема на логовете в продукция с 85–95% и ускорява диагностиката на грешки. Всяко ниво решава своята задача — от дебъгване на етапа на разработка до мониторинг на критични повреди в продукция.
Основни точки
Log Level — атрибут на всяко лог съобщение, определящ неговата важност и спешност на обработка. Съвременните платформи iOS и Android поддържат единна скала от 6–7 нива: от максимално детайлно (Verbose/Trace) до критично (Error/Assert). Изборът на ниво определя дали съобщението ще бъде записано в лога при текущата конфигурация на приложението.
Концепцията на Log Level се основава на принципа на пирамидата на критичност: колкото по-високо е нивото, толкова по-малко съобщения се извеждат на него. Според данни на Semaphore CI, 2024, в продукционно приложение разпределението изглежда така: Info — 60% от съобщенията, Warn — 25%, Error — 10%, Debug — 5%. Verbose съобщенията трябва да бъдат напълно изключени в продукция.
Всяка платформа имплементира Log Level чрез собствено API. Android използва android.util.Log с методите v(), d(), i(), w(), e(). Apple — OSLog с нива default, info, debug, error, fault. Библиотеки като Timber и CocoaLumberjack надграждат допълнителна функционалност върху тези стандартни API.
Според данни на Google I/O 2023, неправилният избор на Log Level е причина за 40% от проблемите с производителността в продукция. Разработчиците оставят Debug логове в release компилацията, което води до прекомерен запис на диска и ускорено разреждане на батерията.
Verbose (TRACE) — най-детайлното ниво, предназначено изключително за разработка. На това ниво се извеждат всички междинни изчисления, итерации на цикли, резултати от всяка стъпка на алгоритъма. На Android това ниво съответства на Log.v(), на iOS — OSLog с тип debug (преди iOS 14 се използваше os_trace).
Debug — съобщения за дебъгване, полезни при разработка и тестване. Съдържат информация за състоянието на ключови обекти, резултати от SQL заявки, параметри на API извиквания. За разлика от Verbose, Debug съобщенията са структурирани и семантично значими. На iOS това ниво съответства на OSLogType.debug.
Info — информационни съобщения за нормални събития в приложението: инициализация на SDK, успешна авторизация, отваряне на екран, получаване на данни от сървъра. Info съобщенията не трябва да съдържат лични данни на потребителите и трябва да са безопасни за анализ в продукция. На iOS се използва OSLogType.info, на Android — Log.i().
Warn — предупреждения за потенциални проблеми. Приложението продължава да работи, но ситуацията изисква внимание: размер на кеша близо до лимита, остаряла версия на API, бавен мрежов отговор, повторен опит за свързване. На Android — Log.w(), на iOS — OSLogType.default (за предупреждения).
Error — критични грешки, при които приложението не може да извърши заявената операция, но продължава да работи: неуспешна заявка към API, загуба на връзка, грешка при запис в базата данни, липса на разрешения. На iOS за грешки се използва OSLogType.error, на Android — Log.e().
Assert (WTF) — най-високото ниво, обозначаващо ситуация „това не може да се случи”. Използва се за логване на грешки, които нарушават фундаментални инварианти на системата. На Android Assert съобщенията не се показват в release компилации по подразбиране. На iOS WTF (What a Terrible Failure) се обработва чрез OSLogType.fault.
Android Log API — вграден механизъм за логване от пакета android.util.Log. Предоставя 6 статични метода: Log.v(), Log.d(), Log.i(), Log.w(), Log.e() и Log.wtf(). Всеки метод приема tag (низ за идентификация на източника) и msg (текст на съобщението).
class UserRepository {
companion object {
private val TAG = "UserRepo"
}
suspend fun loadUser(id: String): User {
Log.d(TAG, "Зареждане на потребител с id: $id")
return try {
val response = api.fetchUser(id)
Log.i(TAG, "Потребителят е зареден успешно")
response.toUser()
} catch (e: Exception) {
Log.e(TAG, "Неуспешно зареждане на потребител: ${e.message}")
throw e
}
}
}
Филтриране по нива в Android Logcat се осъществява чрез ADB: adb logcat *:E ще покаже само Error съобщения. В продукционните компилации всички Log.v() и Log.d() извиквания се премахват от ProGuard/R8, когато минификацията е включена. Log.i(), Log.w() и Log.e() остават, затова е важно да не извеждате чувствителни данни чрез тези методи.
За персонализирано филтриране по време на изпълнение Android предоставя Log.isLoggable(tag, level) — метод, който проверява дали указаното ниво е включено за даден tag. Това позволява динамично включване на детайлно логване за конкретен модул без прекомпилиране на приложението.
OSLog — унифицирана система за логване на Apple, която замени остарелия NSLog. OSLog предоставя 5 нива: debug, info, default (notice), error и fault. Основното предимство е структурираното логване с поддръжка за форматирани низове и динамично филтриране чрез конзолата.
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 работи на ниво операционна система. Debug съобщенията се записват само когато дебъгерът е свързан или когато аргументът -com.apple.CoreData.Logging.debug 1 е включен. Info съобщенията се събират в паметта на устройството (до 512 KB) и са достъпни чрез Console.app. Error и fault се записват постоянно и са достъпни за събиране чрез системи за докладване на сривове.
Важна характеристика на OSLog: форматирани низове с placeholder-и. Вместо интерполация на низове Swift (която се изчислява винаги, независимо от нивото), OSLog използва os_log формат с %{public}@ и %{private}@ за разграничаване на чувствителни данни. Частните параметри се маскират в продукционните логове.
Основно правило — минимален набор от нива в продукция: Info, Warn, Error, Assert. Debug и Verbose трябва да бъдат изключени. Причината не е толкова в сигурността, колкото в производителността: всяко лог извикване заема процесорно време за форматиране на низа, дори ако съобщението не се показва.
Критична оптимизация — никога не използвайте интерполация на низове в лог извиквания. Ако низът се формира преди извикването на log(), процесорното време се губи дори когато нивото е изключено. Използвайте мързеливо форматиране чрез ламбда или защитни условия.
На Android тази цел се обслужва от метода Log.isLoggable(), в OSLog — от родната поддръжка за форматирани низове с placeholder-и. Timber за Android решава проблема чрез timber.log.Tree с проверка на нивото вътре в дървото.
Remote Log Level — практика, при която нивото на логване се управлява от сървъра чрез Firebase Remote Config или подобна услуга. Ако в продукция възникне сложна грешка, разработчикът може дистанционно да включи Debug логване за конкретен модул на устройствата на избрана група потребители.
Според данни на Firebase, 2024, тази практика намалява времето за диагностика на редки грешки с 60% и позволява получаване на пълна картина на проблема без инсталиране на debug компилация. Основното ограничение — логовете се активират едва при следващото стартиране на приложението след получаване на конфигурацията.
BuildConfig.DEBUG на Android и #if DEBUG в Swift — стандартни механизми за условна компилация, които изключват дебъг нивата в release компилациите. За чиста архитектура се препоръчва изнасяне на избора на Log Level в DI контейнер или фабрика за логери, за да не се натоварва бизнес логиката с условни директиви.
Първо правило — всяко лог извикване трябва да отговаря на въпроса „кой, какво, кога”. Кой — компонент или модул (tag на Android, category на iOS). Какво — конкретно събитие или промяна на състояние. Кога — времеви клеймо, поставяно автоматично от системата за логване.
Второ правило — не логвайте чувствителни данни чрез Info и нагоре. Пароли, токени, имейли, телефонни номера, точни геокоординати — категорично забранени във всеки лог, който попада в продукция. При необходимост използвайте маскиране: „email: us***@example.com”.
Трето правило — нивото Warn е зона на отговорност на разработчика, Error — на екипа. Warn означава „тук има потенциален проблем, наблюдавай”. Error — „тук има проблем, поправи”. Не използвайте Error за ситуации, които са очаквани и обработени (например 404 грешка на API).
Четвърто правило — консистентност. Целият проект трябва да използва единни конвенции за именуване на tag-ове и категории. Препоръчва се ClassName.methodName за Android tag-ове и module.subsystem за iOS категории. Това позволява бързо филтриране на логовете по компонент.
Пето правило — тествайте логовете. В unit тестовете проверявайте, че в определени сценарии се извиква правилното Log Level. За тази цел съществуват mock библиотеки за логване: Mockito за Android, Cuckoo за iOS. Проверката на нивата в тестовете предотвратява изтичане на дебъг съобщения в продукция.
Често задавани въпроси
Ускорено разреждане на батерията и прекомерен запис на диска. Всеки Debug лог форматира низа и записва данни в буфера. На устройства с Flash памет това ускорява износването на носителя. Освен това Debug логовете могат да съдържат чувствителни данни, недостъпни за преглед в продукция.
Debug — за тялото на заявката и отговора, заглавки и статус код. Info — за факта на изпълнение на заявката (URL, метод, продължителност). Error — за неуспешни заявки с код 4xx/5xx. Никога не използвайте Verbose за мрежови логове в продукция.
OSLogType.default (ниво notice) — съобщения със средна важност, записват се в системния лог и са видими в Console.app. OSLogType.info — технически съобщения, не се записват постоянно, достъпни са само при активно профилиране чрез Instruments.
R8/ProGuard премахва Log.v() и Log.d() когато минификацията е включена в release компилацията. Log.i(), Log.w(), Log.e() се запазват. За пълно премахване на всички логове е необходимо персонализирано правило -assumenosideeffects class android.util.Log с посочване на всички нива.
Не — прекомерното логване влошава четимостта и производителността. Логвайте входа само в сложни или асинхронни методи. За синхронни методи е достатъчен един лог в точката на връщане или грешка. Използвайте Debug ниво за проследяване на извиквания.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също