ロガーへの移行はSensei

2020年11月30日発行
アラン・リチャードソン著
ケーススタディ

ロガーへの移行はSensei

2020年11月30日発行
アラン・リチャードソン著
リソースを見る
リソースを見る

ロガーへの移行はSensei

この記事では、System.out.printlnからJava Loggerを使用するように移行するためのレシピを作成します。

TDDではなく、コードを書いていてミスをしたときに、System.outに1行出力してしまう悪い癖があり、その癖をなくしたいと思っていました。

以下のコードを書く際に、たくさんの間違いを犯してしまいました。

   private String getCountdownString() {
        String output = "";
        String prefix="";
        for(int countdown = 10; countdown > 0; countdown-- ){
            output = output + prefix + countdown;
            System.out.println(output);
            prefix=", ";
        }
        System.out.println(output);
        return output;
    }


最初、countdown++と書いたところ、ループが終了しませんでした。

また、countdown > 1を使用したため、希望の出力が得られませんでした。

最終的には、デバッグのためにSystem.out.printlnをコードに散りばめました。そして、この経験から、デフォルトのアプローチとしてロガーの使い方を学ぶ必要があると強く感じました。

リサーチ

幸いなことに、私はSensei のドキュメントに目を通し、System.out.println から変換するためのレシピを作成し、ロガーの使用を促す「Getting Started」ガイドを使用することにしました。

  • java.util.logging.Logger

レシピの作成

まず、printlnをクリックしてからalt+enterで新しいレシピを作ります。

システムアウトプリント 新しいレシピを作成する


以下の内容で作成しています。


名前を教えてください。ロガー:printlnの代わりにロガーを使う
説明: printlnの代わりにloggerを使用 - System.out.printlnの使用をやめることを忘れないでください。
レベルです。エラー


まずはprintlnという名前のメソッドコールをマッチングさせてみます。


の検索を行います。
  メソッドコールを使用します。
    名前: "println"

また、プレビューでは、私のコードにマッチするものがすべて表示されます。


レシピ設定方法call Println


私のコードでは、すべてのマッチがSystem.out.printlnであることがわかりますが、長期的にはこれが唯一のマッチであるとは信じられません。私が変更したいのは、より修飾されたステートメントにマッチさせたいのです。

私はマッチャーを拡張して、クラス「System」で指定されたフィールドのメソッドコールを検索します。


の検索を行います。
  メソッドコールを使用します。
    名前: "println"
    "on "になっています。
      の分野でも活躍しています。
        である。
          クラスを作成しました。
            name: "System"
        名前: "out"



望むならば、Systemの名前をjava.lang.Systemに完全に修飾することもできます。

レシピ設定方法呼び出し名方式

規約の改正


次に、QuickFixを作成したいと思います。

まず最初に、出力を記録するコードの行を修正したいと思います。


availableFixes:
- name: "use Logger"
  のアクションを行います。
  - を書き換えます。
      to: "logger.log(Level.INFO, {{{ arguments.0 }}})"



ヒゲのテンプレート形式を覚える必要はありませんね。私はGUIのShow Variablesを使って引数を表示させ、それをダブルクリックしました。すると、GUIが適切にマッチした口ひげのテンプレートを埋めてくれました。



実際に試してみると、Level enumをインポートするには、まだalt+enterしなければならないことがわかります。しかし、QuickFixを修正して完全修飾のアイテムを持つようにすると、Sensei がインポートを追加してくれます。

  • これは、System.out.println(output);を次のように置き換えます。

logger.log(Level.INFO, output);

  • そして、enumのインポートを追加します。

import java.util.logging.Level.Login

  • に書き換えたら。
logger.log(java.util.logging.Level.INFO, {{{ arguments.0 }}})

そして、これはうまくいくのですが、そもそもロガーをインスタンス化するための構文を覚えておかなければなりません。

ロガーを最初にインスタンス化するためのシンタックス


ロガーフィールドを追加するためのコードの修正

私もQuickFixを修正して、フィールドを作成することができます。

