Tomcatでのロギング

目次

はじめに

Apache Tomcatの内部ロギングは、java.util.loggingフレームワークを使用するようにハードコードされている、Apache Commons Loggingのパッケージ名を変更したフォークであるJULIを使用しています。これにより、WebアプリケーションがApache Commons Loggingを使用している場合でも、Tomcatの内部ロギングとWebアプリケーションのロギングが独立したままであることが保証されます。

Tomcatが内部ロギングに代替ロギングフレームワークを使用するように設定するには、java.util.loggingを使用するアプリケーションのロギングをリダイレクトするための代替ロギングフレームワークが提供する指示に従ってください。これらの指示の一部へのリンクは、このページの最後に記載されています。代替ロギングフレームワークは、異なるクラスローダーに同じ名前の異なるロガーが存在する環境で動作できる必要があることに留意してください。

Apache Tomcat上で動作するWebアプリケーションは、次のことができます。

  • 任意のロギングフレームワークを使用できます。
  • システムロギングAPIであるjava.util.loggingを使用できます。
  • Javaサーブレット仕様で提供されるロギングAPI、jakarta.servlet.ServletContext.log(...)を使用できます。

異なるWebアプリケーションで使用されるロギングフレームワークは独立しています。詳細については、クラスローディングを参照してください。この規則の例外はjava.util.loggingです。もしロギングライブラリによって直接的または間接的に使用される場合、システムクラスローダーによってロードされるため、その要素はWebアプリケーション間で共有されます。

JavaロギングAPI — java.util.logging

Apache Tomcatは、java.util.logging APIのいくつかの主要な要素について独自のJULIと呼ばれる実装を持っています。その主要なコンポーネントは、Tomcat上で動作する異なるWebアプリケーション(およびそれらの異なるクラスローダー)を認識するカスタムのLogManager実装です。これは、アプリケーションごとのプライベートなロギング設定をサポートします。また、WebアプリケーションがメモリからアンロードされるとTomcatから通知され、そのクラスへの参照がクリアされ、メモリリークが防止されます。

このjava.util.logging実装は、Javaの起動時に特定のシステムプロパティを提供することで有効になります。Apache Tomcatの起動スクリプトはこれらを自動で行いますが、Tomcatを実行するために異なるツール(jsvcやIDE内からのTomcat実行など)を使用している場合は、ご自身でそれらの設定を行う必要があります。

java.util.loggingに関する詳細情報は、ご使用のJDKのドキュメントおよびjava.util.loggingパッケージのJavadocページで確認できます。

Tomcat JULIに関する詳細情報は、以下を参照してください。

サーブレットロギングAPI

ログメッセージを書き込むためのjakarta.servlet.ServletContext.log(...)への呼び出しは、Tomcatの内部ロギングによって処理されます。これらのメッセージは、次のカテゴリにログが記録されます。

org.apache.catalina.core.ContainerBase.[${engine}].[${host}].[${context}]

このロギングはTomcatのロギング設定に従って実行されます。Webアプリケーションでこれを上書きすることはできません。

サーブレットロギングAPIは、現在Javaによって提供されているjava.util.logging APIよりも古いものです。そのため、多くのオプションを提供しません。例えば、ログレベルを制御することはできません。ただし、Apache Tomcatの実装では、ServletContext.log(String)またはGenericServlet.log(String)への呼び出しはINFOレベルで、ServletContext.log(String, Throwable)またはGenericServlet.log(String, Throwable)への呼び出しはSEVEREレベルでログが記録されることに注意してください。

コンソール

Unix系OSでTomcatを実行している場合、コンソール出力は通常、catalina.outという名前のファイルにリダイレクトされます。この名前は環境変数を使用して設定可能です(起動スクリプトを参照)。System.err/outに書き込まれたものはすべてそのファイルにキャッチされます。これには以下が含まれる場合があります。

  • java.lang.ThreadGroup.uncaughtException(..)によって出力された未処理の例外
  • システムシグナルを介して要求した場合のスレッドダンプ

Windowsでサービスとして実行している場合、コンソール出力もキャッチされリダイレクトされますが、ファイル名は異なります。

Apache Tomcatのデフォルトのロギング設定は、同じメッセージをコンソールとログファイルの両方に書き込みます。これは開発目的でTomcatを使用する場合には非常に便利ですが、通常、本番環境では必要ありません。

