【Java】こんなログは嫌だ！

# INDEX
- 概要
- こんなログは嫌だ！
- あとがき

# 概要
普段、Javaをベースにしたシステム開発をしている事が多いのですが。。。
他人が作ったシステムとか、OSSとか問題が起きたので「さあ、ログを見よう」と思ってもさっぱり意味不明なログしか出力されてない事が多くて叫びたくなるので、叫ぶ代わりに書き殴ってみようと思います。

![D9DEjb0UcAAqJtI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92447/f46e8df8-c6ff-a629-a8ff-6314546efb33.jpeg)


# こんなログは嫌だ！

## 1. そもそも出していない！
```java
HogeService service = new HogeService();
service.init("aaa", "bbb");
if (!service.isInitError()) {
    service.execute();
}
```

- まずは何か出力してみようや…


## 2. 全部【System.out.println("～")】だ！
```java
if (service.isError()) {
    System.out.println("appropriate error message.");
}
```
- コンソールに垂れ流すな！
- ファイルに残ってないかも知れないぞ！


## 3. 例外を握りつぶしている！
```java
try {
    ～（略）～
} catch (HogeException e) {
    // TODO 自動生成された catch ブロック
    e.printStackTrace();
}
```
- だからコンソールに垂れ流ｓ（ｒｙ
    - 画面コンソールに垂れ流すくらいならどうでも良いんだ…。勝手にシスログ出力なんてした日には面倒事に巻き込まれてしまう奴だ
    - あ、あとタイムスタンプが出ないかもよ？
- IDEが自動生成したコードまんま残ってるやん！
- 処理を続行するのか、中断させるのかの判断も必要だし、基本やっちゃダメなやつ


## 4. 例外を握りつぶしていないと主張されたが、やっぱり潰されていた！
```java
try {
    ～（略）～
} catch (HogeException e) {
    System.out.println("error message.");
}
```
- そろそろロガー使お？
    - 「レビューで言われたログ対応しました。確認お願いします！」と自称経験者から言われた時は、もう指摘せずにこっちでさっさと直してしまおうかと悩んだ。
- ちなみに↑のサンプルだと【e.printStackTrace()】より情報少ないからね？


## 5. ロガーのインスタンスを作っているだけだ！
```java
public class HogeService {
    private Logger logger = LoggerFactory.getLog(HogeService.class);
    // ↑ これ以降、loggerインスタンスを使っている箇所がない
    ～（略）～
}
```
- 惜しい！もう少しでロガーを使ったログ出力が出来るぞ！！
- コピペされたコードにありがち

## 6. そもそも他人任せだ！
```java
public class HogeService {
    public void execute() {
        try {
            ～（略）～
        } catch (HogeServiceException e) {
            throw new RuntimeException(e);
        }
    }
}
```
- 本当は【あり】な実装なんだけど…
- いや、例外キャッチ強制を無意味に強制無しにするのはどうか…いやいや…
- 続く


## 7. 当然、呼出元でキャッチされていない
```java
HogeService service = new HogeService();
service.execute();
```
- 続き
- ああ、やっぱり取ってなかった。悪い予感はしてたんだよ。
- 動きが分かっててやらないと、WEBシステムならブラウザに白い画面に【Internal Server Error】とか出ちゃう


## 8. 全部デバッグレベルだ！
```java
HogeService hs = new HogeService();
hs.execute();
if (hs.isError()) {
    logger.debug("fatal error.");
}
FugaService fs = new FugaService();
fs.execute();
if (fs.isError()) {
    logger.debug("critical error.");
}
```
- これ本番環境でログに出ない奴だから！普通は「infoレベルで運用」とかだから！
    - メッセージだけやばい感じにしても意味ないから！！
- これに加えてログローテーションを仕込んでいないとある日ディスクフルに見舞われるが、きっちりディスクサイズ等を監視に入れておかないと原因特定まで時間がかかるぞ…クックック。

