Android'de günlük düzeylerini nasıl etkinleştirebilir / devre dışı bırakabilirim?


149

Örneğin hata ayıklamak için günlük deyimleri çok yaşıyorum.

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

Bu uygulamayı cihaz telefonuna dağıtırken, günlüğe kaydetmeyi etkinleştirebileceğim / devre dışı bırakabileceğim ayrıntılı günlük kaydını kapatmak istiyorum.


Yanıtlar:


80

Yaygın bir yöntem, loglevel adında bir int yapmak ve loglevel'e göre hata ayıklama seviyesini tanımlamaktır.

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

Daha sonra, tüm hata ayıklama çıkış seviyesi için LOGLEVEL'i değiştirebilirsiniz.


1
güzel, ama örneğinizde DEBUG'ı nasıl devre dışı bırakırsınız, ancak yine de uyarılar gösterir ....
Andre Bossard

1
İf ifadeleri .apk bayt koduyla sonuçlanmaz mı? Ben (genellikle) uygulama dağıtıldığında günlüğü kapatmak istediğini düşündüm ama if deyimi kaldırılamaz.
chessofnerd

2
örneğinizde DEBUG mesajları gösterilirken WARN'ler görünmez mi? normalde karşı çıkmak istemez miydin?
Sam

15
Özel değişkenler yerine BuildConfig.DEBUG kullanın
hB0

1
@chessofnerd "Java, eğer içindeki kod derlenmiş kodun bir parçası bile olmayacak. Derlenmesi gerekir, ancak derlenmiş bayt koduna yazılmaz." stackoverflow.com/questions/7122723/…
stoooops 22.11.2013

197

Android Belgeler Günlüğü Düzeyleri hakkında şunları söylüyor :

Verbose, geliştirme haricinde asla bir uygulamada derlenmemelidir. Hata ayıklama günlükleri derlenir, ancak çalışma zamanında çıkarılır. Hata, uyarı ve bilgi günlükleri her zaman saklanır.

Bu nedenle, büyük olasılıkla başka bir cevapta önerildiği gibi ProGuard'ı kullanarak günlük ayrıntılı ifadelerini çıkarmayı düşünebilirsiniz .

Belgelere göre, Sistem Özellikleri'ni kullanarak bir geliştirme aygıtında günlüğe kaydetmeyi yapılandırabilirsiniz. Kümesine aittir log.tag.<YourTag>ve şu değerlerden birine ayarlanmalıdır: VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, veya SUPPRESS. Bununla ilgili daha fazla bilgi isLoggable()yöntemin belgelerinde mevcuttur .

setpropKomutu kullanarak özellikleri geçici olarak ayarlayabilirsiniz . Örneğin:

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

Alternatif olarak, bunları '/data/local.prop' dosyasında aşağıdaki gibi belirtebilirsiniz:

log.tag.MyAppTag=WARN

Android'in sonraki sürümlerinde /data/local.prop'un salt okunur olmasını gerektirir . Bu dosya önyükleme sırasında okunur, bu nedenle güncelledikten sonra yeniden başlatmanız gerekir. Eğer /data/local.propdünya yazılabilirse, büyük olasılıkla görmezden gelinecektir.

Son olarak, System.setProperty()yöntemi kullanarak bunları programlı olarak ayarlayabilirsiniz .


4
Aynı deneyimi yaşadım; API dokümanları bunun tam olarak nasıl çalışması gerektiği konusunda oldukça belirsizdir ve hatta sabitlenmiş olan android.util.Configsabitlerin çoğundan bahsetmektedir . API belgelerinde belirtilen sabit kodlanmış değerler işe yaramaz çünkü bunlar (sözde) yapıya göre değişir. Bu nedenle ProGuard rotası bizim için en iyi çözüm gibi görünüyordu.
Christopher Orr

3
Android günlüğünü /data/local.prop dosyasını, setprop yöntemini veya System.setProperty'yi kullanarak yapılandırma konusunda şansınız oldu mu? Benim için doğru dönmek için bir Log.isLoggable (TAG, VERBOSE) alma konusunda biraz sorun yaşıyorum.
seanoshea