System.outまたはSystem.errを依然として使用している古いアプリケーションは、ContextswallowOutput属性を設定することで対応できます。この属性がtrueに設定されている場合、リクエスト処理中のSystem.out/errへの呼び出しはインターセプトされ、その出力はjakarta.servlet.ServletContext.log(...)呼び出しを使用してロギングサブシステムに送られます。
: swallowOutput機能は実際には技巧であり、制限があります。これはSystem.out/errへの直接的な呼び出しでのみ機能し、リクエスト処理サイクル中のみです。アプリケーションによって作成される可能性のある他のスレッドでは機能しない場合があります。また、システムストリームに書き込むロギングフレームワーク自体をインターセプトするために使用することはできません。それらは早期に起動し、リダイレクトが行われる前にストリームへの直接参照を取得する可能性があるためです。

アクセスロギング

アクセスロギングは関連するが異なる機能であり、Valveとして実装されています。これは自己完結型のロジックを使用してログファイルを書き込みます。アクセスロギングの必須要件は、低オーバーヘッドで大量の連続データストリームを処理することであるため、デバッグメッセージにはApache Commons Loggingのみを使用します。この実装アプローチにより、追加のオーバーヘッドと潜在的に複雑な設定が回避されます。様々なレポート形式を含め、設定に関する詳細については、Valvesドキュメントを参照してください。

java.util.loggingの使用 (デフォルト)

JDKで提供されるjava.util.loggingのデフォルト実装は、実用的な用途には制限が多すぎます。主な制限は、設定がVMごとに設定されるため、Webアプリケーションごとのロギングができないことです。結果として、Tomcatはデフォルト設定で、デフォルトのLogManager実装を、これらの欠点を解消するコンテナフレンドリーなJULIと呼ばれる実装に置き換えます。

JULIは、プログラムによるアプローチまたはプロパティファイルを使用して、標準のJDK java.util.loggingと同じ設定メカニズムをサポートしています。主な違いは、クラスローダーごとのプロパティファイルを設定できること(これにより、再デプロイに適したWebアプリケーション設定が容易になります)と、プロパティファイルが、ハンドラーの定義とロガーへの割り当てにおいてより自由度を可能にする拡張された構文をサポートしていることです。

JULIはデフォルトで有効になっており、通常のグローバルなjava.util.logging設定に加えて、クラスローダーごとの設定をサポートしています。これにより、ロギングは以下のレイヤーで設定できます。

  • グローバル。通常、${catalina.base}/conf/logging.propertiesファイルで行われます。このファイルは、起動スクリプトによって設定されるjava.util.logging.config.fileシステムプロパティによって指定されます。読み取り可能でないか、設定されていない場合、デフォルトではJRE内の${java.home}/lib/logging.propertiesファイルが使用されます。
  • Webアプリケーション内。ファイルはWEB-INF/classes/logging.propertiesになります。

JREのデフォルトのlogging.propertiesは、ロギングをSystem.errにルーティングするConsoleHandlerを指定します。Apache Tomcatのデフォルトのconf/logging.propertiesも、ファイルに書き込む複数のAsyncFileHandlerを追加します。

ハンドラーのログレベルの閾値はデフォルトでINFOであり、SEVEREWARNINGINFOCONFIGFINEFINERFINESTまたはALLを使用して設定できます。また、特定のパッケージをターゲットにしてロギングを収集し、レベルを指定することもできます。

Tomcatの内部の一部でデバッグロギングを有効にするには、適切なロガーとハンドラーの両方をFINESTまたはALLレベルを使用するように設定する必要があります。例:

org.apache.catalina.session.level=ALL
java.util.logging.ConsoleHandler.level=ALL

デバッグロギングを有効にする際は、大量の情報を生成する可能性があるため、可能な限り最も狭いスコープで有効にすることをお勧めします。

