Amarengo

Articles and news

9JavaアプリケーションでのSinsのログ記録

Javaアプリケーションでのランタイム情報のログ記録は、特に予期しないシナリオ、エラーが発生した場合、または特定のアプリケーションイベントを追跡する必要がある場合に、アプリケーションの動作を理解するために非常に役立ちます。

実際の本番環境では、通常、デバッグの贅沢はありません。 したがって、再現が容易ではない問題を診断しようとするときには、ログファイルが唯一のものになる可能性があります。

適切に完了すると、ログファイルは問題の原因と発生時のシステムの状態を手がかりにすることで、多くの時間を節約することもできます。 また、ログ記録は、監査目的、統計の収集、ビジネスインテリジェンスの抽出、およびその他のさまざまなタスクに役立ちます。

全体的に、ロギングは確かにアプリケーションの有効期間中に大きな利点を提供する基本的な方法です–ので、できるだけ多くのログデータの記録を開

しかし、ログの不適切な使用にも重大な欠点があります。

以下のセクションでは、アプリケーションでログインを使用するときに実行できる最も一般的で最も有害なプラクティスをいくつか見ていきます。

すべての例と設定は、一般的なlog4j2ライブラリを使用しています。 LogbackはStackifyでも十分にサポートされている別の素晴らしいオプションです。

9Javaロギングの問題とその回避方法

機密情報のロギング

まず、”可能な限り念のためにログを記録する”アプローチによってもたらされる最も有害なロ

ほとんどのアプリケーションは、ユーザーの資格情報や財務情報など、非公開のままにする必要があるデータを処理します。 このタイプの情報をプレーンテキストファイルに記録する危険性は明確です。

さらに、財務情報などの一部のカテゴリのデータを記録することも厳しく規制されており、重大な法的影響を及ぼす可能性があります。

これを回避する最善の方法は、この種の機密情報を決して記録しないようにすることです。

ログファイルを暗号化するなどの代替手段がありますが、一般的にこれらのファイルは全体的に使用可能ではなく、理想的ではありません。

先に進む前に、ここでは非常に慎重にロギングする必要がある情報の種類のより包括的なリストがあります。

ログプレーンユーザー入力

Javaアプリケーションの別の一般的なセキュリティ上の問題は、JVMログ鍛造です。

簡単に言えば、ユーザー入力や他の信頼できないソースなどの外部ソースからのデータがログに直接書き込まれると、ログ鍛造が発生する可能性があります。 悪意のある攻撃者は、”\n\nweb–2017-04-12 17:47:08,957info Amount reversed successfully”のようなログエントリをシミュレートする入力を入力することができ、ログデータが破損する可能性があ

この種の脆弱性を処理するには、さまざまな方法があります:

  • ユーザー入力をログに記録しない–ユーザーデータはいくつかの問題の根本原因に到達するために重要である可能性があるため、常に可能ではありません
  • ログに記録する前に検証を使用する–このソリューションは、パフォーマンスに影響を与えるだけでなく、重要な情報
  • データベースへのログ記録に影響を与える可能性があります–パフォーマンスに関してはより安全ですがコストがかかり、別の脆弱性をもたらす可能性があります–SQLインジェクション
  • エンタープライズのようなツールを使用するOWASP

のセキュリティAPI esapiを使用することは間違いなく良い方法です; OWASPのこのオープンソースのセキュリティライブラリは、ログに書き込む前にデータをエンコードできます:

message = message.replace( '\n' , '_' ).replace( '\r' , '_' ) .replace( '\t' , '_' );message = ESAPI.encoder().encodeForHTML( message );

過度のロギング

避けるべきもう一つの方法は、あまりにも多くの情報をロギングすることです。 これは、潜在的に関連するすべてのデータをキャプチャしようとすると発生する可能性があります。

このアプローチの可能性があり、非常に現実的な問題の一つは、パフォーマンスの低下です。 しかし、ロギングライブラリの進化に伴い、これを心配するツールがなくなりました。

性能向上の一例として、2.xバージョンのlog4jは非同期ロギングを使用します。

ログメッセージが多すぎると、問題が発生したときにログファイルを読み取り、関連する情報を識別することが困難になる可能性もあります。

コードのログ行数を減らす1つの方法は、システム内の横断的な懸念事項にわたって重要な情報をログに記録することです。

たとえば、特定のメソッドの開始と終了をログに記録する場合は、指定されたカスタム注釈を持つすべてのメソッドに対してこれを行うアスペクト:

@Aspectpublic class MyLogger { private static final Logger logger = LogManager.getLogger(MyLogger.class); @Around("execution(* *(..)) && @annotation(LogMethod)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("Starting method execution: " + joinPoint.getSignature().getName() + " in class:"+joinPoint.getSignature().getDeclaringTypeName()); Object result = joinPoint.proceed(); logger.info("Exiting method execution: " + joinPoint.getSignature().getName() + " in class:"+joinPoint.getSignature().getDeclaringTypeName()); return result; }}

カスタム側面の助けを借りて、我々は今、非常に選択的であると我々は実際にログにその情報を必要とするアプリケーションの正確な領域を選ぶことがで その結果、システム全体のログフットプリントを大幅に削減することができます。

不可解なログメッセージ

ログファイルを解析するとき、十分な情報を提供していない行に遭遇するとイライラすることがあります。 一般的な落とし穴は、ログメッセージの特異性やコンテキストの欠如です。

問題を説明するために、特異性のないログメッセージを見てみましょう:

Operation failed.

代わりに、より具体的で識別可能な情報を追加できます:

File upload picture.jpg failed.

あなたのログは最も確かに別の開発者やシステム管理者によって読み取られ、アプリケーションで何が起こったのかを理解する必要があることに

ログメッセージにコンテキストを追加するには、タイムスタンプ、ログレベル、スレッド名、イベントの完全修飾クラス名を含めることをお勧めします。 このようにして、実行時に特定のイベントがいつどこで発生するかをより簡単に識別できます。

log4j2を使用するときにこの情報を追加するには、日付に%d、ログレベルに%p、スレッド名に%t、クラス名に%cオプションを使用してパターンレイアウ:

<PatternLayout> <Pattern>%d %p %c - %m%n</Pattern></PatternLayout>

上記のレイアウトを使用したログメッセージは次のようになります:

2017-05-11 22:51:43,223 INFO com.stackify.service.MyService - User info updated

単一のログファイル

を使用するアプリケーションに単一のログファイルのみを使用することの欠点は、時間の経過とともに非常に大きくなり、作業が困難になることです。

関連する情報をすばやく見つけるための良い方法は、ファイル名の一部として日付を使用して、毎日新しいログファイルを作成することです。

log4j2ライブラリを使用する場合、現在の日付と同じ名前のログファイルを作成する方法の例を見てみましょう:

SimpleLayout layout = new SimpleLayout();FileAppender appender = new FileAppender(layout, LocalDate.now().toString(), false);logger.addAppender(appender);

同じライブラリには、特定の時間間隔で新しいログファイルを作成するローリングファイルアペンダーを構成するオプションも用意されています:

<RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c - %m%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="250 MB"/> </Policies> <DefaultRolloverStrategy max="20"/></RollingFile>

上記の設定では、現在の日付をファイル名としてファイルごとに最大250MBのファイルが作成され、年-月の形式の名前を持つフォルダに配置されます。

不適切なログレベルを選択する

不適切なログレベルを選択すると、重要なイベントが欠落したり、重要でないデータが大量に浸水したりします。

簡単に言えば、システム内のさまざまなログに適切なログレベルを選択することは、ログを理解するために必要な中核的なものの1つです。

ほとんどのロギングフレームワークには、FATAL、ERROR、WARN、INFO、DEBUG、TRACEに似たレベルのセットがあり、最高から最低に順序付けられています。

使用可能なログレベル

これらのそれぞれと、重大度に基づいて含める必要があるログメッセージの種類を見てみましょう:

  • FATALは、アプリケーションがクラッシュしたり、起動に失敗したりするエラーのために予約する必要があります(例:
  • エラーには、システムが正常に機能するために解決する必要がある技術的な問題が含まれている必要があります(例:データベースに接続できませんでした)
  • WARNは、アプリケーションの機能を大幅に妨げない一時的な問題や予期しない動作に最適です(例:ユーザーログインに失敗しました)
  • INFOには、アプリケーションで何が起こっているかを説明するメッセージが含まれている必要があります(例:ユーザー登録、注文)
  • >debugは、問題のデバッグに役立つ可能性のあるメッセージを対象としています(例: メソッドの実行が開始されました)
  • TRACEはDEBUGに似ていますが、より詳細なイベントが含まれています(例:データモデルが更新されました)

ログレベルの制御

メッセージのログレベルは、メッセージの書き込み時に設定されます:

logger.info("Order ID:" + order.getId() + " placed.");

ロギングApiでは、通常、メッセージを表示するレベルを設定できます。 つまり、たとえば、アプリケーションまたは特定のクラスのログレベルをINFOに設定すると、FATAL、ERROR、WARN、INFOのレベルのメッセージのみが表示され、DEBUGメッセージとTRACEメッ

これは、通常、開発中にデバッグまたは下位のメッセージを表示するのに役立ちますが、本番環境では表示されません。Log4j2を使用して、log4j2でクラス、パッケージ、またはアプリケーション全体のログレベルを設定する方法は次のとおりです。プロパティファイル:

loggers=classLogger,packageLoggerlogger.classLogger.name=com.stackify.service.MyServicelogger.classLogger.level=infologger.packageLogger.name=com.stackify.configlogger.packageLogger.level=debug
rootLogger.level=debug

メッセージにログレベルを表示するには、log4j2patternlayoutに%pオプションを追加します。

先に進む前に、既存のレベルがアプリケーションのニーズに適していない場合は、カスタムログレベルも定義できることに注意してください。

複数のシステム間で単一の操作を追跡し、ログ

着信要求を処理するために連携する複数の独立して展開されたサービスを持つ分散システムでは、これらすべてのシステム間で単一の要求を追跡することは困難な場合があります。

単一のリクエストがこれらのサービスの複数にヒットする可能性が非常に高く、問題が発生した場合、何が起こったかの全体像を得るために、これらのシ

この種のアーキテクチャに対処するために、zipkinやSpring Cloud Sleuthなどの新世代のロギングヘルパーツールがエコシステムにあります。

Zipkinは、マイクロサービスアーキテクチャ間で要求をトレースし、問題の原因となっているアプリケーションを特定するのに役立ちます。 また、アプリケーション、トレースの長さ、またはタイムスタンプに基づいてトレースをフィルタリングできる便利なUIが付属しています。

Spring Cloud Sleuthプロジェクトは、各トレースに一意の64ビットIDを追加することで機能します。 このようにして、要求を複数のサービス間で識別できます。

これらのツールは、コアライブラリの制限に対処し、より分散されたスタイルのアーキテクチャの新しい現実をナビゲートします。

JSONでログを記録しない

平文形式でのログ記録は非常に一般的ですが、ログストレージとデータ分析システムの出現により、JSONに移行しました。主要なアプリケーションログ形式としての

JSONには、プレーンテキストと同じくらい読みやすく、自動処理ツールで解析するのがはるかに簡単であるという利点があります。

たとえば、Log4J2はこの正確な目的のためにJSONLayoutを提供します:

<JSONLayout complete="true" compact="false"/>

これにより、整形式のJSONドキュメントが生成されます:

JSONとして、ログデータは、Retraceなどのログ管理システムによって処理されると、意味的に豊かになります。

パフォーマンスへのロギングの影響

最後に、アプリケーションにロギングを追加するときに避けられない問題、すなわちパフォーマンスへの影響を考

パフォーマンスのわずかな低下が予想されます。 ただし、これを最小限に抑え、システムを遅くしないようにするには、これを追跡することが重要です。

ロギングAPIを選択する際に考慮すべきパフォーマンス関連のいくつかの側面は次のとおりです:

  • バッファを使用したファイルI/O操作–ファイルI/Oは高価な操作であるため、これは重要です
  • 非同期ロギング–これは、ロギングが他のアプリケーフィルタリング–これは、メッセージに対応するログレベルが有効になっているかどうかを確認するために行われ、階層を走査するか、ロガーがロガー設定; 後者のアプローチは、パフォーマンスに関して好ましい

もちろん、選択肢をオープンにし、システムを柔軟に保つ必要がある場合は、slf4jのような高レベ:

  • 冗長パッケージ用のアプリケーションのログレベルを調整する
  • 実行時にソースの場所情報をログに記録することを避ける現在のスレッド、ファイルを4306>
  • 本番環境に移行する前にログを確認して、ログを削除できるかどうかを確認します

佳作

最後に、次のようにしましょう あなたが避けるべき最後の練習を見てください–そしてそれはロギングの代わりに標準出力を使用しています。

out()は、開発サイクルの非常に早い段階で開始する簡単な方法になる可能性がありますが、その時点の後に従うことは間違いありません。

専用のロギングAPIのすべての強力な機能を失うという事実に加えて、ここでのこの主な欠点は、ロギングデータがどこにも保持されないという事実

最後に、もう一つの佳作は、ログデータの読み取りと分析をはるかに簡単にすることができる練習です–標準化されたログメッセージ。 簡単に言えば、同様のイベントにはログに同様のメッセージが含まれているはずです。

その特定のイベントのすべてのインスタンスを検索したり、ログデータから意味のある洞察を抽出したりする必要がある場合は、標準のログメッセージ

たとえば、アップロード操作が失敗した場合、ログにこれらの異なるメッセージが含まれていると混乱します:

Could not upload file picture.jpg
File upload picture.jpg failed.

代わりに、ファイルのアップロードが失敗するたびに、これらのメッセージのいずれかを一貫して使用して失敗をログに記録する必要があります。

結論

ロギングの使用は、システムのランタイムにもたらす非常に有用で実用的な洞察のために、アプリケーション開発で遍在するようになりました。

しかし、ログデータを最大限に活用するには、基本を超えて、ログの文化を開発し、このデータを大規模かつ本番環境で操作する細かい点を理解することが

コメントを残す

メールアドレスが公開されることはありません。