まずロガーをコード化し、それをレシピに追加することで、二度とコード化する必要がなくなります。

Logger logger2 = Logger.getLogger(SysOutTest.class.getName());


私は、生成してほしいサンプルコードを最初に書くことが多いのですが、そうすればIntelliJのコード補完や構文チェックを使って、正しいコードが生成されているかどうかを確認することができるからです。副次的な効果として、レシピを編集してそのコードを生成するQuickFixの行を追加したときに、そのコードがコードプレビューに表示されます。

Sensei は重複したフィールドを追加しないように気をつけているので、違う名前を使って誤魔化さなければならないからです。

そこで、このコードを作成するためにレシピを修正し、loggerというフィールドを追加します。

availableFixes:

- name: "use Logger"
  のアクションを行います。
  - を書き換えます。
      to: "logger.log(java.util.logging.Level.INFO, {{{ arguments.0 }}})"
  - addFieldです。
      field: "java.util.logging.Logger logger = Logger.getLogger({{{ containingClass.name\
        }}.class.getName())"
      ターゲット: "parentClass"


SysOutTestをmustache変数にして、このレシピを使用するクラスの名前を拾うようにしたことに注意してください。繰り返しになりますが、私はmustacheの構文を覚えていなかったので、GUIのShow Variablesを使って必要な置換を見つけました。

Loggerをjava.util.logging.Loggerに完全に修飾することで、Sensei はインポートを追加し、私が望むコードの行を書きます。

Logger logger = Logger.getLogger(SysOutTest.class.getName());


Loggerの完全修飾 java.util.logging.Loggerへ


このレシピの便利な点は、loggerフィールドを一度だけ追加するので、「System.out.println」を使用した既存のコードにこのレシピを使用し、Sensei を使用してコードファイル内のすべての出現箇所を同時に変更することができることです。


ロガーを使用する ファイル内のすべての問題を修正する



次のステップ

これに慣れれば、System.out.printlnを使わないように訓練することができます。

Sensei を利用して、ロガーを作成するのに役立つ2つ目のレシピを作成することで、積極的にコードを書くことができます。

例えば、loggerというフィールドが存在しないクラスにマッチして、フィールドを追加することができます。

レベル情報のレシピを作成した場合


名前を教えてください。ロガー:ロガーの追加

説明クラスにロガーを追加


ロガーフィールドのないクラスにマッチさせるには

の検索を行います。
  クラスを作成しました。
    を使わずに。
      の子です。
        の分野でも活躍しています。
          name: "logger"


そして、先ほどのQuickFixの一部を再利用します。


availableFixes:

  - name: "Add Logger" (ロガーの追加)
    のアクションを行います。
      - addFieldです。
          field: "java.util.logging.Logger logger = Logger.getLogger({{{ containingClass.name\
        }}.class.getName())"
          target: "self"


最初のQuickFixと比較して、ここでのターゲットの違いに注目してください。これは、Search がクラスにマッチしたため、self を使用しています。最初のQuickFixでは、クラス自体のコードにマッチしたため、parentClassを使用しています。

概要

これは、Sensei を利用して個人のプログラミングスキルを向上させるための重要な流れの一つです。

  • 目先の「ベスト・プラクティス」に役立つレシピを作る
  • ベストプラクティスの使い方がわかったら、ワークフローを高速化するためのレシピを作成します。


---


IntelliJの「Preferences ‾ Plugins」(Mac)または「Settings ‾ Plugins」(Windows)から、「sensei secure code」を検索して、「Sensei 」をインストールすることができます。


このためのソースコードとレシピは、Secure Code Warrior GitHub アカウントの `sensei-blog-examples` リポジトリの `pojoexamples` モジュールの中にあります。




リソースを見る
リソースを見る

著者

アラン・リチャードソン

もっと知りたい?

セキュアコーディングに関する最新の知見をブログでご紹介しています。

当社の豊富なリソースライブラリは、安全なコーディングアップスキルに対する人間的なアプローチを強化することを目的としています。

ブログを見る
もっと知りたい?

開発者主導のセキュリティに関する最新の研究成果を入手する

