Androidでログレベルを有効/無効にするにはどうすればよいですか?


149

たとえば、デバッグするログステートメントがたくさんあります。

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

このアプリケーションをデバイスの電話に展開するときに、ログを有効/無効にできる場所から詳細ログをオフにしたいと思います。


回答:


80

一般的な方法は、loglevelという名前のintを作成し、そのデバッグレベルを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

後で、すべてのデバッグ出力レベルのLOGLEVELを変更できます。


1
素敵な、しかしどのようにあなたは、まだあなたの例では無効にDEBUGを示すだろう警告....
アンドレボサード

1
ifステートメントが.apkバイトコードになってしまうのではないでしょうか。アプリケーションのデプロイ時に(通常)ロギングをオフにしたいと思っていましたが、ifステートメントは削除されませんでした。
chessofnerd

2
あなたの例では、デバッグメッセージが表示されますが、警告は表示されませんか?普段は反対にしたくないですか?
サム

15
カスタム変数の代わりにBuildConfig.DEBUGを使用します
hB0

1
@chessofnerd "Javaでは、if内のコードはコンパイルされたコードの一部でさえありません。コンパイルする必要がありますが、コンパイルされたバイトコードには書き込まれません。" stackoverflow.com/questions/7122723/...
stoooops

197

Androidのドキュメントには、ログレベルについて次のことを言います

開発中以外は、詳細をアプリケーションにコンパイルしないでください。デバッグログはコンパイルされますが、実行時に削除されます。エラー、警告、情報ログは常に保持されます。

そのため、おそらく別の回答で提案されているようにProGuardを使用して、ログの詳細なログステートメントを削除することを検討してください。

ドキュメントによると、システムプロパティを使用して、開発デバイスのロギングを設定できます。セットのプロパティがありlog.tag.<YourTag>、それは次のいずれかの値に設定する必要があります:VERBOSEDEBUGINFOWARNERRORASSERT、またはSUPPRESSこの詳細については、isLoggable()メソッドのドキュメントを参照してください。

setpropコマンドを使用して、プロパティを一時的に設定できます。例えば:

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

または、次のようにファイル/data/local.propでそれらを指定できます。

log.tag.MyAppTag=WARN

それ以降のバージョンのAndroidでは、/ data / local.propを読み取り専用にする必要があるようです。このファイルは起動時に読み込まれるため、更新後に再起動する必要があります。/data/local.propが世界中で書き込み可能な場合、無視される可能性があります。

最後に、System.setProperty()メソッドを使用してプログラムで設定できます。


4
私は同じ経験をしました。APIドキュメントは、これがどのように機能するかについて正確に明確ではandroid.util.Configなく、廃止予定のほとんどの定数についても言及しているようです。APIドキュメントで指定されているハードコードされた値は、(おそらく)ビルドによって異なるため、役に立ちません。したがって、ProGuardルートは私たちにとって最良のソリューションのように思われました。
Christopher Orr

3
/data/local.propファイル、setpropメソッド、またはSystem.setPropertyのいずれかを使用してAndroidロギングを構成することに運がありましたか?Log.isLoggable(TAG、VERBOSE)がtrueを返すのにかなり苦労しています。
seanoshea

2
私はアンドロイドのデバッグを機能させました。秘訣は、Log.d( "xyz")などを呼び出すと、ロガーのデバッグが無効になっている場合でも、メッセージがlogcatに書き込まれることです。これは、通常、書き込み後にフィルタリングが行われることを意味します。Log.isLoggable(TAG、Log.VERBOSE)){Log.v(TAG、 "my log message");などの前にフィルタリングするために、}が必要です。これは一般的にかなり面倒です。変更したバージョンのslf4j-androidを使用して、必要なものを取得します。
2011年

2
@Daveは、local.propメソッドを正しく機能させることができました。私もこの作業を行うことができません。エントリlog.tag.test = INFOを作成してから、adbシェルからsetprop log.tag.test SUPPRESSを実行して変更しようとしましたが、何も変更されません。また、System.getPropertyとSystem.setPropertyを使用しても何も起こりません。あなたから最新情報を受け取りたいと思っていました。ありがとう。
jjNford

2
コメントの+1「APIドキュメントは、これがどのように機能するかについて正確に不明確です」。
アラン