## 9. 全部エラーレベルだ！
```java
String userName = request.getParamter("userName");
if (StringUtils.isEmpty(userName)) {
    logger.error("validation error.");
}
service = new HogeService();
service.execute();
if (service.isError()) {
    logger.error("minor error.");
}
```
- これお客さんからの電話止まらなくなる奴だから！
- 入力チェックをエラーレベルとかやり過ぎで、ユーザが変な文字入れる度に監視に引っかかっちゃうから！


## 10. エラーが発生した事しか分からない
```java
String arg = request.getParameter("hoge");
try {
    return Integer.parseInt(arg);
} catch (NumberFormatException e) {
    logger.info("An error has occurred.");
}
```
- エラーが起きた。そんな事は分かってるんだ。
- 本番環境でデバッガを走らせるつもりなのかい？


## 11. エラーの原因が分からない
```java
UserService us = new UserService();
UserInfo user = us.getUser(request.getSession());
String age = request.getParameter("age");
try {
    user.setAge(Integer.parseInt(age));
    us.update(user);
} catch (NumberFormatException e) {
    logger.info("conversion(string -> int) error.");
}
```
- 文字を数字に変換したかったんだろ？もう少しだ！もう少し情報があると原因究明に役立つんだ！
- "age"の中にはどんな値が入っていたんだい？


## 12. 誰がエラーを起こしたのか分からない
```java
UserService us = new UserService();
UserInfo user = us.getUser(request.getSession());
String age = request.getParameter("age");
try {
    user.setAge(Integer.parseInt(age));
    us.update(user);
} catch (InvalidUserInfoException e) {
    logger.error("User data is invalid. / age=[" + age + "]");
}
```
- 物によっては誰がやらかしたのかを特定する必要もある
    - ADに入っているユーザ情報が間違ってたとか
    - ```logger.error("User data is invalid. / age=[" + age + "], userName=[" + user.getName() + "]");```
- そういやObjectは、文字列として出力しようとすると、toString()が呼ばれるので、【UserInfo#toString()】をOverrideしておけば綺麗に出力できるが注意も必要
    - そういや、[lombok](https://projectlombok.org/)とかはアノテーションで自動生成してくれたな。フィールド事に除外設定も可能。スゴイ


## 13. そうは言っても、これは出し過ぎだ！
```java
UserService us = new UserService();
UserInfo user = us.getUser(request.getSession());
String age = request.getParameter("age");
try {
    user.setAge(Integer.parseInt(age));
    us.update(user);
} catch (InvalidUserInfoException e) {
    logger.error("User data is invalid. / userName=" + user.getUserName()
        + ", age=" + user.getAge()
        + ", email=" + user.getEmail()
        + ", password=" + user.getPassword() + "]";
}
```
- 本当は名前とかだけで特定出来たのに、余計なものも出しちゃってセキュリティ部門から怒られる奴
- 追記分、Kilisameさん感謝！


## 14. 使わないメッセージを作っている
```java
StringBuilder msg = new StringBuilder();
HogeService hs = new HogeService();
hs.execute();
if (hs.isError()) {
    msg.append("hoge's error.").append('\n');
}
FugaService fs = new FugaService();
fs.execute();
if (fs.isError()) {
    msg.append("fuga's error.").append('\n');
}
if (logger.isWarning()) {
    logger.warn(msg);
}
```

- これはほぼイチャモンで、実装テクニックだから運用保守マンはスルーでOK。
- 以下の流れで実装すると、ログレベルがerrorの時、メッセージ生成コストが無駄になってしまう
    - 処理に応じてメッセージを組み立てる
    - ログレベルを調べる
    - warnレベル以上だったらログ出力する
- 対処としてはこんな感じか
    - Javaバージョン関わらずに出来るのは、ログレベルの判定箇所を工夫するなど
    - Java8以降なら、Supplierクラスで遅延実行するロガーに挑戦しても良いと思う
        - ちなみに[標準ロガー](https://docs.oracle.com/javase/jp/8/docs/api/java/util/logging/Logger.html)は対応している

# あとがき
- 何か気がついたら追記するかも知れません。
- 6/18 Kilisameさんのコメントが個人的にあるあるだったので、「そうは言っても、これは出し過ぎだ！)」を追加して後ろの番号ずらしました。ありがとうございました！
- では ﾉｼ