Come abilitare / disabilitare i livelli di registro in Android?


149

Sto avendo molte dichiarazioni di registrazione per il debug, ad esempio.

Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");

durante la distribuzione di questa applicazione sul telefono del dispositivo, desidero disattivare la registrazione dettagliata da dove posso abilitare / disabilitare la registrazione.


Possibile duplicato dei livelli di registrazione Android

Risposte:


80

Un modo comune è creare un int chiamato loglevel e definirne il livello di debug in base a loglevel.

public static int LOGLEVEL = 2;
public static boolean ERROR = LOGLEVEL > 0;
public static boolean WARN = LOGLEVEL > 1;
...
public static boolean VERBOSE = LOGLEVEL > 4;

    if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown
    if (WARN) Log.w(TAG, "WARNING HERE");    // Still goes through

Successivamente, è possibile modificare LOGLEVEL per tutti i livelli di output di debug.


1
bello, ma come disabiliteresti DEBUG nel tuo esempio, ma continui a mostrare avvisi ...
Andre Bossard

1
Le istruzioni if ​​non finiscono nel codice byte .apk? Pensavo che volessimo (generalmente) disattivare la registrazione quando l'applicazione era distribuita, ma l'istruzione if non sarebbe stata rimossa.
chessofnerd,

2
nel tuo esempio, verranno mostrati i messaggi DEBUG, mentre i WARN no? non vorresti normalmente opporre?
Sam,

15
Utilizzare BuildConfig.DEBUG invece di variabili personalizzate
hB0

1
@chessofnerd "in Java, il codice all'interno di if non farà nemmeno parte del codice compilato. Deve compilare, ma non verrà scritto nel bytecode compilato." stackoverflow.com/questions/7122723/...
stoooops

197

La documentazione di Android dice quanto segue sui livelli di registro :

Verbose non deve mai essere compilato in un'applicazione se non durante lo sviluppo. I registri di debug vengono compilati ma eliminati in fase di esecuzione. I log degli errori, degli avvisi e delle informazioni vengono sempre mantenuti.

Quindi potresti prendere in considerazione la possibilità di eliminare il registro delle istruzioni di log dettagliato, eventualmente usando ProGuard come suggerito in un'altra risposta .

Secondo la documentazione, è possibile configurare la registrazione su un dispositivo di sviluppo utilizzando Proprietà di sistema. La proprietà di set è log.tag.<YourTag>e dovrebbe essere impostato su uno dei seguenti valori: VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, o SUPPRESS. Maggiori informazioni su questo sono disponibili nella documentazione per il isLoggable()metodo.

È possibile impostare temporaneamente le proprietà utilizzando il setpropcomando Per esempio:

C:\android>adb shell setprop log.tag.MyAppTag WARN
C:\android>adb shell getprop log.tag.MyAppTag
WARN

In alternativa, puoi specificarli nel file '/data/local.prop' come segue:

log.tag.MyAppTag=WARN

Le versioni successive di Android sembrano richiedere che /data/local.prop sia di sola lettura . Questo file viene letto all'avvio, quindi dovrai riavviarlo dopo averlo aggiornato. Se /data/local.propè scrivibile dal mondo, sarà probabilmente ignorato.

Infine, puoi impostarli a livello di codice usando il System.setProperty()metodo .


4
Ho avuto la stessa esperienza; i documenti API non sono abbastanza chiari su come dovrebbe funzionare e sembrano persino menzionare la maggior parte delle android.util.Configcostanti che sono state deprecate. I valori codificati specificati nei documenti API sono inutili in quanto (presumibilmente) variano in base alla build. Quindi il percorso ProGuard sembrava la soluzione migliore per noi.
Christopher Orr,

3
Hai avuto fortuna a configurare la registrazione Android usando il file /data/local.prop, il metodo setprop o con un System.setProperty? Ho un bel po 'di problemi a ottenere un Log.isLoggable (TAG, VERBOSE) per restituire true per me.
seanoshea,

