Logging nelle applicazioni Android in modo veloce e potente

L

Durante lo sviluppo di un’applicazione, un ruolo importante viene riservato alla possibilità di utilizzare dei log per poter monitorare e analizzare il workflow delle operazioni.

Delle varie librerie presenti in rete quella che ho trovato più funzionale, flessibile e che mi sento di consigliarvi è la libreria “Logger di orhanobut”, presente su GitHub al seguente indirizzo:

githuborhanobut/logger

 

Configurazione

Nel file build.gradle del modulo dell’applicazione andiamo ad includere la libreria aggiungendo questa dichiarazione nelle dependencies:

implementation 'com.orhanobut:logger:2.2.0'

Per quanto riguarda la sua inizializzazione suggerisco di crearci una nostra classe Application ( se già non è presente ) sovrascrivendo il metodo onCreate() nel seguente modo:

override fun onCreate() {
    super.onCreate()
    Logger.addLogAdapter(AndroidLogAdapter())
}

Dopo questi semplici passi siamo già pronti ad utilizzare la libreria per eseguire il logging all’interno della nostra applicazione. Vediamo ora come utilizzarla e quali vantaggi ci offre.

Output

Come prima cosa ci soffermeremo su come l’output sarà visualizzato in Logcat. Utilizzando la configurazione standard quello che vedremo sarà simile a questo:

Le informazioni che ci vengono proposte sono nell’ordine:

  • Thread nel quale ci troviamo
  • Stack di chiamata ( di default 2 metodi )
  • Testo del log

Il valore “I/MY_LOGGER” rappresenta il livello del log ( I = Info ) e il tag ( in questo caso personalizzato con il valore MY_LOGGER, altrimenti PRETTY_LOGGER di default ). Come vedremo più avanti avremo la possibilità di personalizzare ogni aspetto dell’output come le informazioni sul thread, la profondità dello stacktrace, il tag, ecc…

Livelli di log

Oltre ai vari livelli di log tradizionali ( debug, error, warning, verbose e info ) l’autore ha voluto goliardicamente introdurre anche il livello wtf (non è quello che state pensando! l’acronimo sta per What a Terrible Failure).

Utilizzando il print out di default, cioè LogCat, ci sarà molto facile distinguere i vari livelli di log grazie ai colori. Ecco come i vari livelli appariranno in Android Studio:NB: se durante la realizzazione dell’app, l’output risulta monocromatico (bianco su nero), indipendentemente dal livello di log utilizzato, sarà sufficiente configurare i colori dal menu File -> Settings -> Editor -> Color Scheme -> Android LogCat

Collections

Una delle feature molto interessanti di questa libreria è la possibilità di poter loggare direttamente qualsiasi collection in modo chiaro e veloce, senza dover ricorrere ad un ciclo o altri sistemi per poterne visualizzare i valori.

In questo esempio ho istanziato una Map, un Set, una List e un Array con alcuni valori:

val map: Map<Int, String> = hashMapOf(Pair(1, "uno"), Pair(2, "due"), Pair(3, "tre"))
val set: Set<String> = setOf("a", "b", "c", "d")
val list: List<Float> = listOf(23.3f, 45.1f, 12f, 34.8f)
val array: Array<String> = arrayOf("giallo", "verde", "rosso", "blu")

Logger.d(map)
Logger.d(set)
Logger.d(list)
Logger.d(array)

Ecco come viene visualizzato il tutto in LogCat

Json e XML

Un altro aspetto interessante che potrà far felice molti programmatori è quello di poter gestire il log di stringhe contenenti oggetti json e xml visualizzando il contenuto formattato a dovere.

In questo esempio, il contenuto delle stringhe JSON/XML non risulta essere formattato:

val json: String = "{\"menu\": {\"id\": \"file\", \"value\": \"File\", \"popup\": { \"menuitem\": [ {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"}," +
                   "      {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"}, {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}] }}}"
val xml: String = "<menu id=\"file\" value=\"File\"><popup><menuitem value=\"New\" onclick=\"CreateNewDoc()\" />" +
                  "    <menuitem value=\"Open\" onclick=\"OpenDoc()\" /><menuitem value=\"Close\" onclick=\"CloseDoc()\" /></popup></menu>"

Logger.json(json)
Logger.xml(xml)

Tuttavia mediante l’utilizzo dei metodi json e xml l’output verrà formattato nel seguente modo:

Thread

