web-dev-qa-db-ja.com

ベストプラクティスと考えを記録する

アプリからリファクタリングを行うところですが、このシンプルでありながら複雑なトピックであるロギングについて考えるようになりました。クリーンで効果的で有益なロギングを行うのがどれほど難しいのでしょうか。

ロギングに関するドキュメントを読むと、このスニペットがよく見られます

if (BuildConfig.DEBUG) {
    Log.d(TAG + "message");
}

そして、それは何のためにあるのでしょうか?ドキュメントによると、 Android Developer-Log 、デバッグログメッセージはコンパイル時にコンパイルされますが、実行時に削除されるため、ifステートメント内でlogを呼び出す必要はありません。それとも私は何かを誤解していますか?

次に、デバッグ以外のLog.x()呼び出しを使用することの本当の利点は、ログエントリがユーザーに表示されないか、エラーファイルに記録されないため、コンパイルされて実行されるので、どういうことかと思います。本番環境ではまったく目的がない?これは前のifステートメントのユースケースでしょうか?

前に、ログエントリはファイルに記録されないことを述べました。これがAndroidの組み込み機能ではないのはなぜですか?パフォーマンスの問題、不要な権限の使用などが原因ですか?私は自分のロギングクラスにこの機能を実装しましたが、今はそれが悪い習慣かどうか疑問に思いますか?しかし、重要なログエントリを含むログがあることもいいですか。

まとめると、開発中と本番環境の両方で、クリーンで効果的で有益なロギングを実装できます。ベストプラクティスは何ですか?

21
Robert

開発中にアプリケーションをデバッグするためにのみ必要なログ。機能が期待どおりに機能し、望ましい結果が生成されることを確認します。ベストプラクティスは、あなたにとって最も便利で、問題をできるだけ迅速かつ効率的に見つけて解決できるような方法でロギングを行うことだと思います。

前に、ログエントリはファイルに記録されないことを述べました。これがAndroidの組み込み機能ではないのはなぜですか?

だれ(開発段階のプログラマーを除く)が、アプリケーションが自分のデバイスの限られたストレージスペースを無駄なデータで浪費することを望んでいますか?ユーザーは、ログを見たり、読んだり、使用したりしません。彼らはこのゴミを必要としません。運用中のアプリケーションは、ログを生成してはならず、もちろん、それらをファイルに保存してはなりません。

リリースされたアプリケーションに実装する必要がある唯一のロギングは、未処理の例外ロギングです。さらに、クラッシュレポートの送信を提案し、レポートの送信後にログを削除するよう提案された場合、これらのログを処理するのはアプリケーションの責任です。

リリースされたアプリでログを作成してはならないもう1つの理由は、ユーザーの承認を必要とする機密データと出力が含まれる可能性があるため、セキュリティ上の欠陥が生じることです。

モジュールまたは機能が完全に実装され、完全にテストされたらすぐに、本番環境にデプロイする前にすべてのログを削除することがベストプラクティスだと思います。 if (BuildConfig.DEBUG)条件を導入すると、これが確実に達成されます。

12
Alexander Zhak

これにより、この形式のクリーンなデバッグタグが生成されますClasssName[MethodName] - LineNumber反射あり。

インラインコメント付きの完全なコードは ここで要旨 として利用できます。

import Android.util.Log;

public class Logger {

    public enum LOGGER_DEPTH {
        ACTUAL_METHOD(4),
        LOGGER_METHOD(3),
        STACK_TRACE_METHOD(1),
        JVM_METHOD(0);

        private final int value;

        private LOGGER_DEPTH(final int newValue) {
            value = newValue;
        }

        public int getValue() {
            return value;
        }
    }

    private static final String personalTAG = "Logger";

    private StringBuilder sb;

    private Logger() {
        if (LoggerLoader.instance != null) {
            Log.e(personalTAG, "Error: Logger already instantiated");
            throw new IllegalStateException("Already Instantiated");
        } else {
            this.sb = new StringBuilder(255);
        }
    }

    public static Logger getLogger() {
        return LoggerLoader.instance;
    }