2
Ho fatto funzionare il debug Android. Il trucco è che quando si chiama qualcosa come Log.d ("xyz") il messaggio viene scritto in logcat anche se il debug è disabilitato per il logger. Ciò significa che il filtraggio generalmente avviene dopo essere stato scritto. Per filtrare prima qualcosa come Log.isLoggable (TAG, Log.VERBOSE)) {Log.v (TAG, "il mio messaggio di registro"); } è necessario. Questo è generalmente piuttosto noioso. Uso una versione modificata di slf4j-android per ottenere quello che voglio.
concordato il

2
@Dave sei mai riuscito a far funzionare correttamente il metodo local.prop. Inoltre non riesco a farlo funzionare, ho creato una voce log.tag.test = INFO e poi ho provato a cambiarlo eseguendo setprop log.tag.test SUPPRESS dalla shell adb e non cambia nulla. Anche l'utilizzo di System.getProperty e System.setProperty non fa nulla. Volevo ricevere un aggiornamento da te. Grazie.
jjNford,

2
+1 per il commento "i documenti API non sono abbastanza chiari su come dovrebbe funzionare".
Alan,

90

Il modo più semplice è probabilmente di eseguire il JAR compilato tramite ProGuard prima della distribuzione, con una configurazione come:

-assumenosideeffects class android.util.Log {
    public static int v(...);
}

Questo, oltre a tutte le altre ottimizzazioni di ProGuard, rimuoverà qualsiasi istruzione di registro dettagliata direttamente dal bytecode.


farlo contiene qualsiasi file log.property dove possiamo definire le impostazioni.
d-man

1
eliminare le linee con proguard significa che le tracce dello stack dalla produzione potrebbero non allinearsi al codice.
larham1,

3
@ larham1: ProGuard agisce sul bytecode, quindi immagino che la rimozione delle chiamate di registrazione non modificherebbe i metadati del numero di linea incorporato.
Christopher Orr

19
Attenzione: anche se la chiamata effettiva a Log.v () viene rimossa, i suoi argomenti vengono comunque valutati. Quindi, se hai qualche chiamata di metodo costosa all'interno, ad esempio Log.v (TAG, generateLog ()), ciò potrebbe compromettere le tue prestazioni se si trova in un percorso di hot code. Anche cose come toString () o String.format () possono essere significative.
Błażej Czapp,

4
@GaneshKrishnan No, non è vero. La chiamata a Log.v () viene rimossa ma, per impostazione predefinita, le chiamate di metodo per creare la stringa non verranno rimosse. Vedi questa risposta dall'autore di ProGuard: stackoverflow.com/a/6023505/234938
Christopher Orr

18

Ho preso un percorso semplice: la creazione di una classe wrapper che utilizza anche elenchi di parametri variabili.

 public class Log{
        public static int LEVEL = android.util.Log.WARN;


    static public void d(String tag, String msgFormat, Object...args)
    {
        if (LEVEL<=android.util.Log.DEBUG)
        {
            android.util.Log.d(tag, String.format(msgFormat, args));
        }
    }

    static public void d(String tag, Throwable t, String msgFormat, Object...args)
    {
        if (LEVEL<=android.util.Log.DEBUG)
        {
            android.util.Log.d(tag, String.format(msgFormat, args), t);
        }
    }

    //...other level logging functions snipped

1
Come ho già detto sopra. Ho usato una versione modificata di slf4j-android per implementare questa tecnica.
phreed

3
C'è una grande preoccupazione al riguardo, vedi stackoverflow.com/questions/2446248/…
OneWorld

10

Il modo migliore è utilizzare l'API SLF4J + parte della sua implementazione.

Per le applicazioni Android è possibile utilizzare quanto segue:

  1. Android Logger è l'implementazione SLF4J leggera ma facile da configurare (<50 Kb).
  2. LOGBack è l'implementazione più potente e ottimizzata ma la sua dimensione è di circa 1 Mb.
  3. Qualsiasi altro per i tuoi gusti: slf4j-android, slf4android.

2
Su Android, dovresti usarlo logback-android(poiché il logbackcorretto è incompatibile). logback-android-1.0.10-1.jarè 429 KB, il che non è male considerando le funzionalità fornite, ma la maggior parte degli sviluppatori userebbe Proguard per ottimizzare comunque la propria applicazione.
tony19,

Questo non ti salva dall'uso delle istruzioni if ​​per controllare il livello di registro prima della registrazione. Vedi stackoverflow.com/questions/4958860/…
OneWorld

8

Dovresti usare

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "my log message");
    }