2
Android hata ayıklama çalışma aldım. Hile, Log.d ("xyz") gibi bir şey çağırdığınızda, hata ayıklayıcı için hata ayıklama devre dışı olsa bile iletinin logcat'e yazılmasıdır. Bu, filtrelemenin genellikle yazıldıktan sonra gerçekleştiği anlamına gelir. Log.isLoggable (TAG, Log.VERBOSE)) {Log.v (TAG, "günlük iletim") gibi bir şeyden önce filtrelemek için; } gereklidir. Bu genellikle oldukça yorucudur. İstediğimi elde etmek için slf4j-android'in değiştirilmiş bir sürümünü kullanıyorum.
phreed

2
@ Local.prop yönteminin düzgün çalışmasını sağlayabildiniz. Ben de bu işi yapamıyorum, bir giriş log.tag.test = INFO oluşturduk ve sonra adb kabuk setprop log.tag.test SUPPRESS çalışan değiştirmeye çalıştım ve hiçbir şey değişmez. Ayrıca System.getProperty ve System.setProperty kullanmak hiçbir şey yapmaz. Sizden bir güncelleme almak istedim. Teşekkürler.
jjNford

2
+1 yorum için "API dokümanları bunun tam olarak nasıl çalışması gerektiği konusunda oldukça net değil".
Alan

90

En kolay yol muhtemelen derlenmiş JAR'ınızı çalıştırmaktır. JAR'ınızı konuşlandırmadan önce ProGuard :

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

Bu, diğer tüm ProGuard optimizasyonlarının yanı sıra, ayrıntılı günlük ifadelerini doğrudan bayt kodundan kaldıracaktır.


ayarları tanımlayabileceğimiz herhangi bir log.property dosyası içeriyorsa.
d-man

1
satırları proguard ile sıyırma, yığın izlerinizin üretimden itibaren kodunuzla uyuşmayabileceği anlamına gelir.
larham1

3
@ larham1: ProGuard bayt kodu üzerinde çalışır, bu yüzden günlük aramalarını kaldırmanın gömülü satır numarası meta verilerini değiştirmeyeceğini hayal ediyorum.
Christopher Orr

19
Buna dikkat edin - gerçek Log.v () çağrısı kaldırılsa bile argümanları hala değerlendirilir. Bu nedenle, örneğin Log.v (TAG, createLog ()) içinde bazı pahalı yöntem çağrılarınız varsa, bazı sıcak kod yolundaysa performansınıza zarar verebilir. ToString () veya String.format () gibi şeyler bile önemli olabilir.
Błażej Czapp

4
@GaneshKrishnan Hayır, bu doğru değil. Log.v () çağrısı kaldırılır, ancak varsayılan olarak dizeyi oluşturmak için yöntem çağrıları kaldırılmaz. ProGuard'ın yazarından bu yanıta bakın: stackoverflow.com/a/6023505/234938
Christopher Orr

18

Basit bir yol aldım - değişken parametre listelerini de kullanan bir sarıcı sınıfı oluşturdum.

 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
Yukarıda belirttiğim gibi. Bu tekniği uygulamak için slf4j-android'in değiştirilmiş bir versiyonunu kullandım.
phreed

3
Bununla ilgili büyük bir endişe var, bkz. Stackoverflow.com/questions/2446248/…
OneWorld

10

Daha iyi bir yol SLF4J API + 'nın bazı uygulamalarını kullanmaktır.

Android uygulamaları için aşağıdakileri kullanabilirsiniz:

  1. Android Logger , hafif ancak yapılandırması kolay SLF4J uygulamasıdır (<50 Kb).
  2. LOGBack en güçlü ve optimize edilmiş bir uygulamadır ancak boyutu yaklaşık 1 Mb'dir.
  3. Zevkinize göre başka: slf4j-android, slf4android.