90

最も簡単な方法は、おそらく次のような構成で、コンパイルしたJARを展開前にProGuardで実行することです。

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

これにより、他のすべてのProGuard最適化とは別に、バイトコードから直接詳細ログステートメントが削除されます。


設定を定義できるlog.propertyファイルが含まれていますか。
d-man

1
proguardで行を削除すると、本番環境からのスタックトレースがコードと一致しない可能性があります。
larham1 '10 / 10/20

3
@ larham1:ProGuardはバイトコードに基づいて動作するので、ロギングの呼び出しを削除しても、埋め込まれた行番号のメタデータは変更されないと想像します。
Christopher Orr、2011年

19
これを気にしてください-Log.v()への実際の呼び出しが取り除かれている場合でも、その引数は評価されます。したがって、Log.v(TAG、generateLog())などの高価なメソッド呼び出しが内部にある場合、ホットコードパスにあるとパフォーマンスが低下する可能性があります。toString()やString.format()のようなものでも重要になる可能性があります。
BłażejCzapp

4
@GaneshKrishnanいいえ、そうではありません。Log.v()の呼び出しは取り除かれますが、デフォルトでは、文字列を作成するためのメソッド呼び出しは削除されません。ProGuardの作成者からのこの回答を参照してください:stackoverflow.com/a/6023505/234938
Christopher Orr

18

私は単純なルートをとりました-変数パラメーターリストも使用するラッパークラスを作成しました。

 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
上記のとおり。この手法を実装するためにslf4j-androidの修正バージョンを使用しました。
2011年

3
その上の大きな問題があり、見るstackoverflow.com/questions/2446248/...
OneWorldの

10

より良い方法は、SLF4J API +その実装の一部を使用することです。

Androidアプリケーションの場合、以下を使用できます。

  1. Android Loggerは軽量ですが、設定が簡単なSLF4J実装です(<50 Kb)。
  2. LOGBackは最も強力で最適化された実装ですが、そのサイズは約1 MBです。
  3. その他、好みに応じて:slf4j-android、slf4android。

2
Androidでは、使用する必要がありますlogback-androidlogback適切なものは互換性がないため)。logback-android-1.0.10-1.jarは429 KBですが、提供されている機能を考慮するとそれほど悪くはありませんが、ほとんどの開発者はアプリケーションを最適化するためにProguardを使用します。
tony19 2013

これは、uを使用してログを記録する前にログレベルをチェックすることを節約しません。stackoverflow.com/questions/4958860/を
OneWorld

8

あなたは使うべきです

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

2
isLoggableの出力を構成する方法 isDebugableがマニフェストでfalseに設定されている場合、デバッグと詳細はログに記録できませんか?
OneWorld

5

プロガード(@Christopherからの回答を参照)を使用してロギングを取り除くことは簡単かつ高速でしたが、ファイルにデバッグロギングがあると、本番からのスタックトレースがソースと一致しなくなりました。

代わりに、proguardが本番環境でのみ使用されると想定して、開発と本番で異なるロギングレベルを使用する手法を以下に示します。プロガードが指定されたクラス名の名前を変更したかどうかを確認することでプロダクションを認識します(この例では「com.foo.Bar」を使用します。これを、proguardによって名前が変更されることがわかっている完全修飾クラス名に置き換えます)。

この手法では、コモンズロギングを利用します。

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

標準のAndroid Logクラスの小さなドロップイン置換があります-https ://github.com/zserge/log

基本的にあなたがしなければならないすべてはからのインポートを置き換えるandroid.util.Logことtrikita.log.Logです。次に、あなたの中Application.onCreate()や、いくつかの静的でのチェックinitalizer BuilConfig.DEBUGまたは任意の他のフラグと使用Log.level(Log.D)またはLog.level(Log.E)最小限のログレベルを変更します。を使用Log.useLog(false)して、ロギングをまったく無効にすることができます。


2

このログ拡張クラスを見ることができるかもしれません:https : //github.com/dbauduin/Android-Tools/tree/master/logs

ログを細かく制御できます。たとえば、すべてのログを無効にしたり、一部のパッケージまたはクラスのログだけを無効にしたりできます。

さらに、いくつかの便利な機能が追加されます(たとえば、各ログにタグを渡す必要はありません)。


2

この問題と、ロギングに関するその他の一般的な問題を解決するユーティリティ/ラッパーを作成しました。

次の機能を備えたデバッグユーティリティ:

  • LogModeによってラップされたLogクラスによって提供される通常の機能。
  • メソッドEntry-Exitログ:スイッチでオフにすることができます
  • 選択的なデバッグ:特定のクラスをデバッグします。
  • メソッド実行時間の測定:個々のメソッドの実行時間と、クラスのすべてのメソッドに費やされた総時間を測定します。

使い方?

  • クラスをプロジェクトに含めます。
  • まず、android.util.Logメソッドを使用するように使用します。
  • アプリのメソッドの最初と最後にentry_log()-exit_log()メソッドの呼び出しを配置することにより、Entry-Exitログ機能を使用します。

私はドキュメントを十分なものにしようと試みました。

このユーティリティを改善するための提案は大歓迎です。

無料で使用/共有できます。

GitHubからダウンロードしてください


2

これはより複雑なソリューションです。完全なスタックトレースが取得され、toString()メソッドが必要な場合にのみ呼び出されます(パフォーマンス)。本番モードでは属性BuildConfig.DEBUGがfalseになるため、すべてのトレースログとデバッグログが削除されます。ホットスポットコンパイラは、最終的な静的プロパティをオフにするため、呼び出しを削除する機会があります。

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

次のように使用します:

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

非常に単純なロギングシナリオでは、デバッグ目的で開発中に文字通りコンソールに書き込もうとしているだけですが、本番ビルドの前に検索と置換を行い、LogまたはSystemへのすべての呼び出しをコメント化するのが最も簡単な場合があります。 out.println。

たとえば、「ログ」を使用しなかったとします。Log.dやLog.eなどの呼び出し以外の場所では、ソリューション全体で検索と置換を行って「Log」を置き換えるだけで済みます。「// Log」で。すべてのロギング呼び出しをコメントアウトするか、私の場合はどこでもSystem.out.printlnを使用しているので、本番環境に進む前に、「System.out.println」を完全に検索して置換し、 「//System.out.println」。

私はこれが理想的ではないことを知っています。LogとSystem.out.printlnへの呼び出しを見つけてコメントアウトする機能がEclipseに組み込まれていると便利ですが、それが実現するまで、これを行う最も簡単で最速かつ最良の方法は検索してコメントアウトして置換する。これを行う場合、ソースコードを編集しているため、スタックトレースの行番号の不一致を心配する必要はありません。また、ログレベルの構成などを確認してオーバーヘッドを追加することもありません。


1

私のアプリには、「state」と呼ばれる静的なブール変数を持つLogクラスをラップするクラスがあります。コード全体を通して、実際にログに書き込む前に、静的メソッドを使用して「状態」変数の値をチェックします。次に、「状態」変数を設定する静的メソッドを使用して、アプリによって作成されたすべてのインスタンス間で値が共通であることを確認します。つまり、アプリが実行中であっても、1回の呼び出しでアプリのすべてのログを有効または無効にできます。サポート呼び出しに役立ちます...それは、デバッグ時に銃に固執する必要があることを意味しますが、標準のLogクラスの使用に後退しません...

値が割り当てられていない場合、Javaがブール変数をfalseとして解釈することも便利です(つまり、ロギングをオンにする必要があるまでfalseのままにすることができます:-)。


1

Logローカルコンポーネントでクラスを使用し、メソッドをv / i / e / dとして定義できます。必要に応じて、さらに電話をかけることができます。
以下に例を示します。

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

ここで、メッセージはとでstringあり、args印刷する値です。


0

TAGごとに異なるログレベルを設定できると便利です。

私はこの非常に単純なラッパークラスを使用しています。

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

}

次に、各クラスの最初にTAGごとにログレベルを設定します。

Log2.setLogLevel(TAG, LogLevels.INFO);

0

もう1つの方法は、ログを開いたり閉じたりする機能を持つロギングプラットフォームを使用することです。これにより、ログが開かれている必要があり、たとえば、発生している問題に応じて閉じられる本番環境のアプリでも、柔軟性が大幅に向上する場合があります。

  • 木こり
  • Shipbook(免責事項:私はこのパッケージの作成者です)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.