ホワイトペーパーからウェビナーまで、開発者主導のセキュアコーディングを始めるために役立つリソースが満載のリソースライブラリです。今すぐご覧ください。

リソース・ハブ

ロガーへの移行はSensei

2020年11月30日発行
アラン・リチャードソン著

ロガーへの移行はSensei

この記事では、System.out.printlnからJava Loggerを使用するように移行するためのレシピを作成します。

TDDではなく、コードを書いていてミスをしたときに、System.outに1行出力してしまう悪い癖があり、その癖をなくしたいと思っていました。

以下のコードを書く際に、たくさんの間違いを犯してしまいました。

   private String getCountdownString() {
        String output = "";
        String prefix="";
        for(int countdown = 10; countdown > 0; countdown-- ){
            output = output + prefix + countdown;
            System.out.println(output);
            prefix=", ";
        }
        System.out.println(output);
        return output;
    }


最初、countdown++と書いたところ、ループが終了しませんでした。

また、countdown > 1を使用したため、希望の出力が得られませんでした。

最終的には、デバッグのためにSystem.out.printlnをコードに散りばめました。そして、この経験から、デフォルトのアプローチとしてロガーの使い方を学ぶ必要があると強く感じました。

リサーチ

幸いなことに、私はSensei のドキュメントに目を通し、System.out.println から変換するためのレシピを作成し、ロガーの使用を促す「Getting Started」ガイドを使用することにしました。

  • java.util.logging.Logger

レシピの作成

まず、printlnをクリックしてからalt+enterで新しいレシピを作ります。

システムアウトプリント 新しいレシピを作成する


以下の内容で作成しています。


名前を教えてください。ロガー:printlnの代わりにロガーを使う
説明: printlnの代わりにloggerを使用 - System.out.printlnの使用をやめることを忘れないでください。
レベルです。エラー


まずはprintlnという名前のメソッドコールをマッチングさせてみます。


の検索を行います。
  メソッドコールを使用します。
    名前: "println"

また、プレビューでは、私のコードにマッチするものがすべて表示されます。


レシピ設定方法call Println


私のコードでは、すべてのマッチがSystem.out.printlnであることがわかりますが、長期的にはこれが唯一のマッチであるとは信じられません。私が変更したいのは、より修飾されたステートメントにマッチさせたいのです。

私はマッチャーを拡張して、クラス「System」で指定されたフィールドのメソッドコールを検索します。


の検索を行います。
  メソッドコールを使用します。
    名前: "println"
    "on "になっています。
      の分野でも活躍しています。
        である。
          クラスを作成しました。
            name: "System"
        名前: "out"



望むならば、Systemの名前をjava.lang.Systemに完全に修飾することもできます。

レシピ設定方法呼び出し名方式

規約の改正


次に、QuickFixを作成したいと思います。

まず最初に、出力を記録するコードの行を修正したいと思います。


availableFixes:
- name: "use Logger"
  のアクションを行います。
  - を書き換えます。
      to: "logger.log(Level.INFO, {{{ arguments.0 }}})"



ヒゲのテンプレート形式を覚える必要はありませんね。私はGUIのShow Variablesを使って引数を表示させ、それをダブルクリックしました。すると、GUIが適切にマッチした口ひげのテンプレートを埋めてくれました。



実際に試してみると、Level enumをインポートするには、まだalt+enterしなければならないことがわかります。しかし、QuickFixを修正して完全修飾のアイテムを持つようにすると、Sensei がインポートを追加してくれます。

  • これは、System.out.println(output);を次のように置き換えます。

logger.log(Level.INFO, output);

  • そして、enumのインポートを追加します。

import java.util.logging.Level.Login

  • に書き換えたら。
logger.log(java.util.logging.Level.INFO, {{{ arguments.0 }}})

そして、これはうまくいくのですが、そもそもロガーをインスタンス化するための構文を覚えておかなければなりません。

ロガーを最初にインスタンス化するためのシンタックス


ロガーフィールドを追加するためのコードの修正

私もQuickFixを修正して、フィールドを作成することができます。