    private String getTag(LOGGER_DEPTH depth) {
        try {
            String className = Thread.currentThread().getStackTrace()[depth.getValue()].getClassName();
            sb.append(className.substring(className.lastIndexOf(".") + 1));
            sb.append("[");
            sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getMethodName());
            sb.append("] - ");
            sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getLineNumber());
            return sb.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            Log.d(personalTAG, ex.getMessage());
        } finally {
            sb.setLength(0);
        }
        return null;
    }

    public void d(String msg) {
        try {
            Log.d(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void d(String msg, LOGGER_DEPTH depth) {
        try {
            Log.d(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void d(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.d(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void e(String msg) {
        try {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void e(String msg, LOGGER_DEPTH depth) {
        try {
            Log.e(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void e(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.e(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void w(String msg) {
        try {
            Log.w(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void w(String msg, LOGGER_DEPTH depth) {
        try {
            Log.w(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void w(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.w(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void v(String msg) {
        try {
            Log.v(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void v(String msg, LOGGER_DEPTH depth) {
        try {
            Log.v(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void v(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.v(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void i(String msg) {
        try {
            Log.i(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void i(String msg, LOGGER_DEPTH depth) {
        try {
            Log.i(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void i(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.i(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void wtf(String msg) {
        try {
            Log.wtf(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void wtf(String msg, LOGGER_DEPTH depth) {
        try {
            Log.wtf(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void wtf(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.wtf(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    private static class LoggerLoader {
        private static final Logger instance = new Logger();
    }
}
8
Eefret

私はAndroidで作業を開始してから、同じ問題に遭遇しました。私はこのオープンソースプロジェクト( Android Studio Macros )を作成しました。コード内で "// <#DEBUG_AREA>および// <#/ DEBUG_AREA>"タグを使用してさらに複雑なものを追加したい場合、基本的な考え方は、ビルドバリアントを変更すると、これらのタグ内のすべてがコメント化されるということです。 forループに次のようなものがある場合:

       //=========This piece of code is only for logging purposes...=========
        Log.e("LogUserInfo", "Name: " + name);
        Log.e("LogUserInfo", "Id: " + user.getId());
        Log.e("LogUserInfo", "Id: " + user.getDistance());
        //====================================================================

これを行う代わりに:

if(DEBUG){
      Log.e("LogginUserInfo", "Name: " + name);
      Log.e("LogginUserInfo", "Id: " + user.getId());
      Log.e("LogginUserInfo", "Id: " + user.getDistance());
 }

このマクロを使用すると、これを実行できます(完全なメソッド)。

private List<String> getNamesOfUsersNearMe(String zipCode){
    List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
    if(users == null || users.size() < 1){
        return null;
    }

    List<String> names = new ArrayList<String>();
    int totalUsers = users.size();
    for(int i = 0; i < totalUsers; i++){
        User user = users.get(i);
        String name = user.getName();
        names.add(name);
        //<#DEBUG_AREA>
        Log.e("LogginUserInfo", "Name: " + name);
        Log.e("LogginUserInfo", "Id: " + user.getId());
        Log.e("LogginUserInfo", "Id: " + user.getDistance());
        //</#DEBUG_AREA>
    }
    return names;
}

ビルドバリアントを変更してリリースすると、次のようになります。

private List<String> getNamesOfUsersNearMe(String zipCode){
    List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
    if(users == null || users.size() < 1){
        return null;
    }

    List<String> names = new ArrayList<String>();
    int totalUsers = users.size();
    for(int i = 0; i < totalUsers; i++){
        User user = users.get(i);
        String name = user.getName();
        names.add(name);
        /*<#DEBUG_OFF>
            Log.e("LogginUserInfo", "Name: " + name);
            Log.e("LogginUserInfo", "Id: " + user.getId());
            Log.e("LogginUserInfo", "Id: " + user.getDistance());
        </#DEBUG_OFF>*/
    }

    return names;
}

これは長いループのパフォーマンスがはるかに優れており、「リリース」モードのときに不要なコードを取り除くことでコードをよりクリーンにします。もちろん、「デバッグ」に戻ると、領域のコメントが解除され、元の状態のままになります。 「<#DEBUG_AREA>」タグでした...

また、最も一般的なシナリオに合わせようとすると、取り除くために完全な領域が必要ない場合がありますが、代わりに単一のログのみが必要になる場合があるようです。その場合、プロジェクトには、次のように使用できるログラッパークラスもあります。続く:

if(users == null || users.size() < 1){
    ASDebuggerMacroLog.e("LogUserInfo", "There's no users available near me...");
    return null;
}

「Android Studio。

それが役に立てば幸い!

よろしく!

5
Martin Cazares

標準出力を直接使用して何かをログに記録しないでください(Squid:S106)

メッセージをログに記録する場合、満たす必要のあるいくつかの重要な要件があります。

  • ユーザーはログを簡単に取得できる必要があります
  • ログに記録されたすべてのメッセージの形式は、ユーザーがログを簡単に読み取れるように統一されている必要があります
  • ログに記録されたデータは実際に記録する必要があります
  • 機密データは安全にのみ記録する必要があります

プログラムが標準出力に直接書き込む場合、それらの要件に準拠する方法はまったくありません。そのため、専用ロガーを定義して使用することを強くお勧めします。

出典: Sonarcloud

0

Timberライブラリの使用を強くお勧めします: https://github.com/JakeWharton/timber

これは、Android Logクラスの上にある非常に小さなライブラリであり、すべてのロギング要件を簡単に処理します。いくつかの機能:

  • 呼び出されているクラスを自動的に特定し、その名前をタグとして使用します
  • ビルドタイプごとに異なるツリーを植えることができます
  • すべてのログはツリーの中央の場所を通過します。したがって、それらを処理するか、必要に応じてどこかにアップロードします。
0
Ali Mehrpour