web-dev-qa-db-ja.com

アプリをGoogle Playに公開する前にすべてのデバッグログ呼び出しを削除する方法

Googleによると、私は自分のAndroidアプリを公開する前に「ソースコード内のLogメソッドへの呼び出しを無効にする」必要があります。 パブリケーションチェックリスト のセクション5から抜粋します。

リリース用にアプリケーションをビルドする前に、必ずロギングを無効にし、デバッグオプションを無効にしてください。ソースファイル内のLogメソッドへの呼び出しを削除することでロギングを無効にすることができます。

私のオープンソースプロジェクトは大きく、リリースするたびに手動でやるのは大変です。さらに、Log行を削除することは、たとえば次のようにして手間がかかります。

if(condition)
  Log.d(LOG_TAG, "Something");
data.load();
data.show();

Log行をコメントすると、その条件は次の行に適用され、load()が呼び出されない可能性があります。そのような状況は、私がそれが存在するべきではないと決定できるほど十分にまれですか?

これは公式のチェックリストに載っているので、私は多くの人がこれを定期的に行っていると思います。
では、すべてのLog行を効率的かつ安全に削除するにはどうすればよいですか。

363
Nicolas Raoul

もっと簡単な解決策は、すべてのifname__のチェックをすべて忘れて、Ant releasename__を呼び出すときに ProGuard を使用してLog.d()またはLog.v()メソッド呼び出しを省略することです。ターゲット。

そうすれば、通常のビルドではデバッグ情報が常に出力され、リリースビルドではコードを変更する必要がなくなります。 ProGuardは、他の不要なステートメント、空のブロックを削除するためにバイトコードを複数回パスし、必要に応じて短い方法を自動的にインライン化することもできます。

たとえば、Android用の非常に基本的なProGuard構成は次のとおりです。

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends Android.app.Activity
-assumenosideeffects class Android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

ですから、それをファイルに保存してから、AntからProGuardを呼び出して、コンパイルしたばかりのJARと使用しているAndroidプラットフォームのJARを渡します。

ProGuardマニュアルの examples も参照してください。


アップデート(4.5年後):最近はAndroidのログ記録に Timber を使用しました。

Logタグは自動的に設定され、フォーマットされた文字列や例外をログに記録するのは簡単です - デフォルトのLogname__実装よりも少し良いだけでなく、実行時に異なるロギング動作を指定することもできます。

この例では、ロギングステートメントは私のアプリのデバッグビルドのlogcatにのみ書き込まれます。

材木は私のApplicationonCreate()メソッドでセットアップされます。

if (BuildConfig.DEBUG) {
  Timber.plant(new Timber.DebugTree());
}

それから私のコードのどこにでも簡単にログを取ることができます:

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

開発中にすべてのlogステートメントがlogcatに送信され、本番環境ではdebugステートメントは記録されませんがエラーは静かに記録される、より高度な例については Timberサンプルアプリケーション を参照してください。 Crashlyticsに報告しました。

458
Christopher Orr

すべての良い答えですが、開発が終わったときには、すべてのLog呼び出しを囲むif文を使用したり、外部ツールを使用したりしたくはありませんでした。

だから私が使っている解決策は私の自身のLogクラスでAndroid.util.Logクラスを置き換えることです:

public class Log {
    static final boolean LOG = false;

    public static void i(String tag, String string) {
        if (LOG) Android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG) Android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG) Android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG) Android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG) Android.util.Log.w(tag, string);
    }
}

すべてのソースファイルで私がしなければならなかった唯一のことは、私自身のクラスでAndroid.util.Logのインポートを置き換えることでした。

110
Reiner

ログに記録するかどうかを示す静的ブール値をどこかに持つことをお勧めします。

 class MyDebug {
 static finalブール値LOG = true; 
} 

それからあなたがあなたのコードにログインしたいところはどこでも、ちょうどこれをしてください:

 if(MyDebug.LOG){
 if(条件)Log.i(...); 
} 

MyDebug.LOGをfalseに設定すると、コンパイラはそのようなチェック内のすべてのコードを削除します(これは静的な最後なので、コンパイル時にコードが使用されていないことがわかります)