まずロガーをコード化し、それをレシピに追加することで、二度とコード化する必要がなくなります。

Logger logger2 = Logger.getLogger(SysOutTest.class.getName());


私は、生成してほしいサンプルコードを最初に書くことが多いのですが、そうすればIntelliJのコード補完や構文チェックを使って、正しいコードが生成されているかどうかを確認することができるからです。副次的な効果として、レシピを編集してそのコードを生成するQuickFixの行を追加したときに、そのコードがコードプレビューに表示されます。

Sensei は重複したフィールドを追加しないように気をつけているので、違う名前を使って誤魔化さなければならないからです。

そこで、このコードを作成するためにレシピを修正し、loggerというフィールドを追加します。

availableFixes:

- name: "use Logger"
  のアクションを行います。
  - を書き換えます。
      to: "logger.log(java.util.logging.Level.INFO, {{{ arguments.0 }}})"
  - addFieldです。
      field: "java.util.logging.Logger logger = Logger.getLogger({{{ containingClass.name\
        }}.class.getName())"
      ターゲット: "parentClass"


SysOutTestをmustache変数にして、このレシピを使用するクラスの名前を拾うようにしたことに注意してください。繰り返しになりますが、私はmustacheの構文を覚えていなかったので、GUIのShow Variablesを使って必要な置換を見つけました。

Loggerをjava.util.logging.Loggerに完全に修飾することで、Sensei はインポートを追加し、私が望むコードの行を書きます。

Logger logger = Logger.getLogger(SysOutTest.class.getName());


Loggerの完全修飾 java.util.logging.Loggerへ


このレシピの便利な点は、loggerフィールドを一度だけ追加するので、「System.out.println」を使用した既存のコードにこのレシピを使用し、Sensei を使用してコードファイル内のすべての出現箇所を同時に変更することができることです。


ロガーを使用する ファイル内のすべての問題を修正する



次のステップ

これに慣れれば、System.out.printlnを使わないように訓練することができます。

Sensei を利用して、ロガーを作成するのに役立つ2つ目のレシピを作成することで、積極的にコードを書くことができます。

例えば、loggerというフィールドが存在しないクラスにマッチして、フィールドを追加することができます。

レベル情報のレシピを作成した場合


名前を教えてください。ロガー:ロガーの追加

説明クラスにロガーを追加


ロガーフィールドのないクラスにマッチさせるには

の検索を行います。
  クラスを作成しました。
    を使わずに。
      の子です。
        の分野でも活躍しています。
          name: "logger"


そして、先ほどのQuickFixの一部を再利用します。


availableFixes:

  - name: "Add Logger" (ロガーの追加)
    のアクションを行います。
      - addFieldです。
          field: "java.util.logging.Logger logger = Logger.getLogger({{{ containingClass.name\
        }}.class.getName())"
          ターゲット: "self"


最初のQuickFixと比較して、ここでのターゲットの違いに注目してください。これは、Search がクラスにマッチしたため、self を使用しています。最初のQuickFixでは、クラス自体のコードにマッチしたため、parentClassを使用しています。

概要

これは、Sensei を利用して個人のプログラミングスキルを向上させるための重要な流れの一つです。

  • 目先の「ベスト・プラクティス」に役立つレシピを作る
  • ベストプラクティスの使い方がわかったら、ワークフローを高速化するためのレシピを作成します。


---


IntelliJの「Preferences ‾ Plugins」(Mac)または「Settings ‾ Plugins」(Windows)から、「sensei secure code」を検索して、「Sensei 」をインストールすることができます。


このためのソースコードとレシピは、Secure Code Warrior GitHub アカウントの `sensei-blog-examples` リポジトリの `pojoexamples` モジュールの中にあります。




弊社製品や関連するセキュアコーディングのトピックに関する情報をお送りする許可をお願いします。当社は、お客様の個人情報を細心の注意を払って取り扱い、マーケティング目的で他社に販売することは決してありません。

送信
フォームを送信するには、「Analytics」のCookieを有効にしてください。完了したら、再度無効にしてください。