Log Level — klasyfikacja komunikatów logowania według stopnia krytyczności, pozwalająca programistom kontrolować ilość wyświetlanych informacji na różnych etapach działania aplikacji. Według danych Google Android Developers, 2024, prawidłowy wybór poziomu logowania zmniejsza objętość logów w produkcji o 85–95% i przyspiesza diagnozowanie błędów. Każdy poziom rozwiązuje swoje zadanie — od debugowania na etapie rozwoju po monitorowanie krytycznych awarii w środowisku produkcyjnym.
Najważniejsze
Log Level — to atrybut każdego komunikatu logu określający jego ważność i pilność przetwarzania. Nowoczesne platformy iOS i Android obsługują jednolitą skalę 6–7 poziomów: od maksymalnie szczegółowego (Verbose/Trace) do krytycznego (Error/Assert). Wybór poziomu określa, czy komunikat zostanie zapisany w logu przy bieżącej konfiguracji aplikacji.
Koncepcja Log Level opiera się na zasadzie piramidy krytyczności: im wyższy poziom, tym mniej komunikatów jest na nim wyświetlanych. Według danych Semaphore CI, 2024, w aplikacji produkcyjnej rozkład wygląda następująco: Info — 60% komunikatów, Warn — 25%, Error — 10%, Debug — 5%. Komunikaty Verbose powinny być całkowicie wyłączone w produkcji.
Każda platforma implementuje Log Level poprzez własne API. Android używa android.util.Log z metodami v(), d(), i(), w(), e(). Apple — OSLog z poziomami default, info, debug, error, fault. Biblioteki takie jak Timber i CocoaLumberjack nadbudowują dodatkową funkcjonalność na tych standardowych API.
Według danych Google I/O 2023, nieprawidłowy wybór Log Level jest przyczyną 40% problemów z wydajnością w produkcji. Programiści pozostawiają logi Debug w kompilacji release, co prowadzi do nadmiernego zapisu na dysk i przyspieszonego rozładowania baterii.
Verbose (TRACE) — najbardziej szczegółowy poziom, przeznaczony wyłącznie do rozwoju. Na tym poziomie wyświetlane są wszystkie pośrednie obliczenia, iteracje pętli, wyniki każdego kroku algorytmu. Na Androidzie temu poziomowi odpowiada Log.v(), na iOS — OSLog z typem debug (przed iOS 14 używano os_trace).
Debug — komunikaty debugowania, przydatne podczas tworzenia i testowania. Zawierają informacje o stanie kluczowych obiektów, wyniki zapytań SQL, parametry wywołań API. W przeciwieństwie do Verbose, komunikaty Debug są ustrukturyzowane i semantycznie znaczące. Na iOS temu poziomowi odpowiada OSLogType.debug.
Info — komunikaty informacyjne o standardowych zdarzeniach aplikacji: inicjalizacja SDK, pomyślna autoryzacja, otwarcie ekranu, pobranie danych z serwera. Komunikaty Info nie powinny zawierać danych osobowych użytkowników i powinny być bezpieczne do analizy w produkcji. Na iOS używany jest OSLogType.info, na Androidzie — Log.i().
Warn — ostrzeżenia o potencjalnych problemach. Aplikacja nadal działa, ale sytuacja wymaga uwagi: rozmiar pamięci podręcznej zbliżający się do limitu, przestarzała wersja API, wolna odpowiedź sieciowa, ponowna próba połączenia. Na Androidzie — Log.w(), na iOS — OSLogType.default (dla ostrzeżeń).
Error — krytyczne błędy, przy których aplikacja nie może wykonać żądanej operacji, ale kontynuuje działanie: nieudane żądanie do API, utrata połączenia, błąd zapisu do bazy danych, brak uprawnień. Na iOS dla błędów używany jest OSLogType.error, na Androidzie — Log.e().
Assert (WTF) — najwyższy poziom, oznaczający sytuację „to nie powinno się zdarzyć”. Używany do logowania błędów naruszających fundamentalne niezmienniki systemu. Na Androidzie komunikaty Assert nie są wyświetlane w kompilacjach release domyślnie. Na iOS WTF (What a Terrible Failure) jest obsługiwane przez OSLogType.fault.
Android Log API — wbudowany mechanizm logowania z pakietu android.util.Log. Dostarcza 6 statycznych metod: Log.v(), Log.d(), Log.i(), Log.w(), Log.e() i Log.wtf(). Każda metoda przyjmuje tag (ciąg identyfikujący źródło) i msg (treść komunikatu).
class UserRepository {
companion object {
private val TAG = "UserRepo"
}
suspend fun loadUser(id: String): User {
Log.d(TAG, "Wczytywanie użytkownika o id: $id")
return try {
val response = api.fetchUser(id)
Log.i(TAG, "Użytkownik wczytany pomyślnie")
response.toUser()
} catch (e: Exception) {
Log.e(TAG, "Nie udało się wczytać użytkownika: ${e.message}")
throw e
}
}
}
Filtrowanie według poziomów w Android Logcat odbywa się przez ADB: adb logcat *:E pokaże tylko komunikaty Error. W kompilacjach produkcyjnych wszystkie wywołania Log.v() i Log.d() są usuwane przez ProGuard/R8 przy włączonej minifikacji. Log.i(), Log.w() i Log.e() pozostają, dlatego ważne jest, aby nie wyświetlać wrażliwych danych przez te metody.
Do niestandardowego filtrowania w czasie wykonania Android udostępnia Log.isLoggable(tag, level) — metodę, która sprawdza, czy określony poziom jest włączony dla danego tagu. Pozwala to dynamicznie włączać szczegółowe logowanie dla konkretnego modułu bez przebudowy aplikacji.
OSLog — ujednolicony system logowania Apple, który zastąpił przestarzały NSLog. OSLog udostępnia 5 poziomów: debug, info, default (notice), error i fault. Główną zaletą jest ustrukturyzowane logowanie z obsługą sformatowanych ciągów i dynamicznym filtrowaniem przez konsolę.
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)")
}
}
System filtrowania OSLog działa na poziomie systemu operacyjnego. Komunikaty Debug są zapisywane tylko przy podłączonym debuggerze lub po włączeniu argumentu -com.apple.CoreData.Logging.debug 1. Komunikaty Info są gromadzone w pamięci urządzenia (do 512 KB) i dostępne przez Console.app. Error i fault są zapisywane stale i dostępne do zbierania przez systemy raportowania awarii.
Ważna cecha OSLog: sformatowane ciągi z placeholderami. Zamiast interpolacji ciągów Swift (która jest obliczana zawsze, niezależnie od poziomu), OSLog używa formatu os_log z %{public}@ i %{private}@ do rozgraniczania wrażliwych danych. Prywatne parametry są maskowane w logach produkcyjnych.
Podstawowa zasada — minimalny zestaw poziomów w produkcji: Info, Warn, Error, Assert. Debug i Verbose powinny być wyłączone. Przyczyna nie leży tyle w bezpieczeństwie, co w wydajności: każde wywołanie logu zajmuje czas procesora na formatowanie ciągu, nawet jeśli komunikat nie jest wyświetlany.
Krytyczna optymalizacja — nigdy nie używaj interpolacji ciągów w wywołaniach logu. Jeśli ciąg jest tworzony przed wywołaniem log(), czas procesora jest marnowany nawet przy wyłączonym poziomie. Używaj leniwego formatowania przez lambdy lub warunki ochronne.
W Androidzie temu celowi służy metoda Log.isLoggable(), w OSLog — natywna obsługa sformatowanych ciągów z placeholderami. Timber dla Androida rozwiązuje problem poprzez timber.log.Tree z sprawdzaniem poziomu wewnątrz drzewa.
Remote Log Level — praktyka, w której poziom logowania jest zarządzany z serwera przez Firebase Remote Config lub podobną usługę. Jeśli w produkcji wystąpi złożony błąd, programista może zdalnie włączyć logowanie Debug dla konkretnego modułu na urządzeniach wybranej grupy użytkowników.
Według danych Firebase, 2024, taka praktyka skraca czas diagnozowania rzadkich błędów o 60% i pozwala uzyskać pełny obraz problemu bez instalowania debugowanej kompilacji. Głównym ograniczeniem jest to, że logi włączają się dopiero przy następnym uruchomieniu aplikacji po otrzymaniu konfiguracji.
BuildConfig.DEBUG na Androidzie i #if DEBUG w Swift — standardowe mechanizmy kompilacji warunkowej, wyłączające poziomy debugowania w kompilacjach release. Dla czystej architektury zaleca się przeniesienie wyboru Log Level do kontenera DI lub fabryki loggerów, aby nie zaśmiecać logiki biznesowej dyrektywami warunkowymi.
Pierwsza zasada — każde wywołanie logu powinno odpowiadać na pytanie „kto, co, kiedy”. Kto — komponent lub moduł (tag na Androidzie, category na iOS). Co — konkretne zdarzenie lub zmiana stanu. Kiedy — znacznik czasu, ustawiany automatycznie przez system logowania.
Druga zasada — nie loguj wrażliwych danych przez Info i wyższe. Hasła, tokeny, emaile, numery telefonów, dokładne współrzędne geograficzne — są kategorycznie zabronione w każdym logu, który trafia do produkcji. W razie potrzeby używaj maskowania: „email: us***@example.com”.
Trzecia zasada — poziom Warn to strefa odpowiedzialności programisty, Error — zespołu. Warn oznacza „tu jest potencjalny problem, obserwuj”. Error — „tu jest problem, naprawiaj”. Nie używaj Error dla sytuacji, które są oczekiwane i obsłużone (na przykład błąd API 404).
Czwarta zasada — konsystentność. Cały projekt powinien używać jednolitych konwencji nazewnictwa tagów i kategorii. Zaleca się ClassName.methodName dla tagów Android i module.subsystem dla kategorii iOS. Pozwala to szybko filtrować logi według komponentu.
Piąta zasada — testuj logi. W testach jednostkowych sprawdzaj, czy w określonych scenariuszach wywoływany jest prawidłowy Log Level. W tym celu istnieją biblioteki mockujące logowanie: Mockito dla Androida, Cuckoo dla iOS. Sprawdzanie poziomów w testach zapobiega wyciekowi komunikatów debugowania do produkcji.
Często zadawane pytania
Przyspieszone rozładowanie baterii i nadmierny zapis na dysk. Każdy log Debug formatuje ciąg i zapisuje dane do bufora. Na urządzeniach z pamięcią Flash przyspiesza to zużycie nośnika. Ponadto logi Debug mogą zawierać wrażliwe dane niedostępne do przeglądania w produkcji.
Debug — dla treści żądania i odpowiedzi, nagłówków i kodu statusu. Info — dla faktu wykonania żądania (URL, metoda, czas trwania). Error — dla nieudanych żądań z kodem 4xx/5xx. Nigdy nie używaj Verbose dla logów sieciowych w produkcji.
OSLogType.default (poziom notice) — komunikaty średniej ważności, zapisywane w logu systemowym i widoczne w Console.app. OSLogType.info — komunikaty techniczne, niezapisywane stale, dostępne tylko przy aktywnym profilowaniu przez Instruments.
R8/ProGuard usuwa Log.v() i Log.d() przy włączonej minifikacji w kompilacji release. Log.i(), Log.w(), Log.e() są zachowywane. Do całkowitego usunięcia wszystkich logów wymagana jest niestandardowa reguła -assumenosideeffects class android.util.Log z określeniem wszystkich poziomów.
Nie — nadmierne logowanie pogarsza czytelność i wydajność. Loguj wejście tylko w złożonych lub asynchronicznych metodach. Dla metod synchronicznych wystarczy jeden log w punkcie zwrotu lub błędu. Używaj poziomu Debug do śledzenia wywołań.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również