2
Come configurare l'output di isLoggable? Il debug e il verbose non sono registrabili quando isDebugable è impostato su manifest in manifest?
OneWorld,

5

L'eliminazione della registrazione con proguard (vedi risposta da @Christopher) è stata facile e veloce, ma ha causato la mancata corrispondenza dell'origine da parte delle tracce dello stack dalla produzione in caso di registrazione di debug nel file.

Invece, ecco una tecnica che utilizza diversi livelli di registrazione nello sviluppo rispetto alla produzione, supponendo che proguard sia usato solo nella produzione. Riconosce la produzione vedendo se proguard ha rinominato un determinato nome di classe (nell'esempio, utilizzo "com.foo.Bar" - lo sostituiresti con un nome di classe completo che sai verrà rinominato da proguard).

Questa tecnica utilizza la registrazione dei beni comuni.

private void initLogging() {
    Level level = Level.WARNING;
    try {
        // in production, the shrinker/obfuscator proguard will change the
        // name of this class (and many others) so in development, this
        // class WILL exist as named, and we will have debug level
        Class.forName("com.foo.Bar");
        level = Level.FINE;
    } catch (Throwable t) {
        // no problem, we are in production mode
    }
    Handler[] handlers = Logger.getLogger("").getHandlers();
    for (Handler handler : handlers) {
        Log.d("log init", "handler: " + handler.getClass().getName());
        handler.setLevel(level);
    }
}


3

C'è una piccola sostituzione drop-in per la classe Log Android standard - https://github.com/zserge/log

Fondamentalmente tutto ciò che devi fare è sostituire le importazioni da android.util.Loga trikita.log.Log. Quindi, nel tuo Application.onCreate()o in qualche inizializzatore statico, controlla la BuilConfig.DEBUGo qualsiasi altra bandiera e usa Log.level(Log.D)o Log.level(Log.E)per cambiare il livello minimo di registro. È possibile utilizzare Log.useLog(false)per disabilitare la registrazione a tutti.


2

Potrebbe essere possibile vedere questa classe di estensione del registro: https://github.com/dbauduin/Android-Tools/tree/master/logs .

Ti consente di avere un controllo accurato sui log. Ad esempio, è possibile disabilitare tutti i registri o solo i registri di alcuni pacchetti o classi.

Inoltre, aggiunge alcune funzionalità utili (ad esempio non è necessario passare un tag per ciascun registro).


2

Ho creato un'utilità / wrapper che risolve questo problema + altri problemi comuni relativi alla registrazione.

Un'utilità di debug con le seguenti funzionalità:

  • Le solite funzionalità fornite dalla classe Log racchiuse tra LogMode s.
  • Registri di entrata-uscita del metodo: può essere disattivato da un interruttore
  • Debugging selettivo: debug di classi specifiche.
  • Misura del tempo di esecuzione del metodo: misura il tempo di esecuzione per i singoli metodi e il tempo collettivo impiegato per tutti i metodi di una classe.

Come usare?

  • Includi la classe nel tuo progetto.
  • Usalo come usi i metodi android.util.Log, per cominciare.
  • Utilizzare la funzione Registri entrata-uscita effettuando le chiamate ai metodi entry_log () - exit_log () all'inizio e alla fine dei metodi nella propria app.

Ho cercato di rendere la documentazione autosufficiente.

Suggerimenti per migliorare questa utility sono benvenuti.

Gratuito da usare / condividere.

Scaricalo da GitHub .


2

Ecco una soluzione più complessa. Otterrai la traccia dello stack completo e il metodo toString () verrà chiamato solo se necessario (Prestazioni). L'attributo BuildConfig.DEBUG sarà falso nella modalità di produzione, quindi verranno rimossi tutti i registri di traccia e debug. Il compilatore hot spot ha la possibilità di rimuovere le chiamate a causa delle proprietà statiche finali.

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import android.util.Log;

public class Logger {

    public enum Level {
        error, warn, info, debug, trace
    }

    private static final String DEFAULT_TAG = "Project";

    private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info;

    private static boolean isEnabled(Level l) {
        return CURRENT_LEVEL.compareTo(l) >= 0;
    }

    static {
        Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name());
    }

    private String classname = DEFAULT_TAG;

    public void setClassName(Class<?> c) {
        classname = c.getSimpleName();
    }

    public String getClassname() {
        return classname;
    }

    public boolean isError() {
        return isEnabled(Level.error);
    }

    public boolean isWarn() {
        return isEnabled(Level.warn);
    }

    public boolean isInfo() {
        return isEnabled(Level.info);
    }

    public boolean isDebug() {
        return isEnabled(Level.debug);
    }

    public boolean isTrace() {
        return isEnabled(Level.trace);
    }

    public void error(Object... args) {
        if (isError()) Log.e(buildTag(), build(args));
    }

    public void warn(Object... args) {
        if (isWarn()) Log.w(buildTag(), build(args));
    }

    public void info(Object... args) {
        if (isInfo()) Log.i(buildTag(), build(args));
    }

    public void debug(Object... args) {
        if (isDebug()) Log.d(buildTag(), build(args));
    }

    public void trace(Object... args) {
        if (isTrace()) Log.v(buildTag(), build(args));
    }

    public void error(String msg, Throwable t) {
        if (isError()) error(buildTag(), msg, stackToString(t));
    }

    public void warn(String msg, Throwable t) {
        if (isWarn()) warn(buildTag(), msg, stackToString(t));
    }

    public void info(String msg, Throwable t) {
        if (isInfo()) info(buildTag(), msg, stackToString(t));
    }

    public void debug(String msg, Throwable t) {
        if (isDebug()) debug(buildTag(), msg, stackToString(t));
    }

    public void trace(String msg, Throwable t) {
        if (isTrace()) trace(buildTag(), msg, stackToString(t));
    }

    private String buildTag() {
        String tag ;
        if (BuildConfig.DEBUG) {
            StringBuilder b = new StringBuilder(20);
            b.append(getClassname());

            StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4];
            if (stackEntry != null) {
                b.append('.');
                b.append(stackEntry.getMethodName());
                b.append(':');
                b.append(stackEntry.getLineNumber());
            }
            tag = b.toString();
        } else {
            tag = DEFAULT_TAG;
        }
    }

    private String build(Object... args) {
        if (args == null) {
            return "null";
        } else {
            StringBuilder b = new StringBuilder(args.length * 10);
            for (Object arg : args) {
                if (arg == null) {
                    b.append("null");
                } else {
                    b.append(arg);
                }
            }
            return b.toString();
        }
    }

    private String stackToString(Throwable t) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(500);
        baos.toString();
        t.printStackTrace(new PrintStream(baos));
        return baos.toString();
    }
}

