Androidでクラスごとに、Log用の"final static String TAG = ..."を定義しなくていいようにしてみた（おまけあり）

この記事はJava言語をつかったAndroidアプリ開発に関する記事です。

デバッグをちょっと楽にする<Font color="teal">小ネタ</font>としてログ出力に関する提案です。
おまけもあるので見てください。（[おまけ](http://qiita.com/Pooh3Mobi/items/e2dbf7da05f9e7cb3859#%E3%81%8A%E3%81%BE%E3%81%91)が本番です）


こんなものつくってみました。

```java:LogUtil.java
public class LogUtil {
    private static final int MAX_TAG_SIZE = 23;
    // Objのクラス名をMAX_TAG_SIZEの文字数以内で出力してくれる。
    public static String TAG(Object obj) {
        String objName = obj.getClass().getSimpleName();
        return objName.length() > MAX_TAG_SIZE ? objName.substring(0, MAX_TAG_SIZE) : objName;
    }
}
```
**TAG()**
LogUtilのようなクラスに定義しておいて、`import static`で使うといい感じに使えます。
AndroidStudio（IntellijIDE）の機能でimport文補完を使います。TAG()と打ち込んだ時点で`static import`しちゃう？って提案してくれるので便利です。

つまり
**LogUtilのTAG()の定義＋AndroidStudioのコード入力サポート機能(自動import)**

を使えば`final static String TAG = MyClass.class.getSimpleName();`をクラス毎に定義しなくてよくなります。

これでLogデバッグするときのジミーで冗長的な作業がほとんどなくなって、集中力が保てていい感じです。


使い方
====
```java:MainActivity.java
public class MainActivity extends ExtensionActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // TAGの引数は出したいクラス名のインスタンスを入れる。この場合はMainActivity
        Log.d(TAG(this), " onCreate()");
    }
}
```
注意：定数とは違い動的に価を決定するので、定数を使った時よりもごく微小ですが遅くなります。パフォーマンスを気にされるデバッグでは使わないほうがいいかもしれません。（そもそもstatic finalにしているのはパフォーマンスを阻害しないの理由なので、両者の違いを考えた上でつかってください）

おまけ
====
- **CLASS_LINE()** : `[クラス.メソッド名(行番号)]`
- **METHOD_INFO()** : `[パッケージ名.クラス名.メソッド名(ファイル名:行数)]` AndroidStudioの<Font color="red">LogCat上からファイルの対象行にマウスクリックで飛べる</font>
- **CALLED_BY()** : CALLED_BYが呼ばれているメソッドを呼び出した`[クラス名.メソッド名(行数)]`が出力される

を紹介します。

これらはLogで実行クラスファイルの行番号やどこから呼ばれているかを出力したいときに使えます。
Stringで返すのでそのままログ出力にいれていただければ結構です。
メソッド名が大文字のスネークケースなのは、利用頻度と見やすさを重視したためです。


```java:LogUtil.java
    public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
    // [クラス.メソッド名(行番号)]
    public static String CLASS_LINE() {
        try {
            StackTraceElement[] elements = Thread.currentThread().getStackTrace();
            for (StackTraceElement ste: elements) {
                String cls = ste.getClassName();
                if (cls.startsWith(PACKAGE_NAME)) {
                    cls = cls.replace(PACKAGE_NAME, "");
                    return cls + "." + ste.getMethodName() + "(" + ste.getLineNumber() + ")";
                }
            }
        } catch (Exception e) {
            return "null";
        }
        return "null";
    }

    // [パッケージ名.クラス名.メソッド名(ファイル名:行数)] AndroidStudioのLogCat上からファイルの対象行に飛べる
    public static String METHOD_INFO() {
        try {
            StackTraceElement[] elements = Thread.currentThread().getStackTrace();
            for (StackTraceElement ste: elements) {
                // 最後の改行がないとリンクされない
                if (ste.getClassName().startsWith(PACKAGE_NAME))  return ste.toString() + "\n";
            }
        } catch (Exception e) {
            return "null";
        }
        return "null";
    }

    // CALLED_BYが呼ばれているメソッドを呼び出した[クラス名.メソッド名(行数)]が出力される
    public static String CALLED_BY() {
        try {
            StackTraceElement[] elements = Thread.currentThread().getStackTrace();

            boolean breakFlg = false;
            for (StackTraceElement ste: elements) {
                String cls = ste.getClassName();
                if (breakFlg) {
                    cls = cls.replace(PACKAGE_NAME, "");
                    return cls + "." + ste.getMethodName() + "(" + ste.getLineNumber() + ")";
                }
                if (cls.startsWith(PACKAGE_NAME)) breakFlg = true;
            }
        } catch (Exception e) {
            return "null";
        }
        return "null";
    }
```
<font color="Red">
※注意：これはLogUtilクラスを定義してあるパッケージに、`BuildConfig.APPLICATION_ID(=build.gradleのdefaultConfig.applicationIdの値)`が含まれていないことが前提です。逆にこれらのメソッドを呼び出す側は`BuildConfig.APPLICATION_ID`が含まれるパッケージ上のクラス上で実行される事が前提になります。
僕は`com.android.extensions.util`というパッケージにLogUtilクラスを配置しておくことでこれを実現しています。
また値決定に微小な処理があるためパフォーマンスに関するデバッグを行う場合は使用を避けていただくのがベターです。
</font>

Logデバッグではこれを使ってみて！
====
お勧めの使い方は
個人的には**METHOD_INFO()**をつかったデバッグがお勧めです。ログでデバッグが必要なときで、どこで問題が起きているか突き止めにくい時に、関係ありそうなところの各処理ごとに`Log.d(TAG(this), METHOD_INFO());`をコピペで配置しておけば。
アプリの落ちる前の最後のMETHOD_INFO()を使ったログを使って、AndroidStudioのLogCatから瞬時にマウスクリックで移動してソースを確認するのに役に立ちます。

注意を読んでいただいた上でコレをつかって、ログによるデバッグをもっと楽にしてみたらいかがでしょうか？

ここまで読んでくださった方ありがとうございました。
なおこのコードは[GitHub](https://git.io/vVKtJ)上に公開してあります。