大規模なプロジェクトでは、必要に応じてログを簡単に有効または無効にできるように、個々のファイルにブール値を含めることをお勧めします。たとえば、これらはウィンドウマネージャにあるさまざまなロギング定数です。

static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;

対応するコードでは:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
59
hackbod

ChristopherのProguardソリューションが最善ですが、何らかの理由でProguardが気に入らない場合は、ローテクソリューションを使用してください。

コメントログ:

find . -name "*\.Java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

コメントを外す:

find . -name "*\.Java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

制約は、ロギングの指示が複数行にまたがってはいけないということです。

(プロジェクトのルートにあるUNIXシェルでこれらの行を実行します。Windowsを使用している場合は、UNIXレイヤーを入手するか、同等のWindowsコマンドを使用してください)

30
Nicolas Raoul

最終的なバイナリからログ行を削除するのには多くの問題があるので、Android Studioでproguardを使用してgradleすることについて、いくつかの精度を追加したいと思います。

Proguardでassumenosideeffectsを機能させるには、前提条件があります。

あなたのgradleファイルでは、デフォルトファイルとしてproguard-Android-optimize.txtの使用法を指定しなければなりません。

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-Android-optimize.txt'), 'proguard-rules.pro'

        // With the file below, it does not work!
        //proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
    }
}

実際、デフォルトのproguard-Android.txtファイルでは、最適化は2つのフラグで無効になっています。

-dontoptimize
-dontpreverify

proguard-Android-optimize.txtファイルはこれらの行を追加しないので、assumenosideeffectsが機能するようになりました。

それから、個人的には SLF4J を使用します。他の人に配布されるライブラリーを開発するときはなおさらです。利点は、デフォルトでは出力がないことです。また、インテグレータがログ出力を希望する場合、彼はAndroid用Logbackを使用してログをアクティブ化できるため、ログをファイルまたはLogCatにリダイレクトできます。

最後のライブラリからログを削除する必要がある場合は、(もちろんproguard-Android-optimize.txtファイルを有効にした後で)Proguardファイルに追加します。

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
16

Google IOサンプルアプリケーションのように LogUtils クラスを使用しました。 BuildConfig.DEBUGは信頼できない なので、BuildConfig.DEBUGではなくアプリケーション固有のDEBUG定数を使用するようにこれを変更しました。それから私のクラスで私は次のものを持っています。

import static my.app.util.LogUtils.makeLogTag;
import static my.app.util.LogUtils.LOGV;

public class MyActivity extends FragmentActivity {
  private static final String TAG = makeLogTag(MyActivity.class);

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LOGV(TAG, "my message");
  }
}
8
JosephL

Jake WhartonのTimberを使うことを強くお勧めします。

https://github.com/JakeWharton/timber

それは有効/無効に加えて自動的にタグクラスを追加することであなたの問題を解決します

ただ

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

ログはあなたのデバッグ版でしか使われないでしょう。

Timber.d("lol");

または

Timber.i("lol says %s","lol");

印刷する

タグを指定せずに "あなたのクラス/ msg"

8
AndroidGecko

私は内蔵のAndroid.util.Logの代わりにroboguiceの logging facility を使うことを考えます

彼らの機能は自動的にリリースビルドのデバッグと詳細ログを無効にします。さらに、あなたは無料でいくつかの気の利いた機能(例えばカスタマイズ可能なロギングの振る舞い、すべてのログのための追加のデータなど)を手に入れます

Proguardを使うのはかなり面倒かもしれませんし、あなたがそれを設定してそれをあなたのアプリケーションで動作させることのトラブルを経験しないでしょう。 t良いもの)

7
Zvi

Android.util.Logごとにログを有効/無効にする方法があります。

public static native boolean isLoggable(String tag, int level);

デフォルトでは、isLoggable(...)メソッドはfalseを返します。これは、deviceでproppropを設定した後に限ります。

adb Shell setprop log.tag.MyAppTag DEBUG

これは、DEBUGレベルを超えるログを印刷できることを意味します。参照Androidのドキュメント:

指定されたタグのログが指定されたレベルでログに記録できるかどうかを確認します。タグのデフォルトレベルはINFOに設定されています。これは、INFO以上のあらゆるレベルがログに記録されることを意味します。ロギングメソッドを呼び出す前に、タグを記録する必要があるかどうかを確認する必要があります。システムプロパティ「setprop log.tag」を設定して、デフォルトレベルを変更できます。 'levelはVERBOSE、DEBUG、INFO、WARN、ERROR、ASSERT、またはSUPPRESSです。 SUPPRESSはあなたのタグのすべてのロギングをオフにします。 local.propファイルを作成して、その中に 'log.tag。='と入力して/data/local.propに置くこともできます。

そのため、カスタムログutilを使用できます。

public final class Dlog 
{
    public static void v(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.VERBOSE))
            Log.v(tag, msg);
    }

    public static void d(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.DEBUG))
            Log.d(tag, msg);
    }

    public static void i(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.INFO))
            Log.i(tag, msg);
    }

    public static void w(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.WARN))
            Log.w(tag, msg);
    }

    public static void e(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.ERROR))
            Log.e(tag, msg);
    }
}
6
Richard

私はAndroid Studioユーザーに特に当てはまるこのソリューションを投稿しています。私は最近Timberを発見し、次のようにしてそれを私のアプリに正常にインポートしました。

最新版のライブラリをbuild.gradleに入れます。

compile 'com.jakewharton.timber:timber:4.1.1'

次にAndroid Studioで、[編集] - > [検索] - > [パス内で置換]をクリックします。

Log.e(TAG,を入力するか、Logメッセージを"Text to find"テキストボックスに定義してください。それからあなたはそれをTimber.e(に置き換えるだけです。

enter image description here

「検索」をクリックしてからすべて置換します。

Android Studiosは、プロジェクト内のすべてのファイルを調べて、すべてのログを材木に置き換えます。

私がこの方法で持っていた唯一の問題はそれがあなたのそれぞれのJavaファイルのための輸入で「材木」を見つけることができないのでgradleがその後何百万ものエラーメッセージで出てくることです。エラーをクリックするだけで、Android Studiosは自動的に "Timber"をあなたのJavaにインポートします。あなたがすべてのあなたのエラーファイルのためにそれをしたら、gradleは再びコンパイルするでしょう。

また、このコードをonCreateクラスのApplicationメソッドに入れる必要があります。

    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    }

これにより、本番モードではなく開発モードにあるときにのみアプリログが記録されます。リリースモードでログインするためのBuildConfig.RELEASEを持つこともできます。

6
Simon

あなたが(一回)グローバル置換を実行することができ、その後に何らかのコーディング規約を守ることができれば、あなたはAndroidでよく使われるパターンに従うことができます framework

書く代わりに

Log.d(TAG, string1 + string2 + arg3.toString());

それを持っている

if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());

今proguardはStringBuilderとそれが途中で使うすべての文字列とメソッドを、最適化されたリリースDEXから削除することができます。 proguard-Android-optimize.txtを使用すれば、あなたのproguard-rules.proの中でAndroid.util.Logについて心配する必要はありません。

Android {
  …
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-Android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

Android Studio gradle pluginでは、BuildConfig.DEBUGは非常に信頼性が高いので、ストリッピングを制御するために追加の定数は必要ありません。

5
Alex Cohn

私はとても簡単な解決策を持っています。私は開発にIntelliJを使用しているので、詳細はさまざまですが、アイデアはすべてのIDEに当てはまるはずです。

私は自分のソースツリーのルートを選び、右クリックして "replace"をすることを選択します。私はそれからすべての「ログ」を取り替えることを選びます。 「// Log」と入力します。これにより、すべてのログステートメントが削除されます。それらを後で元に戻すために、私は同じ置き換えを繰り返しますが、今回はすべての "// Log"を置き換えます。 「ログ」と。

私にとっては素晴らしい作品です。 「Dialog」のような事故を避けるために、置き換えを大文字と小文字を区別するように設定することを忘れないでください。さらなる保証のために、あなたは「Log」で最初のステップをすることもできます。検索する文字列として。

ブリリアント。

3
kg_sYy

proguard-rules.txtファイルに以下を追加してください

-assumenosideeffects class Android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}
3
eranga

enter image description here

これは私が私のAndroidプロジェクトでしていたものです..

Android Studioでは、プロジェクト全体から検索するにはCtrl + Shift + F(MacOではCommand + Shift + F)、置換するにはCtrl + Shift + R((MacOではCommand + Shift + R))を使用します。

3
Lins Louis

zserge's comment が示唆しているように、

Timberはとても素敵ですが、すでに既存のプロジェクトがある場合は - github.com/zserge/logを試してください。これはAndroid.util.Logのドロップイン代替品で、Timberが持っている機能のほとんどを備えています。

彼のログライブラリ 以下のように単純な有効/無効のログ印刷スイッチを提供します。

また、のみimportname__行を変更する必要があり、何も必要ありませんLog.d(...);ステートメントに変更します。

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.Android, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
2
Youngjae

ProGuardはあなたのリリースビルドであなたのためにそれをするでしょうそして今やAndroid.comからの良いニュース:

http://developer.Android.com/tools/help/proguard.html

ProGuardツールは、未使用のコードを削除し、クラス、フィールド、およびメソッドの名前を意味が不明瞭なものに変更することで、コードを縮小、最適化、および難読化します。その結果、リバースエンジニアリングがより困難な、より小さなサイズの.apkファイルが作成されます。 ProGuardを使用すると、アプリケーションのリバースエンジニアリングが難しくなるため、アプリケーションのライセンスを取得しているときのように、アプリケーションがセキュリティに敏感な機能を使用しているときに使用することが重要です。

ProGuardはAndroidビルドシステムに統合されているため、手動で起動する必要はありません。 ProGuardは、アプリケーションをリリースモードでビルドしたときにのみ実行されるため、アプリケーションをデバッグモードでビルドしたときに難読化されたコードを処理する必要はありません。 ProGuardを実行することは完全にオプションですが、強くお勧めします。

このドキュメントでは、ProGuardを有効にして設定する方法と、トレースツールを使用して難読化されたスタックトレースをデコードする方法について説明します。

1
Max Gold

さまざまなログレベルをサポートし、コードが実際のデバイスで実行されているかエミュレータで実行されているかに応じて自動的にログレベルを変更することによって、上記の解決策を改善しました。

public class Log {

final static int WARN = 1;
final static int INFO = 2;
final static int DEBUG = 3;
final static int VERB = 4;

static int LOG_LEVEL;

static
{
    if ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT)) {
        LOG_LEVEL = VERB;
    } else {
        LOG_LEVEL = INFO;
    }

}


/**
 *Error
 */
public static void e(String tag, String string)
{
        Android.util.Log.e(tag, string);
}

/**
 * Warn
 */
public static void w(String tag, String string)
{
        Android.util.Log.w(tag, string);
}

/**
 * Info
 */
public static void i(String tag, String string)
{
    if(LOG_LEVEL >= INFO)
    {
        Android.util.Log.i(tag, string);
    }
}

/**
 * Debug
 */
public static void d(String tag, String string)
{
    if(LOG_LEVEL >= DEBUG)
    {
        Android.util.Log.d(tag, string);
    }
}

/**
 * Verbose
 */
public static void v(String tag, String string)
{
    if(LOG_LEVEL >= VERB)
    {
        Android.util.Log.v(tag, string);
    }
}


}
1
danwms

私はLog.d(TAG、なんらかの文字列、しばしばString.format())を使うのが好きです。

TAGは常にクラス名です

Log.d(TAG、 - > Logd)をクラスのテキスト内で変換する

private void Logd(String str){
    if (MainClass.debug) Log.d(className, str);
}

このようにして、リリース版を作成する準備ができたら、MainClass.debugをfalseに設定します。

0
user462990

これは古い質問ですが、すべてのログ呼び出しをBoolean logCallWasHere = trueのようなものに置き換えなかったのはなぜでしょうか。 // ---残りのログはこちら

これが、あなたがいつそれらを元に戻したいのかを知る理由であり、それらはあなたのifステートメント呼び出しに影響を与えません:)

0
masood elsad

ログはlinuxとsedのbashを使って削除できます。

find . -name "*\.Java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'

複数行ログに有効です。このソリューションでは、ログが本番コードに存在しないことを確認できます。

0
sylwano