usare così:

Loggor log = new Logger();
Map foo = ...
List bar = ...
log.error("Foo:", foo, "bar:", bar);
// bad example (avoid something like this)
// log.error("Foo:" + " foo.toString() + "bar:" + bar); 

1

In uno scenario di registrazione molto semplice, in cui stai letteralmente solo cercando di scrivere sulla console durante lo sviluppo per scopi di debug, potrebbe essere più semplice fare una ricerca e sostituirla prima della generazione della tua produzione e commentare tutte le chiamate a Log o Sistema. out.println.

Ad esempio, supponendo che non sia stato utilizzato il "Registro". ovunque al di fuori di una chiamata a Log.d o Log.e, ecc., è possibile semplicemente trovare e sostituire l'intera soluzione per sostituire "Log". con "// Log". per commentare tutte le tue chiamate di registrazione, o nel mio caso sto solo usando System.out.println ovunque, quindi prima di andare in produzione farò semplicemente una ricerca completa e sostituirò "System.out.println" e sostituirò con "//System.out.println".

So che questo non è l'ideale, e sarebbe bello se la possibilità di trovare e commentare le chiamate a Log e System.out.println fosse integrata in Eclipse, ma fino a quando ciò non accada il modo più semplice, veloce e migliore per farlo è commentare cercando e sostituendo. Se lo fai, non devi preoccuparti di non corrispondere ai numeri delle righe di traccia dello stack, perché stai modificando il codice sorgente e non stai aggiungendo alcun overhead controllando una configurazione a livello di registro, ecc.