JULIで使用される設定は、標準のjava.util.loggingでサポートされているものと同じですが、ロガーとハンドラーの設定における柔軟性を高めるためにいくつかの拡張機能を使用しています。主な違いは以下の通りです。

  • ハンドラー名にプレフィックスを追加できます。これにより、単一クラスの複数のハンドラーをインスタンス化できます。プレフィックスは、数字で始まり、'.'で終わる文字列です。例えば、22foobar.は有効なプレフィックスです。
  • ${systemPropertyName}を含むプロパティ値に対して、システムプロパティの置換が実行されます。
  • org.apache.juli.WebappPropertiesインターフェースを実装するクラスローダー(TomcatのWebアプリケーションクラスローダーなど)を使用している場合、${classloader.webappName}${classloader.hostName}${classloader.serviceName}についてもプロパティ置換が実行され、それぞれWebアプリケーション名、ホスト名、サービス名に置き換えられます。
  • デフォルトでは、ロガーは関連するハンドラーを持っている場合、親に委譲しません。これは、真偽値を受け入れるloggerName.useParentHandlersプロパティを使用してロガーごとに変更できます。
  • ルートロガーは、.handlersプロパティを使用してハンドラーのセットを定義できます。
  • デフォルトでは、ログファイルはファイルシステムに90日間保持されます。これは、handlerName.maxDaysプロパティを使用してハンドラーごとに変更できます。プロパティの指定された値が≤0の場合、ログファイルはファイルシステムに永久に保持され、それ以外の場合は、指定された最大日数保持されます。

Javaによって提供されるものと一緒に使用できる、いくつかの追加の実装クラスがあります。注目すべきは、org.apache.juli.FileHandlerorg.apache.juli.AsyncFileHandlerです。

org.apache.juli.FileHandlerはログのバッファリングをサポートしています。バッファリングはデフォルトでは有効になっていません。設定するには、ハンドラーのbufferSizeプロパティを使用します。値0はシステムデフォルトのバッファリングを使用します(通常8Kのバッファが使用されます)。値<0は各ログ書き込み時にライターのフラッシュを強制します。値>0は定義された値でBufferedOutputStreamを使用しますが、システムデフォルトのバッファリングも適用されることに注意してください。

org.apache.juli.AsyncFileHandlerは、ログメッセージをキューに入れ、非同期でログファイルに書き込むFileHandlerのサブクラスです。その追加の動作は、いくつかのシステムプロパティを設定することで構成できます。

$CATALINA_BASE/confに配置するlogging.propertiesファイルの例

handlers = 1catalina.org.apache.juli.AsyncFileHandler, \
           2localhost.org.apache.juli.AsyncFileHandler, \
           3manager.org.apache.juli.AsyncFileHandler, \
           java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

1catalina.org.apache.juli.AsyncFileHandler.level = ALL
1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.
1catalina.org.apache.juli.AsyncFileHandler.maxDays = 90
1catalina.org.apache.juli.AsyncFileHandler.encoding = UTF-8

2localhost.org.apache.juli.AsyncFileHandler.level = ALL
2localhost.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost.
2localhost.org.apache.juli.AsyncFileHandler.maxDays = 90
2localhost.org.apache.juli.AsyncFileHandler.encoding = UTF-8

3manager.org.apache.juli.AsyncFileHandler.level = ALL
3manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
3manager.org.apache.juli.AsyncFileHandler.prefix = manager.
3manager.org.apache.juli.AsyncFileHandler.bufferSize = 16384
3manager.org.apache.juli.AsyncFileHandler.maxDays = 90
3manager.org.apache.juli.AsyncFileHandler.encoding = UTF-8

java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.OneLineFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = \
   2localhost.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = \
   3manager.org.apache.juli.AsyncFileHandler

# For example, set the org.apache.catalina.util.LifecycleBase logger to log
# each component that extends LifecycleBase changing state:
#org.apache.catalina.util.LifecycleBase.level = FINE

Webアプリケーション内のWEB-INF/classesに配置するservlet-examples Webアプリケーション用のlogging.propertiesの例

handlers = org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

org.apache.juli.AsyncFileHandler.level = ALL
org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
org.apache.juli.AsyncFileHandler.prefix = ${classloader.webappName}.
org.apache.juli.AsyncFileHandler.encoding = UTF-8

java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.OneLineFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8

ドキュメント参照

追加情報については、以下のリソースを参照してください。

本番環境での使用に関する考慮事項

以下の点に注意してください。

  • 設定からConsoleHandlerを削除することを検討してください。デフォルトでは(.handlers設定により)、ロギングはAsyncFileHandlerConsoleHandlerの両方に出力されます。後者の出力は通常、catalina.outなどのファイルにキャプチャされます。したがって、同じメッセージが2つのコピーとして存在することになります。
  • 使用しないアプリケーションのAsyncFileHandlerを削除することを検討してください。例えば、host-manager用などです。
  • アクセスログの設定を検討してください。

Log4jの使用

Log4jプロジェクトは、Tomcatの内部ロギングにLog4Jを使用するための手順を提供しています。