Non poteva certo mancare un test riguardante la prima sezione che viene esposta nell’output, quella che riguarda il thread dal quale il log viene lanciato.

@Click
internal fun threadLogButtonClicked() {
    Logger.d("Log in main thread")
    backgroundWork()
}

@Background
open fun backgroundWork() {
    Logger.d("Log in background thread")
}

In questo caso di esempio viene lanciato un primo log sul click del button, mi aspetto quindi di essere sul Main Thread mentre il secondo log è posizionato in un altro thread. Le annotation @Click e @Background fanno parte di Android Annotations e del loro utilizzo abbiamo già parlato ampiamente nell’articolo Android annotations. Meno codice, più leggibilità. Per chi non le avesse mai utilizzate può semplicemente tradurre la chiamata backgroundWork() come la execute di un AsyncTask che nel metodo doInBackground esegue il secondo log.

Il risultato dell’esempio in LogCat sarà il seguente:Come ci si aspettava nel primo log viene indicato Thread: main mentre per il secondo troviamo Thread: pool-1-thread-1.

Configurazione avanzata

La personalizzazione dell’output risulta essere molto semplice e abbastanza flessibile. Finora abbiamo inizializzato il logger aggiungendo un adapter standard. Se desideriamo aggiungere o togliere verbosità a quanto ci viene stampato su LogCat dovremo solo definire un FormatStrategy configurandolo a nostro piacimento. Nel caso degli esempi proposti finora, l’output restituito viene generato con questo specifico strategy:

val formatStrategy = PrettyFormatStrategy.newBuilder()
    .showThreadInfo(true)  // Whether to show thread info or not. Default true
    .methodCount(2)         // How many method line to show. Default 2
    .methodOffset(5)        // Hides internal method calls up to offset. Default 5
    .logStrategy(LogcatLogStrategy()) // Changes the log strategy to print out. Default LogCat
    .tag("MY_LOGGER")   // Global tag for every log. Default PRETTY_LOGGER
    .build()

Utilizzando lo stesso esempio proposto nel paragrafo relativo alle collection, ecco invece come potrebbe apparire un log compatto configurandolo in questo modo:

val formatStrategy = PrettyFormatStrategy.newBuilder()
    .showThreadInfo(false)
    .methodCount(0)
    .methodOffset(0)
    .logStrategy(LogcatLogStrategy())
    .tag("MY_LOGGER")
    .build()

Log su file

Fino a questo momento abbiamo sempre utilizzato un log visualizzato su LogCat. La libreria ci mette anche a disposizione un adapter per poter scrivere tutto su filesystem.

L’adapter che dovremo andare ad utilizzare è il DiskLogAdapter che possiamo inizializzare nel seguente modo:

Logger.addLogAdapter(DiskLogAdapter())

Alcune precisazioni:

  • Per l’utilizzo di questo adapter è richiesto il permesso di scrittura da inserire nel Manifest.xml in questo modo
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • Il file viene generato su sdcard/logger in format csv (con la virgola come separatore dei valori)
  • Il tag di default è PRETTY_LOGGER ed è possibile modificarlo usando uno strategy personalizzato, nel caso del nostro esempio:
    val csvFormatStrategy = CsvFormatStrategy.newBuilder()
        .tag(LOGGER_TAG)
        .build()
    
    Logger.addLogAdapter(DiskLogAdapter(csvFormatStrategy))
  • Il nome del file di default è “logs_N.csv” dove N sta’ ad indicare il numero del file perché ne viene generato uno nuovo ogni circa 500K che equivalgono più o meno a 4000 righe.

 

Come possiamo notare, sia la configurazione sia l’utilizzo di questa libreria è molto semplice ma ci permettere poi di avere un utilizzo dei log molto completo e flessibile. Se proprio volessi trovare un neo, direi che avrei dato la possibilità di includere nello stesso “riquadro” log che vengono generati nello stesso metodo o impostando un gruppo o id in modo tale da ridurre l’output generato e far risultare il tutto un po più leggibile.

Tutto il codice riportato in questo articolo lo si può trovare sul mio account di GitHub nel repository android-logger.

A proposito di me

Gianluca Fattarsi

Perito informatico, esperienza come programmatore nell'ambito lavorativo dal lontano 2002. La sua formazione in Java spazia dal Front-End al Back-End con esperienze in gestione database e realizzazione di App Android in Kotlin. Hobby preferiti: Linux, lettura e World of Warcraft.

I nostri Partner

Gli articoli più letti

Articoli recenti

Commenti recenti