2
Android'de, kullanmanız gerekir logback-android( logbackuygun olmadığı için uyumsuzdur). logback-android-1.0.10-1.jar429 KB, sağlanan özellikler göz önüne alındığında çok da kötü değil, ancak çoğu geliştirici Proguard'ı zaten uygulamalarını optimize etmek için kullanacak.
tony19

Bu, günlüğe kaydetmeden önce günlük düzeyini kontrol etmek için if ifadelerini kullanmaktan kurtarmaz. Bkz. Stackoverflow.com/questions/4958860/…
OneWorld

8

Kullanmalısın

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

2
İsLoggable çıkışı nasıl yapılandırılır? İsDebugable, manifest'te false olarak ayarlandığında hata ayıklama ve ayrıntılı günlük kaydı yapılamaz mı?
OneWorld

5

Proguard ile günlüğe kaydetmeyi kaldırmak (@Christopher'dan gelen cevaba bakın) kolay ve hızlıydı, ancak dosyada herhangi bir hata ayıklama günlüğü varsa, yığın izlerinin üretimden kaynakla eşleşmemesine neden oldu.

Bunun yerine, korumanın sadece üretimde kullanıldığı varsayılarak, geliştirme ile üretimde farklı loglama düzeyleri kullanan bir teknik. Proguard'ın belirli bir sınıf adını yeniden adlandırıp adlandırmadığını görerek üretimi tanır (örnekte "com.foo.Bar" kullanıyorum - bunu proguard tarafından yeniden adlandırılacağını bildiğiniz tam nitelikli bir sınıf adıyla değiştirirsiniz).

Bu teknik, ortak günlük kaydını kullanır.

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

Standart android Log sınıfı için küçük bir drop-in değiştirme var - https://github.com/zserge/log

Temelde yapmanız gereken tüm ithalatı değiştirmektir android.util.Logiçin trikita.log.Log. Sonra sizin Application.onCreate()veya bazı statik initalizer BuilConfig.DEBUGveya diğer herhangi bir bayrak için kontrol edin ve en az günlük seviyesini değiştirmek için Log.level(Log.D)veya Log.level(Log.E)kullanın. Log.useLog(false)Günlük tutmayı hiç devre dışı bırakmak için kullanabilirsiniz .


2

Belki bu Günlük uzantısı sınıfını görebilirsiniz: https://github.com/dbauduin/Android-Tools/tree/master/logs .

Günlükler üzerinde iyi bir kontrol sahibi olmanızı sağlar. Örneğin, tüm günlükleri veya yalnızca bazı paketlerin veya sınıfların günlüklerini devre dışı bırakabilirsiniz.

Dahası, bazı kullanışlı işlevler ekler (örneğin, her günlük için bir etiket geçirmeniz gerekmez).


2

Bu sorunu + Günlük Kaydı ile ilgili diğer yaygın sorunları çözen bir Yardımcı Program / Sarıcı oluşturdum.

Aşağıdaki özelliklere sahip bir Hata Ayıklama yardımcı programı:

  • Log sınıfı tarafından sağlanan olağan özellikler, LogMode s tarafından sarılır .
  • Yöntem Giriş-Çıkış günlükleri: Bir anahtarla kapatılabilir
  • Seçici Hata Ayıklama: Belirli sınıflarda hata ayıklama.
  • Yöntem Yürütme Süresi Ölçümü: Bir sınıfın tüm yöntemlerine harcanan toplu sürenin yanı sıra tek tek yöntemler için Yürütme süresini ölçün.

Nasıl kullanılır?

  • Sınıfı projenize dahil edin.
  • Başlamak için android.util.Log yöntemlerini kullandığınız gibi kullanın.
  • Uygulamanızdaki yöntemlerin başına ve sonuna input_log () - exit_log () yöntemlerine çağrı yaparak Giriş-Çıkış günlükleri özelliğini kullanın.

Belgeleri kendi kendine yeterli kılmaya çalıştım.

Bu Yardımcı Programı geliştirmeye yönelik önerilerinizi bekliyoruz.

Kullanımı / paylaşılması ücretsiz.

GitHub'dan indirin .


2

İşte daha karmaşık bir çözüm. Tam yığın izlemesi alırsınız ve toString () yöntemi yalnızca gerektiğinde çağrılır (Performans). Yapım modunda BuildConfig.DEBUG özelliği false olur, böylece tüm izleme ve hata ayıklama günlükleri kaldırılır. Etkin nokta derleyici, son statik özelliklerden dolayı çağrıları kaldırma şansına sahiptir.

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();
    }
}

