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() остају, па је важно не приказивати осетљиве податке кроз ове методе.
За прилагођено филтрирање у runtime-у, 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 поруке се уписују само када је debugger повезан или када је аргумент -com.apple.CoreData.Logging.debug 1 укључен. Info поруке се прикупљају у меморији уређаја (до 512 KB) и доступне су преко Console.app. Error и fault се уписују стално и доступни су за прикупљање путем система за извештавање о падовима.
Важна карактеристика OSLog-а: форматирани низови са чуварима места. Уместо интерполације низова Swift (која се израчунава увек, без обзира на ниво), OSLog користи os_log формат са %{public}@ и %{private}@ за разграничење осетљивих података. Приватни параметри се маскирају у продукцијским логовима.
Основно правило — минимални скуп нивоа у продукцији: Info, Warn, Error, Assert. Debug и Verbose морају бити искључени. Разлог није толико у безбедности колико у перформансама: сваки лог позив заузима процесорско време на форматирање низа, чак и ако се порука не приказује.
Критична оптимизација — никада не користите интерполацију низова у лог позивима. Ако се низ формира пре позива log(), процесорско време се троши чак и када је ниво искључен. Користите лење форматирање преко ламбди или заштитних услова.
На Android-у овој сврси служи метод Log.isLoggable(), у OSLog-у — изворна подршка за форматиране низове са чуварима места. Timber за Android решава проблем кроз timber.log.Tree са провером нивоа унутар стабла.
Remote Log Level — пракса у којој се ниво логовања контролише са сервера путем Firebase Remote Config или сличног сервиса. Ако се у продукцији појави сложена грешка, програмер може даљински да укључи Debug логовање за одређени модул на уређајима изабране групе корисника.
Према подацима Firebase, 2024, ова пракса смањује време дијагностике ретких грешака за 60% и омогућава добијање потпуне слике проблема без инсталирања debug верзије. Главно ограничење — логови се укључују тек при следећем покретању апликације након пријема конфигурације.
BuildConfig.DEBUG на Android-у и #if DEBUG у Swift-у — стандардни механизми условне компајлације који искључују debug нивое у 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 порука у продукцију.
Често постављана питања
Убрзано пражњење батерије и прекомеран упис на диск. Сваки 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође