ログの一元管理(Graylog、Logstash、Fluentd)
このガイドでは、Graylog、Logstash(Elastic StackやELK - Elasticsearch、Logstash、Kibanaの内部)、Fluentd(EFK - Elasticsearch、Fluentd、Kibanaの内部)のような集中型ログ管理システムにログを送信する方法を説明します。
ログを一元化する方法はたくさんあります(Kubernetesを使用している場合、最も簡単な方法はコンソールにログインして、クラスター管理者にクラスター内に一元化されたログマネージャーを統合するように依頼することです)。このガイドでは、TCPまたはUDPを使用してGraylog Extended Log Format (GELF)でログを送信できる quarkus-logging-gelf
エクステンションを使用して外部ツールに送信する方法を公開します。
quarkus-logging-gelf
エクステンションは、Quarkusが使用する基礎となるロギングバックエンド(jboss-logmanager)にGELFログハンドラーを追加します。デフォルトでは無効になっており、有効にした場合も別のハンドラーも使用している場合(デフォルトではコンソールハンドラーが有効になっています)、ログは両方のハンドラーに送信されます。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
Docker と Docker Compose、または Podman 、および Docker Compose
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
アプリケーション例
以下の例はすべて、以下の手順で作成できる同じアプリケーションの例を基にしています。
quarkus-logging-gelf
のエクステンションを持つアプリケーションを作成します。以下のコマンドで作成することができます:
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=gelf-logging"
すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに logging-gelf
エクステンションを追加することができます。
quarkus extension add logging-gelf
./mvnw quarkus:add-extension -Dextensions='logging-gelf'
./gradlew addExtension --extensions='logging-gelf'
これにより、ビルドファイルに以下の依存関係が追加されます:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-gelf</artifactId>
</dependency>
implementation("io.quarkus:quarkus-logging-gelf")
デモのために、文をログに記録するだけのエンドポイントを作成します。これをアプリケーション内で行う必要はありません。
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.jboss.logging.Logger;
@Path("/gelf-logging")
@ApplicationScoped
public class GelfLoggingResource {
private static final Logger LOG = Logger.getLogger(GelfLoggingResource.class);
@GET
public void log() {
LOG.info("Some useful log message");
}
}
GELF ログハンドラーを構成して、ポート 12201 の外部 UDP エンドポイントにログを送信します:
quarkus.log.handler.gelf.enabled=true
quarkus.log.handler.gelf.host=localhost
quarkus.log.handler.gelf.port=12201
ログをGraylogに送信
Graylogにログを送信するには、まずGraylogスタックを構成するコンポーネントを起動する必要があります:
-
MongoDB
-
Elasticsearch
-
Graylog
これは、以下の docker-compose.yml
ファイルから、 docker-compose up -d
で起動できます:
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:8.15.0
ports:
- "9200:9200"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- graylog
mongo:
image: mongo:4.0
networks:
- graylog
graylog:
image: graylog/graylog:4.3.0
ports:
- "9000:9000"
- "12201:12201/udp"
- "1514:1514"
environment:
GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
# CHANGE ME (must be at least 16 characters)!
GRAYLOG_PASSWORD_SECRET: "forpasswordencryption"
# Password: admin
GRAYLOG_ROOT_PASSWORD_SHA2: "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"
networks:
- graylog
depends_on:
- elasticsearch
- mongo
networks:
graylog:
driver: bridge
次に、GraylogでUDP入力を作成する必要があります。これは、http://localhost:9000 の GraylogのWebコンソール(System → Input → Select GELF UDP)から、 またはAPI経由で行うことができます。
この curl の例では、Graylog (admin/admin) からのデフォルトのログインを使用し、GELF UDP タイプの新しい入力を作成します。
curl -H "Content-Type: application/json" -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "X-Requested-By: curl" -X POST -v -d \
'{"title":"udp input","configuration":{"recv_buffer_size":262144,"bind_address":"0.0.0.0","port":12201,"decompress_size_limit":8388608},"type":"org.graylog2.inputs.gelf.udp.GELFUDPInput","global":true}' \
http://localhost:9000/api/system/inputs
アプリケーションを起動すると、Graylog内にログが届くのがわかるはずです。
Logstash / Elastic Stack (ELK)にログを送信
LogstashにはGELF形式を理解できるInputプラグインがデフォルトで入ってるので、まずこのプラグインを有効にするパイプラインを作成します。
$HOME/pipelines/gelf.conf
に以下のファイルを作成します。
input {
gelf {
port => 12201
}
}
output {
stdout {}
elasticsearch {
hosts => ["http://elasticsearch:9200"]
}
}
最後に、Elastic Stackを構成するコンポーネントを起動します:
-
Elasticsearch
-
Logstash
-
Kibana
これは、以下の docker-compose.yml
ファイルから、 docker-compose up -d
で起動できます:
# Launch Elasticsearch
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:8.15.0
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- elk
logstash:
image: docker.io/elastic/logstash:8.15.0
volumes:
- source: $HOME/pipelines
target: /usr/share/logstash/pipeline
type: bind
ports:
- "12201:12201/udp"
- "5000:5000"
- "9600:9600"
networks:
- elk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:8.15.0
ports:
- "5601:5601"
networks:
- elk
depends_on:
- elasticsearch
networks:
elk:
driver: bridge
アプリケーションを起動すると、Elastic Stack内にログが届いているのが確認できるはずです。 http://localhost:5601/ で利用できるKibanaを使って、ログにアクセスすることができます。
GELF alternative: Send logs to Logstash in the ECS (Elastic Common Schema) format
You can also send your logs to Logstash using a TCP input in the ECS format.
To achieve this we will use the quarkus-logging-json
extension to format the logs in JSON format and the socket handler to send them to Logstash.
For this you can use the same docker-compose.yml
file as above but with a different Logstash pipeline configuration.
input {
tcp {
port => 4560
coded => json
}
}
filter {
if ![span][id] and [mdc][spanId] {
mutate { rename => { "[mdc][spanId]" => "[span][id]" } }
}
if ![trace][id] and [mdc][traceId] {
mutate { rename => {"[mdc][traceId]" => "[trace][id]"} }
}
}
output {
stdout {}
elasticsearch {
hosts => ["http://elasticsearch:9200"]
}
}
Then configure your application to log in JSON format instead of GELF
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-json</artifactId>
</dependency>
implementation("io.quarkus:quarkus-logging-json")
and specify the host and port of your Logstash endpoint. To be ECS compliant, specify the log format.
# to keep the logs in the usual format in the console
quarkus.log.console.json=false
quarkus.log.socket.enable=true
quarkus.log.socket.json=true
quarkus.log.socket.endpoint=localhost:4560
# to have the exception serialized into a single text element
quarkus.log.socket.json.exception-output-type=formatted
# specify the format of the produced JSON log
quarkus.log.socket.json.log-format=ECS
Fluentd(EFK)へのログ送信
まず、必要なプラグインである elasticsearch と input-elf を使って Fluentd イメージを作成します。 以下の Dockerfile を使用することができます。このファイルは fluentd
ディレクトリー内に作成する必要があります。
FROM fluent/fluentd:v1.3-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--version", "3.7.0"]
RUN ["gem", "install", "fluent-plugin-input-gelf", "--version", "0.3.1"]
イメージをビルドするか、docker-composeにビルドを任せることができます。
次に、$HOME/fluentd/fluent.conf
の中にfluentd 設定ファイルを作成する必要があります
<source>
type gelf
tag example.gelf
bind 0.0.0.0
port 12201
</source>
<match example.gelf>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
</match>
最後に、EFKスタックを構成するコンポーネントを起動します:
-
Elasticsearch
-
Fluentd
-
Kibana
これは、以下の docker-compose.yml
ファイルから、 docker-compose up -d
で起動できます:
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:8.15.0
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- efk
fluentd:
build: fluentd
ports:
- "12201:12201/udp"
volumes:
- source: $HOME/fluentd
target: /fluentd/etc
type: bind
networks:
- efk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:8.15.0
ports:
- "5601:5601"
networks:
- efk
depends_on:
- elasticsearch
networks:
efk:
driver: bridge
アプリケーションを起動すると、EFK内にログが到着しているのが確認できるはずです: それらにアクセスするには、 http://localhost:5601/ で利用可能な Kibana を使用することができます。
GELFの代替:Syslogの使用
Syslog入力を使ってFluentdにログを送ることもできます。GELF入力とは対照的に、Syslog入力は1つのイベントで複数行のログをレンダリングすることができないため、Quarkusで実装されているGELF入力を使用することをお勧めします。
まず、elasticsearchプラグインでFluentdイメージを作成する必要があります。 以下の Dockerfile を使用することができます。このファイルは fluentd
ディレクトリー内に作成する必要があります。
FROM fluent/fluentd:v1.3-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--version", "3.7.0"]
次に、fluentd設定ファイルを $HOME/fluentd/fluent.conf
内に作成する必要があります
<source>
@type syslog
port 5140
bind 0.0.0.0
message_format rfc5424
tag system
</source>
<match **>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
</match>
次に、EFKスタックを構成するコンポーネントを起動します:
-
Elasticsearch
-
Fluentd
-
Kibana
これは、以下の docker-compose.yml
ファイルから、 docker-compose up -d
で起動できます:
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:8.15.0
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- efk
fluentd:
build: fluentd
ports:
- "5140:5140/udp"
volumes:
- source: $HOME/fluentd
target: /fluentd/etc
type: bind
networks:
- efk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:8.15.0
ports:
- "5601:5601"
networks:
- efk
depends_on:
- elasticsearch
networks:
efk:
driver: bridge
最後に、Syslogを使用してEFKにログを送信するようにアプリケーションを設定します:
quarkus.log.syslog.enable=true
quarkus.log.syslog.endpoint=localhost:5140
quarkus.log.syslog.protocol=udp
quarkus.log.syslog.app-name=quarkus
quarkus.log.syslog.hostname=quarkus-test
アプリケーションを起動すると、EFK内にログが到着しているのが確認できるはずです: それらにアクセスするには、 http://localhost:5601/ で利用可能な Kibana を使用することができます。
Elasticsearch インデックスの検討
デフォルトでは、Elasticsearch は未知のフィールド(インデックス設定で無効になっていない場合)を、そのタイプを検出して自動的にマッピングすることに注意してください。これは、ログパラメータ(デフォルトで含まれている)を使用する場合や、MDC インクルード(デフォルトでは無効)を有効にする場合、最初のログがインデックス内のメッセージパラメータ(または MDC パラメータ)フィールドのタイプを定義するため、厄介なことになる可能性があります。
次のようなケースを想像してみてください:
LOG.info("some {} message {} with {} param", 1, 2, 3);
LOG.info("other {} message {} with {} param", true, true, true);
ログメッセージのパラメータを有効にすると、Elasticsearch に送信される最初のログメッセージは int
型の MessageParam0
パラメータが設定され、これにより integer
型のフィールドでインデックスが構成されます。2 番目のメッセージが Elasticsearch に届くと、ブーリアン値 true
を持つ MessageParam0
パラメータが設定され、これによりインデックス作成エラーが発生します。
この制限を回避するには、 quarkus.log.handler.gelf.include-log-message-parameters=false
を設定して logging-gelf
を介したログメッセージパラメータの送信を無効にするか、Elasticsearch インデックスにテキストまたはキーワードとしてこれらのフィールドを保存するように設定して、Elasticsearch は int/boolean から String への変換を自動的に行います。
Graylogについては以下のドキュメントを参照してください(ただし、他のセントラルロギングスタックにも同じ問題があります)。 カスタム インデックス マッピング 。
設定リファレンス
設定は通常の application.properties
ファイルから行います。
ビルド時に固定される構成プロパティ - 他のすべての構成プロパティは実行時にオーバーライド可能
Configuration property |
型 |
デフォルト |
---|---|---|
Determine whether to enable the GELF logging handler Environment variable: Show more |
boolean |
|
Hostname/IP-Address of the Logstash/Graylog Host By default it uses UDP, prepend tcp: to the hostname to switch to TCP, example: "tcp:localhost" Environment variable: Show more |
string |
|
The port Environment variable: Show more |
int |
|
GELF version: 1.0 or 1.1 Environment variable: Show more |
string |
|
Whether to post Stack-Trace to StackTrace field. Environment variable: Show more |
boolean |
|
Only used when Environment variable: Show more |
int |
|
Whether to perform Stack-Trace filtering Environment variable: Show more |
boolean |
|
Java date pattern, see Environment variable: Show more |
string |
|
The logging-gelf log level. Environment variable: Show more |
|
|
Name of the facility. Environment variable: Show more |
string |
|
型 |
デフォルト |
|
Additional field value. Environment variable: Show more |
string |
required |
Additional field type specification. Supported types: String, long, Long, double, Double and discover. Discover is the default if not specified, it discovers field type based on parseability. Environment variable: Show more |
string |
|
Whether to include all fields from the MDC. Environment variable: Show more |
boolean |
|
Send additional fields whose values are obtained from MDC. Name of the Fields are comma-separated. Example: mdcFields=Application,Version,SomeOtherFieldName Environment variable: Show more |
string |
|
Dynamic MDC Fields allows you to extract MDC values based on one or more regular expressions. Multiple regexes are comma-separated. The name of the MDC entry is used as GELF field name. Environment variable: Show more |
string |
|
Pattern-based type specification for additional and MDC fields. Key-value pairs are comma-separated. Example: my_field.*=String,business\..*\.field=double Environment variable: Show more |
string |
|
Maximum message size (in bytes). If the message size is exceeded, the appender will submit the message in multiple chunks. Environment variable: Show more |
int |
|
Include message parameters from the log event Environment variable: Show more |
boolean |
|
Include source code location Environment variable: Show more |
boolean |
|
Origin hostname Environment variable: Show more |
string |
|
Bypass hostname resolution. If you didn’t set the Environment variable: Show more |
boolean |
|
このエクステンションは、システムプロパティーを介してより多くの設定オプションを提供する logstash-gelf
ライブラリを使用しています。ドキュメントにはこちら: https://logging.paluch.biz/ からアクセスできます。