1

Nelle mie app ho una classe che avvolge la classe Log che ha una var booleana statica chiamata "stato". Durante il mio codice, controllo il valore della variabile "state" usando un metodo statico prima di scrivere sul registro. Ho quindi un metodo statico per impostare la variabile "state" che assicura che il valore sia comune in tutte le istanze create dall'app. Ciò significa che posso abilitare o disabilitare tutta la registrazione per l'app in una chiamata, anche quando l'app è in esecuzione. Utile per le chiamate di supporto ... Significa che devi attenersi alle tue pistole durante il debug e non regredire utilizzando la classe Log standard sebbene ...

È anche utile (conveniente) che Java interpreti un var booleano come falso se non gli è stato assegnato un valore, il che significa che può essere lasciato falso fino a quando non è necessario attivare la registrazione :-)


1

Possiamo usare la classe Lognel nostro componente locale e definire i metodi come v / i / e / d. Sulla base della necessità, possiamo effettuare ulteriori chiamate.
l'esempio è mostrato sotto.

    public class Log{
        private static boolean TAG = false;
        public static void d(String enable_tag, String message,Object...args){
            if(TAG)
            android.util.Log.d(enable_tag, message+args);
        }
        public static void e(String enable_tag, String message,Object...args){
            if(TAG)
            android.util.Log.e(enable_tag, message+args);
        }
        public static void v(String enable_tag, String message,Object...args){
            if(TAG)
            android.util.Log.v(enable_tag, message+args);
        }
    }
    if we do not need any print(s), at-all make TAG as false for all else 
    remove the check for type of Log (say Log.d).
    as 
    public static void i(String enable_tag, String message,Object...args){
    //      if(TAG)
            android.util.Log.i(enable_tag, message+args);
    }

qui il messaggio è per stringed ed argsè il valore che si desidera stampare.


0

Per me è spesso utile poter impostare diversi livelli di registro per ciascun TAG.

Sto usando questa classe wrapper molto semplice:

public class Log2 {

    public enum LogLevels {
        VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN(
                android.util.Log.WARN), ERROR(android.util.Log.ERROR);

        int level;

        private LogLevels(int logLevel) {
            level = logLevel;
        }

        public int getLevel() {
            return level;
        }
    };

    static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>();

    public static void setLogLevel(String tag, LogLevels level) {
        logLevels.put(tag, level.getLevel());
    }

    public static int v(String tag, String msg) {
        return Log2.v(tag, msg, null);
    }

    public static int v(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.VERBOSE) {
                return -1;
            }
        }
        return Log.v(tag, msg, tr);
    }

    public static int d(String tag, String msg) {
        return Log2.d(tag, msg, null);
    }

    public static int d(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.DEBUG) {
                return -1;
            }
        }
        return Log.d(tag, msg);
    }

    public static int i(String tag, String msg) {
        return Log2.i(tag, msg, null);
    }

    public static int i(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.INFO) {
                return -1;
            }
        }
        return Log.i(tag, msg);
    }

    public static int w(String tag, String msg) {
        return Log2.w(tag, msg, null);
    }

    public static int w(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.WARN) {
                return -1;
            }
        }
        return Log.w(tag, msg, tr);
    }

    public static int e(String tag, String msg) {
        return Log2.e(tag, msg, null);
    }

    public static int e(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.ERROR) {
                return -1;
            }
        }
        return Log.e(tag, msg, tr);
    }

}

Ora imposta il livello di registro per TAG all'inizio di ogni classe:

Log2.setLogLevel(TAG, LogLevels.INFO);

0

Un altro modo è utilizzare una piattaforma di registrazione che ha le capacità di aprire e chiudere i registri. Questo può dare molta flessibilità a volte anche su un'app di produzione quali registri dovrebbero essere aperti e quali chiusi a seconda di quali problemi hai ad esempio:

  • taglialegna
  • Shipbook (dichiarazione di non responsabilità: sono l'autore di questo pacchetto)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.