şöyle kullanın:

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

Kelimenin tam anlamıyla hata ayıklama amacıyla geliştirme sırasında konsola yazmaya çalıştığınız çok basit bir günlük kaydı senaryosunda, üretiminizi oluşturmadan ve Log veya System'a yapılan tüm çağrıları yorumlamadan önce sadece bir arama yapmak ve değiştirmek en kolay olabilir. out.println.

Örneğin, "Günlük" ü kullanmadığınız varsayılarak. Log.d veya Log.e, vb. çağrı dışında herhangi bir yerde, "Günlük" yerine tüm çözümü bulup değiştirebilirsiniz. "// Günlük" ile. tüm günlük aramalarınızı yorumlamak için, ya da benim durumumda her yerde System.out.println kullanıyorum, bu yüzden üretime geçmeden önce "System.out.println" için tam bir arama yapacağım ve değiştireceğim "//System.out.println".

Bunun ideal olmadığını biliyorum ve Log ve System.out.println çağrılarını bulma ve yorumlama yeteneği Eclipse içine yerleştirilmişse güzel olurdu, ancak bu gerçekleşene kadar bunu yapmanın en kolay ve en hızlı ve en iyi yolu arama ve değiştirme yoluyla yorum yapmak. Bunu yaparsanız, kaynak izleme kodunuzu düzenlediğinizden ve bazı günlük düzeyi yapılandırmasını vb. Kontrol ederek herhangi bir ek yük eklemediğiniz için yığın izleme satır numaralarını eşleşmemesi konusunda endişelenmeniz gerekmez.


1

Uygulamalarımda statik durumu olan "durum" adı verilen bir boole değişkenine sahip Log sınıfını saran bir sınıfım var. Benim kod boyunca gerçekten günlük için yazmadan önce statik bir yöntem kullanarak "state" değişkeninin değerini denetleyin. Sonra değeri app tarafından oluşturulan tüm durumlarda ortak olmasını sağlar "durum" değişkeni ayarlamak için statik bir yöntem var. Bu, Uygulama çalışırken bile Uygulama için tüm günlük kaydını etkinleştirebileceğim veya devre dışı bırakabileceğim anlamına gelir. Destek çağrıları için kullanışlıdır ... Bu, hata ayıklama sırasında silahlarınıza bağlı kalmanız ve yine de standart Log sınıfını kullanmamanız gerektiği anlamına gelir ...

Ayrıca, bir değer atanmamışsa Java'nın bir boole değişkenini false olarak yorumlaması da yararlıdır (yani), günlüğe kaydetmeyi açmanız gerekene kadar yanlış olarak bırakılabilir :-)


1

Sınıfı Logyerel bileşenimizde kullanabilir ve yöntemleri v / i / e / d olarak tanımlayabiliriz. İhtiyacımıza dayanarak daha fazla çağrı yapabiliriz.
örnek aşağıda gösterilmiştir.

    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);
    }

burada mesaj içindir stringve argsyazdırmak istediğiniz değerdir.


0

Benim için her bir ETİKET için farklı günlük seviyeleri ayarlayabilmek genellikle yararlıdır.

Bu çok basit sarmalayıcı sınıfını kullanıyorum:

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);
    }

}

Şimdi her sınıfın başında TAG başına günlük seviyesini ayarlayın:

Log2.setLogLevel(TAG, LogLevels.INFO);

0

Başka bir yol, günlükleri açma ve kapama yeteneklerine sahip bir günlük platformu kullanmaktır. Bu, örneğin hangi sorunlara sahip olduğunuza bağlı olarak hangi kayıtların açık olması ve kapalı olması gereken bir üretim uygulamasında bile çok fazla esneklik sağlayabilir:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.