Log Level — classificazione dei messaggi di log in base al livello di criticità, che consente agli sviluppatori di controllare il volume delle informazioni emesse in diverse fasi dell’applicazione. Secondo Google Android Developers, 2024, la scelta del giusto livello di logging riduce il volume dei log in produzione del 85–95% e accelera la diagnosi degli errori. Ogni livello svolge il suo compito — dal debugging in fase di sviluppo al monitoraggio dei guasti critici in produzione.
Punti chiave
Log Level è un attributo di ogni messaggio di log che ne determina l’importanza e l’urgenza di elaborazione. Le piattaforme moderne iOS e Android supportano una scala unificata di 6–7 livelli: dal più dettagliato (Verbose/Trace) al critico (Error/Assert). La scelta del livello determina se il messaggio verrà scritto nel log con la configurazione corrente dell’applicazione.
Il concetto di Log Level si basa sul principio della piramide di criticità: più alto è il livello, meno messaggi vengono emessi a quel livello. Secondo Semaphore CI, 2024, in un’applicazione di produzione la distribuzione è la seguente: Info — 60% dei messaggi, Warn — 25%, Error — 10%, Debug — 5%. I messaggi Verbose devono essere completamente disattivati in produzione.
Ogni piattaforma implementa Log Level attraverso la propria API. Android utilizza android.util.Log con i metodi v(), d(), i(), w(), e(). Apple utilizza OSLog con i livelli default, info, debug, error, fault. Librerie come Timber e CocoaLumberjack aggiungono funzionalità supplementari sopra queste API standard.
Secondo Google I/O 2023, la scelta errata del Log Level è la causa del 40% dei problemi di prestazioni in produzione. Gli sviluppatori lasciano log Debug nelle build di rilascio, causando scritture eccessive su disco e un consumo accelerato della batteria.
Verbose (TRACE) — il livello più dettagliato, destinato esclusivamente allo sviluppo. A questo livello vengono emessi tutti i calcoli intermedi, le iterazioni dei cicli e i risultati di ogni passo dell’algoritmo. Su Android, questo livello corrisponde a Log.v(), su iOS — OSLog di tipo debug (prima di iOS 14, veniva utilizzato os_trace).
Debug — messaggi di debug utili durante lo sviluppo e il testing. Contengono informazioni sullo stato degli oggetti chiave, risultati di query SQL e parametri di chiamate API. A differenza di Verbose, i messaggi Debug sono strutturati e semanticamente significativi. Su iOS, questo livello corrisponde a OSLogType.debug.
Info — messaggi informativi sugli eventi normali dell’applicazione: inizializzazione SDK, autenticazione riuscita, apertura schermata, ricezione dati dal server. I messaggi Info non devono contenere dati personali degli utenti e devono essere sicuri per l’analisi in produzione. Su iOS viene utilizzato OSLogType.info, su Android — Log.i().
Warn — avvisi su problemi potenziali. L’applicazione continua a funzionare, ma la situazione richiede attenzione: dimensione della cache vicina al limite, versione API obsoleta, risposta di rete lenta, tentativo di riconnessione. Su Android — Log.w(), su iOS — OSLogType.default (per avvisi).
Error — errori critici in cui l’applicazione non può eseguire l’operazione richiesta ma continua a funzionare: richiesta API fallita, perdita di connessione, errore di scrittura nel database, mancanza di autorizzazioni. Su iOS, per gli errori viene utilizzato OSLogType.error, su Android — Log.e().
Assert (WTF) — il livello più alto, che indica una situazione che “non può accadere.” Utilizzato per registrare bug che violano gli invarianti fondamentali del sistema. Su Android, i messaggi Assert non vengono visualizzati nelle build di rilascio per impostazione predefinita. Su iOS, WTF (What a Terrible Failure) viene gestito tramite OSLogType.fault.
Android Log API — il meccanismo di logging integrato del pacchetto android.util.Log. Fornisce 6 metodi statici: Log.v(), Log.d(), Log.i(), Log.w(), Log.e() e Log.wtf(). Ogni metodo riceve un tag (stringa identificativa della sorgente) e msg (testo del messaggio).
class UserRepository {
companion object {
private val TAG = "UserRepo"
}
suspend fun loadUser(id: String): User {
Log.d(TAG, "Caricamento utente con id: $id")
return try {
val response = api.fetchUser(id)
Log.i(TAG, "Utente caricato con successo")
response.toUser()
} catch (e: Exception) {
Log.e(TAG, "Errore durante il caricamento dell’utente: ${e.message}")
throw e
}
}
}
Filtraggio per livelli in Android Logcat viene effettuato tramite ADB: adb logcat *:E mostrerà solo i messaggi Error. Nelle build di produzione, tutte le chiamate Log.v() e Log.d() vengono rimosse da ProGuard/R8 quando la minificazione è attivata. Log.i(), Log.w() e Log.e() rimangono, quindi è importante non emettere dati sensibili attraverso questi metodi.
Per il filtraggio personalizzato in fase di esecuzione, Android fornisce Log.isLoggable(tag, level) — un metodo che verifica se il livello specificato è attivato per il tag dato. Ciò consente di attivare dinamicamente la registrazione dettagliata per un modulo specifico senza ricostruire l’applicazione.
OSLog — il sistema di logging unificato di Apple, che ha sostituito il deprecato NSLog. OSLog fornisce 5 livelli: debug, info, default (notice), error e fault. Il vantaggio principale è il logging strutturato con supporto per stringhe formattate e filtraggio dinamico tramite console.
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)")
}
}
Il sistema di filtraggio di OSLog funziona a livello di sistema operativo. I messaggi Debug vengono scritti solo quando il debugger è collegato o quando l’argomento -com.apple.CoreData.Logging.debug 1 è attivato. I messaggi Info vengono raccolti nella memoria del dispositivo (fino a 512 KB) e sono accessibili tramite Console.app. I messaggi Error e fault vengono scritti continuamente e sono disponibili per la raccolta tramite sistemi di crash reporting.
Una caratteristica importante di OSLog: stringhe formattate con segnaposto. Invece dell’interpolazione di stringhe Swift (che viene sempre valutata, indipendentemente dal livello), OSLog utilizza il formato os_log con %{public}@ e %{private}@ per distinguere i dati sensibili. I parametri privati vengono mascherati nei log di produzione.
La regola principale — un insieme minimo di livelli in produzione: Info, Warn, Error, Assert. Debug e Verbose devono essere disattivati. Il motivo non è tanto la sicurezza quanto le prestazioni: ogni chiamata di log consuma tempo CPU per formattare la stringa, anche se il messaggio non viene emesso.
Ottimizzazione critica — non utilizzare mai l’interpolazione di stringhe nelle chiamate di log. Se la stringa viene costruita prima della chiamata log(), il tempo CPU viene sprecato anche quando il livello è disattivato. Utilizza la formattazione pigra tramite lambda o condizioni di guardia.
Su Android, il metodo Log.isLoggable() serve a questo scopo; su OSLog, le stringhe formattate native con segnaposto sono supportate. Timber per Android risolve il problema tramite timber.log.Tree con verifica del livello all’interno dell’albero.
Remote Log Level — una pratica in cui il livello di logging viene controllato dal server tramite Firebase Remote Config o un servizio simile. Se si verifica un errore complesso in produzione, lo sviluppatore può attivare da remoto il logging Debug per un modulo specifico sui dispositivi di un gruppo selezionato di utenti.
Secondo Firebase, 2024, questa pratica riduce il tempo di diagnosi dei bug rari del 60% e consente di ottenere un quadro completo del problema senza installare una build di debug. La limitazione principale — il logging viene attivato solo al successivo avvio dell’applicazione dopo aver ricevuto la configurazione.
BuildConfig.DEBUG su Android e #if DEBUG su Swift sono meccanismi standard di compilazione condizionale che disattivano i livelli di debug nelle build di rilascio. Per un’architettura pulita, si consiglia di spostare la selezione del Log Level in un contenitore DI o in una factory di logger per non appesantire la logica di business con direttive condizionali.
Prima regola — ogni chiamata di log dovrebbe rispondere alla domanda “chi, cosa, quando.” Chi — il componente o modulo (tag su Android, category su iOS). Cosa — l’evento specifico o il cambiamento di stato. Quando — il timestamp, aggiunto automaticamente dal sistema di logging.
Seconda regola — non registrare dati sensibili attraverso Info e superiori. Password, token, email, numeri di telefono, coordinate geografiche precise sono categoricamente vietati in qualsiasi log che finisca in produzione. Se necessario, utilizza il mascheramento: “email: us***@example.com.”
Terza regola — il livello Warn è responsabilità dello sviluppatore, Error — del team. Warn significa “qui c’è un problema potenziale, tienilo d’occhio.” Error significa “qui c’è un problema, correggilo.” Non utilizzare Error per situazioni previste e gestite (ad esempio, un errore API 404).
Quarta regola — coerenza. L’intero progetto dovrebbe utilizzare convenzioni di denominazione unificate per tag e categorie. Si raccomanda ClassName.methodName per i tag Android e module.subsystem per le categorie iOS. Ciò consente di filtrare rapidamente i log per componente.
Quinta regola — testa i tuoi log. Nei test unitari, verifica che il Log Level corretto venga chiamato in scenari specifici. Esistono liberie mock di logging per questo scopo: Mockito per Android, Cuckoo per iOS. Verificare i livelli nei test impedisce la fuga di messaggi di debug in produzione.
Domande frequenti
Consumo accelerato della batteria e scritture eccessive su disco. Ogni log Debug formatta una stringa e scrive dati nel buffer. Su dispositivi con memoria Flash, ciò accelera l’usura dello storage. Inoltre, i log Debug possono contenere dati sensibili che non dovrebbero essere visibili in produzione.
Debug — per il corpo della richiesta e risposta, intestazioni e codice di stato. Info — per il fatto della richiesta completata (URL, metodo, durata). Error — per richieste fallite con codici 4xx/5xx. Non utilizzare mai Verbose per i log di rete in produzione.
OSLogType.default (livello notice) — messaggi di importanza media, salvati nel log di sistema e visibili in Console.app. OSLogType.info — messaggi tecnici, non salvati permanentemente, disponibili solo durante la profilazione attiva tramite Instruments.
R8/ProGuard rimuove Log.v() e Log.d() quando la minificazione è attivata nelle build di rilascio. Log.i(), Log.w() e Log.e() vengono preservati. Per la rimozione completa di tutti i log, è necessaria una regola personalizzata -assumenosideeffects class android.util.Log con tutti i livelli specificati.
No — la registrazione eccessiva compromette la leggibilità e le prestazioni. Registra l’ingresso solo nei metodi complessi o asincroni. Per i metodi sincroni, un singolo log al punto di ritorno o di errore è sufficiente. Utilizza il livello Debug per la tracciatura delle chiamate.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche