434

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

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

私のオープンソース プロジェクトは大規模で、リリースするたびに手動で行うのは面倒です。さらに、ログ行を削除することは、潜在的に注意が必要です。たとえば、次のようになります。

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

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

それで、それを行うためのより良いソースコードレベルの方法はありますか? それとも、すべてのログ行を効率的かつ安全に削除するための巧妙な ProGuard 構文でしょうか?

4

30 に答える 30

526

はるかに簡単な解決策は、あちこちのすべてのifチェックを忘れて、Antターゲットを呼び出すときにProGuardを使用して任意のLog.d()またはメソッド呼び出しを削除することです。Log.v()release

そうすれば、通常のビルドのデバッグ情報が常に出力され、リリース ビルドのコードを変更する必要がなくなります。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 マニュアルの例も参照してください。


更新 (4.5 年後):最近では、Android のログ記録にTimberを使用しました。

既定の実装よりも少し優れているだけでなく (Logログ タグが自動的に設定され、書式設定された文字列と例外を簡単にログに記録できます)、実行時にさまざまなログの動作を指定することもできます。

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

Application onCreate()木材は私の方法で設定されています:

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

次に、コードの他の場所で簡単にログを記録できます。

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

より高度な例については、 Timber サンプル アプリを参照してください。開発中にすべてのログ ステートメントが logcat に送信され、本番環境ではデバッグ ステートメントはログに記録されませんが、エラーはサイレントに Crashlytics に報告されます。

于 2010-03-17T23:30:02.247 に答える
130

すべて良い答えですが、開発が終了したとき、すべての Log 呼び出しで if ステートメントを使用したくありませんでしたし、外部ツールも使用したくありませんでした。

したがって、私が使用している解決策は、android.util.Log クラスを独自の Log クラスに置き換えることです。

public class Log {
    static final boolean LOG = BuildConfig.DEBUG;

    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 のインポートを独自のクラスに置き換えることだけでした。

于 2011-01-04T11:12:21.070 に答える
63

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

クラス MyDebug {
  static final boolean LOG = true;
}

次に、コードにログインしたい場所で、次のようにします。

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

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

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

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 + ")");
于 2010-03-15T15:35:24.110 に答える
31

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 コマンドを使用します)

于 2010-03-15T13:34:24.110 に答える
20

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

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を使用します。他の人に配布されるライブラリを開発するときはなおさらです。利点は、デフォルトでは出力がないことです。また、インテグレーターがログ出力を必要とする場合は、Logback for Android を使用してログをアクティブ化できるため、ログをファイルまたは LogCat にリダイレクトできます。

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

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
于 2015-09-09T15:08:47.953 に答える
11

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

印刷する

タグを指定せずに「あなたのクラス/メッセージ」

于 2014-10-20T09:13:12.203 に答える
8

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");
  }
}
于 2013-03-30T04:52:41.363 に答える
7

組み込みの android.util.Log の代わりに、 roboguice のロギング機能を使用することを検討します。

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

proguard を使用するのは非常に面倒な場合があり、正当な理由がない限り、構成してアプリケーションで機能させるという問題を経験することはありません (ログを無効にすることは良いことではありません)。

于 2011-07-27T23:46:32.240 に答える
6

android.util.Log ごとに、ログを有効/無効にする方法が提供されます。

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

デフォルトでは、メソッド isLoggable(...) は false を返します。これは、デバイスで setprop を実行した後でのみ次のようになります。

adb shell setprop log.tag.MyAppTag DEBUG

これは、DEBUG レベルを超えるログを出力できることを意味します。リファレンス Android ドキュメント:

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

したがって、カスタム ログ ユーティリティを使用できます。

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);
    }
}
于 2013-12-06T13:21:50.617 に答える
3

ここに画像の説明を入力

これは、私がAndroidプロジェクトで行っていたことです..

Android Studio では、Ctrl+Shift+F でプロジェクト全体から検索し (MacO では Command+Shift+F)、置換するには Ctrl+Shift+R ((MacO では Command+Shift+R)) で同様の操作を行うことができます。

于 2015-12-11T11:06:56.357 に答える
1

ProGuard がリリース ビルドでそれを行います。android.com からの朗報です。

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

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

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

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

于 2013-03-26T07:39:52.123 に答える
1

さまざまなログ レベルのサポートを提供し、コードがライブ デバイスで実行されているかエミュレータで実行されているかに応じてログ レベルを自動的に変更することで、上記のソリューションを改善しました。

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


}
于 2012-03-29T10:12:22.703 に答える
0

私は 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 に設定してください。

于 2015-01-14T13:53:59.413 に答える
-1

最も簡単な方法;

使用するDebugLog

アプリがリリースされると、DebugLog によってすべてのログが無効になります。

https://github.com/MustafaFerhan/DebugLog

于 2015-07-01T08:55:35.300 に答える