web-dev-qa-db-ja.com

組み込みの文字列フォーマットとロギングパラメータとしての文字列連結

SonarLint を使用しています。これは、次の行に問題があることを示しています。

LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

補足:この行を含むメソッドは頻繁に呼び出される場合があります。

この問題の説明は

「前提条件」とロギング引数は評価を必要としないはずです(squid:S2629)

さらなる評価が必要なメッセージ引数をGuava com.google.common.base.Preconditionsチェックに渡すと、パフォーマンスが低下する可能性があります。それは、それらが必要かどうかにかかわらず、メソッドが実際に呼び出される前に各引数を解決する必要があるためです。

同様に、連結された文字列をログメソッドに渡すと、ログレベルがメッセージを表示するのに十分かどうかにかかわらず、メソッドが呼び出されるたびに連結が実行されるため、不必要なパフォーマンスヒットが発生する可能性があります。

代わりに、静的な値または事前に計算された値を前提条件条件チェックおよび呼び出しの呼び出しに渡すようにコードを構成する必要があります。

具体的には、文字列の連結の代わりに組み込みの文字列フォーマットを使用する必要があります。メッセージがメソッド呼び出しの結果である場合は、前提条件をすべてスキップし、代わりに関連する例外を条件付きでスローする必要があります。

違反コード例

logger.log(Level.DEBUG, "Something went wrong: " + message);  // Noncompliant; string concatenation performed even when log level too high to show DEBUG messages

LOG.error("Unable to open file " + csvPath, e);  // Noncompliant

Preconditions.checkState(a > 0, "Arg must be positive, but got " + a); // Noncompliant. String concatenation performed even when a > 0

Preconditions.checkState(condition, formatMessage());  //Noncompliant. formatMessage() invoked regardless of condition

Preconditions.checkState(condition, "message: %s", formatMessage()); // Noncompliant

適合ソリューション

logger.log(Level.SEVERE, "Something went wrong: %s", message);  // String formatting only applied if needed

logger.log(Level.SEVERE, () -> "Something went wrong: " + message); //since Java 8, we can use Supplier , which will be evaluated lazily

LOG.error("Unable to open file {}", csvPath, e);

if (LOG.isDebugEnabled() {   LOG.debug("Unable to open file " + csvPath, e);  // this is compliant, because it will not evaluate if log level is above debug. }

Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a);  // String formatting only applied if needed

if (!condition) {   throw new IllegalStateException(formatMessage()); // formatMessage() only invoked conditionally }

if (!condition) {   throw new IllegalStateException("message: " + formatMessage()); }

私はこの権利を理解しているかどうか100%確信していません。なぜこれが本当に問題なのでしょうか。特に、文字列連結を使用する場合のパフォーマンスヒットに関する部分。なぜなら、文字列の連結はフォーマットよりも高速だとよく読んだからです。

EDIT:誰かが私に

LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

そして

LOGGEr.debug("Comparing objects: {} and {}",object1, object2);

バックグラウンドにあります。なぜなら、文字列はメソッドに渡される前に作成されると思うからです。正しい?だから私にとって違いはありません。しかし、明らかに私は間違っています。SonarLintが文句を言っているからです

15
Naxos84

答えがあると思います。

連結は事前に条件チェックで計算されます。したがって、ロギングフレームワークを条件付きで10K回呼び出し、それらすべてがfalseと評価された場合、理由なしに10K回連結されます。

このトピック も確認してください。そして、イカロの答えのコメントを確認してください。

StringBuilder もご覧ください。

11
luso

以下のロギングステートメントを検討してください。

LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

この「デバッグ」とは何ですか?

これはロギングステートメントのレベルであり、LOGGERのレベルではありません。参照してください、2つのレベルがあります。

a)ロギングステートメントの1つ(ここではデバッグ):

"Comparing objects: " + object1 + " and " + object2

b)1つはロガーのレベルです。したがって、LOGGERオブジェクトのレベルは何ですか:これはコードまたは一部のxmlで定義する必要があります。そうでない場合は、祖先からレベルを取得します。

なぜ私はこれをすべて言っているのですか?

次の場合にのみ、ロギングステートメントが印刷されます(または、より専門的な用語で「アペンダー」に送信されます)。

Level of logging statement >= Level of LOGGER defined/obtained from somewhere in the code

レベルの可能な値は

DEBUG < INFO <  WARN < ERROR

(ロギングフレームワークに応じて、さらにいくつかあります)

質問に戻りましょう:

"Comparing objects: " + object1 + " and " + object2

上記で説明した「レベルルール」が失敗した場合でも、常に文字列が作成されます。

しかしながら、

LOGGER.debug("Comparing objects: {} and {}",object1, object2);

「上記のレベルルール」が満たされる場合にのみ、文字列が形成されます。

それでは、どちらがよりスマートですか?

これを参照してください rl

5

文字列の連結は、LOGGER.info( "The program started at" + new Date());を意味します。

ロガーの組み込みフォーマットは
LOGGER.info( "{}でプログラムが開始されました"、new Date());

違いを理解するための非常に良い記事 http://dba-presents.com/index.php/jvm/Java/120-use-the-built-in-formatting-to-construct-this-argument =

4
Shubham Pandey