ログを Datadog に送信するには、ファイルにログを記録し、そのファイルを Datadog Agent でテールします。
一般的な Java ログのスタックトレースは複数の行に分割されているため、元のログイベントに関連付けることが困難です。例:
//1 つのはずのイベントに、4 つのイベントが生成される
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
この問題を解決するには、ログを JSON 形式で生成するようにログライブラリを構成します。JSON にログすると、次のことができます。
- スタックトレースがログイベントに適切にラップされることを確実にします。
- すべてのログイベント属性 (重大度、ロガー名、スレッド名など) が適切に抽出されることを確実にします。
- マップされた診断コンテキスト (MDC) 属性にアクセスできます。この属性は、任意のログイベントにアタッチできます。
- カスタムパースルールが不要になります。
次の手順は、Log4j、Log4j 2、および Logback ログライブラリのセットアップ例を示しています。
ロガーの構成
JSON 形式
Log4j の場合、SLF4J モジュール log4j-over-slf4j を Logback と組み合わせて使用して JSON 形式でログします。log4j-over-slf4j
は、アプリケーションの Log4j を完全に置き換えるため、コードを変更する必要はありません。これを使用するには
pom.xml
ファイルで、log4j.jar
依存関係を log4j-over-slf4j.jar
依存関係に置き換え、Logback 依存関係を追加します。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
logback.xml
の JSON レイアウトを使用してアペンダーを構成します。
ファイル:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/app.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
コンソール:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.JsonEncoder"/>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Log4j 2 には JSON レイアウトが含まれています。
log4j2.xml
の JSON レイアウトを使用してアペンダーを構成します。
ファイルアペンダー:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<File name="FILE" fileName="logs/app.log" >
<JSONLayout compact="true" eventEol="true" properties="true" stacktraceAsString="true" />
</File>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="FILE"/>
</Root>
</Loggers>
</Configuration>
コンソールアペンダー:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" properties="true" stacktraceAsString="true" />
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
JSON レイアウトの依存関係を pom.xml
に追加します。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
Logback の JSON 形式のログには、logstash-logback-encoder を使用します。
logback.xml
の JSON レイアウトを使用してファイルアペンダーを構成します。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/app.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
Logstash エンコーダの依存関係を pom.xml
ファイルに追加します。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
Tinylog 公式ドキュメントに基づいて、JSON ライターの構成を作成します。
tinylog.properties
ファイルには以下のフォーマットを使用します。
writer = json
writer.file = log.json
writer.format = LDJSON
writer.level = info
writer.field.level = level
writer.field.source = {class}.{method}()
writer.field.message = {message}
writer.field.dd.trace_id = {context: dd.trace_id}
writer.field.dd.span_id = {context: dd.span_id}
writer.field.dd.service = {context: dd.service}
writer.field.dd.version = {context: dd.version}
writer.field.dd.env = {context: dd.env}
ログへのトレース ID の挿入
このアプリケーションで APM が有効になっている場合は、トレース ID インジェクションを有効にすることで、ログとトレースを相互に関連付けることができます。詳細については、Java ログとトレースの接続を参照してください。
未加工の形式
log4j.xml
でファイルアペンダーを構成します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender name="FILE" class="org.apache.log4j.FileAppender">
<param name="File" value="logs/app.log"/>
<param name="Append" value="true"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %X{dd.trace_id} %X{dd.span_id} - %m%n"/>
</layout>
</appender>
<root>
<priority value="INFO"/>
<appender-ref ref="FILE"/>
</root>
</log4j:configuration>
log4j2.xml
でファイルアペンダーを構成します。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<File name="FILE" fileName="logs/app.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %X{dd.trace_id} %X{dd.span_id} - %m%n"/>
</File>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="FILE"/>
</Root>
</Loggers>
</Configuration>
logback.xml
でファイルアペンダーを構成します。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${dd.test.logfile}</file>
<append>false</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %X{dd.trace_id} %X{dd.span_id} - %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
Tinylog 公式ドキュメントに基づいて、ファイルに出力するライターの構成を作成します。
tinylog.properties
ファイルには以下のフォーマットを使用します。
writer = file
writer.level = debug
writer.format = {level} - {message} - "dd.trace_id":{context: dd.trace_id} - "dd.span_id":{context: dd.span_id}
writer.file = log.txt
ログへのトレース ID の挿入
このアプリケーションで APM が有効になっている場合は、トレース ID インジェクションを有効にすることで、ログとトレースを相互に関連付けることができます。Java ログとトレースの接続を参照してください。
ログとトレースを相関させていない場合は、上記の構成例に含まれているログパターンから MDC プレースホルダー (%X{dd.trace_id} %X{dd.span_id}
) を削除できます。
Datadog Agent の構成
ログ収集が有効になったら、ログファイルを追跡して Datadog に送信するカスタムログ収集を設定します。
java.d/
フォルダーを conf.d/
Agent 構成ディレクトリに作成します。
java.d/
に以下の内容で conf.yaml
ファイルを作成します。
#Log section
logs:
- type: file
path: "<path_to_your_java_log>.log"
service: <service_name>
source: java
sourcecategory: sourcecode
# For multiline logs, if they start by the date with the format yyyy-mm-dd uncomment the following processing rule
#log_processing_rules:
# - type: multi_line
# name: new_log_start_with_date
# pattern: \d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])
Agent を再起動します。
Agent の status サブコマンドを実行し、Checks
セクションで java
を探し、ログが Datadog に正常に送信されることを確認します。
ログが JSON 形式の場合、Datadog は自動的にログメッセージをパースし、ログ属性を抽出します。ログエクスプローラーを使用して、ログを表示し、トラブルシューティングを行うことができます。
エージェントレスのログ収集
アクセスできない、またはファイルにログを記録できないマシンでアプリケーションが実行されている例外的なケースでは、ログを Datadog または Datadog Agent に直接ストリーミングすることができます。アプリケーションが接続の問題を処理する必要があるため、これは推奨される設定ではありません。
ログを Datadog に直接ストリーミングするには
- Logback ログライブラリをコードに追加するか、現在のロガーを Logback にブリッジします。
- Logback を構成して Datadog にログを送信します。
Java ロギングライブラリから Logback へのブリッジ
まだ Logback を使用していない場合、ほとんどの一般的なログライブラリは Logback にブリッジすることができます。
SLF4J モジュール log4j-over-slf4j を Logback とともに使用して、ログを別のサーバーに送信します。log4j-over-slf4j
は、アプリケーションの Log4j を完全に置き換えるため、コードを変更する必要はありません。これを使用するには
pom.xml
ファイルで、log4j.jar
依存関係を log4j-over-slf4j.jar
依存関係に置き換え、Logback 依存関係を追加します。<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
- Logback を構成します。
注: この変更の結果、Log4j コンフィギュレーションファイルは使用されなくなります。Log4j トランスレーター を使用して log4j.properties
ファイルを logback.xml
に移行してください。
Log4j 2 では、リモートホストへのログ記録が可能ですが、ログの前に API キーを付ける機能はありません。このため、SLF4J モジュール log4j-over-slf4j と Logback を使用してください。log4j-to-slf4j.jar
は、アプリケーションの Log4j 2 を完全に置き換えるため、コードを変更する必要はありません。これを使用するには
pom.xml
ファイルで、log4j.jar
依存関係を log4j-over-slf4j.jar
依存関係に置き換え、Logback 依存関係を追加します。<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
- Logback を構成します。
注:
Logback を構成する
logstash-logback-encoder ログライブラリを Logback と一緒に使用して、ログを Datadog に直接ストリーミングします。
logback.xml
ファイルに TCP アペンダーを構成します。この構成では、API キーは環境変数 DD_API_KEY
から取得されます。あるいは、コンフィギュレーションファイルに直接 API キーを挿入することもできます。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/app.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<appender name="JSON_TCP" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>intake.logs.datadoghq.com:10516</destination>
<keepAliveDuration>20 seconds</keepAliveDuration>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<prefix class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${DD_API_KEY} %mdc{keyThatDoesNotExist}</pattern>
</layout>
</prefix>
</encoder>
<ssl />
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
<appender-ref ref="JSON_TCP" />
</root>
</configuration>
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/app.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<appender name="JSON_TCP" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>tcp-intake.logs.datadoghq.eu:443</destination>
<keepAliveDuration>20 seconds</keepAliveDuration>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<prefix class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${DD_API_KEY} %mdc{keyThatDoesNotExist}</pattern>
</layout>
</prefix>
</encoder>
<ssl />
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
<appender-ref ref="JSON_TCP" />
</root>
</configuration>
注: XML コンフィギュレーションで空白が削除されるため、%mdc{keyThatDoesNotExist}
が追加されます。プレフィックスパラメータの詳細については、Logback ドキュメントを参照してください。
Logstash エンコーダの依存関係を pom.xml
ファイルに追加します。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
補足説明
ログイベントをコンテキスト属性で補完することができます。
キー値パーサーの使用
キー値パーサーは、ログイベント内で認識された <KEY>=<VALUE>
パターンを抽出します。
Java のログイベントを補完するには、コードでメッセージを書き直し、<キー>=<値>
のシーケンスを挿入します。
たとえば、次のメッセージがあるとします。
logger.info("Emitted 1001 messages during the last 93 seconds for customer scope prod30");
これを次のように変更します。
logger.info("Emitted quantity=1001 messages during the last durationInMs=93180 ms for customer scope=prod30");
キー値パーサーを有効にすると、各ペアが JSON から抽出されます。
{
"message": "Emitted quantity=1001 messages during the last durationInMs=93180 ms for customer scope=prod30",
"scope": "prod30",
"durationInMs": 93180,
"quantity": 1001
}
これで、scope をフィールド、durationInMs と quantity をログメジャーとして利用できます。
MDC
ログを補完するもう 1 つの方法として、Java の マップされた診断コンテキスト (MDC) の利用があります。
SLF4J を使用する場合は、次の Java コードを使用してください。
...
MDC.put("scope", "prod30");
logger.info("Emitted 1001 messages during the last 93 seconds");
...
この JSON を生成するには
{
"message": "過去 93 秒間に 1001 メッセージを送信",
"scope": "prod30"
}
注: MDC は文字列タイプのみを許可するため、数値メトリクスには使用しないでください。
その他の参考資料