ログを Datadog に送信するには、ファイルにログを記録し、そのファイルを Datadog Agent で tail します。
一般的な Java ログのスタックトレースは複数の行に分割されているため、元のログイベントに関連付けることが困難です。たとえば、以下のとおりです。
//4 events generated when only one is expected!
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 ログライブラリのセットアップ例を示しています。
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>
For console:
<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 レイアウトを使用してアペンダーを構成します。ファイルアペンダーとコンソールアペンダー向けのサンプル構成は下記を参照してください。Log4j プラグインの包括的な説明については、Log4j プラグインリファレンス を参照してください。Copy
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<File name= "FILE" fileName= "logs/app.log" >
<JsonTemplateLayout eventTemplateUri= "classpath:MyLayout.json" />
</File>
</Appenders>
<Loggers>
<Root level= "INFO" >
<AppenderRef ref= "FILE" />
</Root>
</Loggers>
</Configuration> Copy
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name= "console" target= "SYSTEM_OUT" >
<JsonTemplateLayout eventTemplateUri= "classpath:MyLayout.json" />
</Console>
</Appenders>
<Loggers>
<Root level= "INFO" >
<AppenderRef ref= "console" />
</Root>
</Loggers>
</Configuration> Java プロジェクトの src/main/resources ディレクトリに JSON レイアウトテンプレートファイル (MyLayout.json など) を追加します。たとえば、以下のとおりです。
{
"timestamp" :{
"$resolver" : "timestamp" ,
"pattern" :{
"format" : "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ,
"timeZone" : "UTC"
}
},
"status" :{
"$resolver" : "level" ,
"field" : "name"
},
"thread_name" :{
"$resolver" : "thread" ,
"field" : "name"
},
"logger_name" :{
"$resolver" : "logger" ,
"field" : "name"
},
"message" :{
"$resolver" : "message" ,
"stringified" : true
},
"exception_class" :{
"$resolver" : "exception" ,
"field" : "className"
},
"exception_message" :{
"$resolver" : "exception" ,
"field" : "message"
},
"stack_trace" :{
"$resolver" : "exception" ,
"field" : "stackTrace" ,
"stackTrace" :{
"stringified" : true
}
},
"host" : "${hostName}" ,
"service" : "${env:DD_SERVICE}" ,
"version" : "${env:DD_VERSION}" ,
"dd.trace_id" :{
"$resolver" : "mdc" ,
"key" : "dd.trace_id"
},
"dd.span_id" :{
"$resolver" : "mdc" ,
"key" : "dd.span_id"
}
}
pom.xml に JSON レイアウトの依存関係を追加します。たとえば、以下のとおりです。
<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>
pom.xml ファイルに Logstash エンコーダの依存関係を追加します。たとえば、以下のとおりです。
<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}
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:%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:%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:%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}) を削除します。
たとえば、Log4j 2 を使用していて、ログとトレースを相関させていない場合は、例のログレイアウトテンプレート MyLayout.json から次のブロックを削除します。
"dd.trace_id" : {
"$resolver" : "mdc" ,
"key" : "dd.trace_id"
} ,
"dd.span_id" : {
"$resolver" : "mdc" ,
"key" : "dd.span_id"
}
ログ収集が有効 になったら、ログファイルを追跡して 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 は自動的にログメッセージをパース し、ログ属性を抽出します。ログエクスプローラー を使用して、ログを表示し、トラブルシューティングを行うことができます。
ログを Agent に直接ストリーミングする アクセスできない、またはファイルにログを記録できないマシンでアプリケーションが実行されている例外的なケースでは、ログを 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 を構成します。 注:
Datadog では、ログを TCP 経由で Datadog インテークに直接送信することはサポートされていません。代わりに、ローカルの Datadog Agent に対する Logback を構成し、Agent から自動エンリッチメントによってログを HTTPS 経由で Datadog に転送します。
ローカルの Datadog Agent をインストール します (v6 以上/v7 以上)。
datadog.yaml でログ収集を有効にし、Agent がログを HTTPS 経由で転送することを確認します (Agent v6.19 以上/v7.19 以上では HTTPS 転送がデフォルトです)。
logs_enabled: true
logs_config:
# HTTPS is the default. Keep or set this to force HTTPS forwarding.
force_use_http: true
# (Optional) auto-detect multi-line patterns
auto_multi_line_detection: true
Agent でのログ収集を有効にします。
# /etc/datadog-agent/conf.d/logback.d/conf.yaml
logs :
- type : tcp
port : 10518 # Port the Agent will listen on
service : my-java-app # Your service name (unified service tagging)
source : java # Or a more specific source, e.g., "logback"
変更を適用するために Agent を再起動します。
Agent にログを送信するように Logback を構成します。logback.xml で logstash-logback-encoder TCP アペンダーを使用して、ログを Agent に転送します。
<configuration>
<appender name= "DD_TCP_JSON" class= "net.logstash.logback.appender.LogstashTcpSocketAppender" >
<destination> localhost:10518</destination>
<encoder class= "net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder" >
<providers>
<timestamp/>
<pattern>
<pattern>
{
"message": "%message",
"level": "%level",
"logger": "%logger",
"service": "${DD_SERVICE:-my-java-app}",
"env": "${DD_ENV:-prod}",
"version": "${DD_VERSION:-1.0.0}",
"dd.trace_id": "%X{dd.trace_id}",
"dd.span_id": "%X{dd.span_id}"
}
</pattern>
</pattern>
<arguments/>
<stackTrace/>
</providers>
</encoder>
</appender>
</configuration>
次に、それをルートロガーで参照します。
<root level= "INFO" >
<appender-ref ref= "DD_TCP_JSON" />
</root>
ログ転送を確認します。datadog-agent status を実行して TCP リスナーを確認し、サービスのタグが付いたエントリが[ログエクスプローラー]に表示されることを確認します。
補足説明 ログイベントをコンテキスト属性で補完することができます。
キー値パーサーの使用 キー値パーサー は、ログイベント内で認識された <KEY>=<VALUE> パターンを抽出します。
Java のログイベントを補完するには、コードでメッセージを書き直し、<KEY>=<VALUE> のシーケンスを挿入します。
たとえば、次のメッセージがあるとします。
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" : "Emitted 1001 messages during the last 93 seconds" ,
"scope" : "prod30"
}
注 : MDC は文字列タイプのみを許可するため、数値メトリクスには使用しないでください。
参考資料